Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
282 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1/**
A project is an app or a library: libraries show their package and how to ship a release, not production2 * The steps that take a project to production, or for a library to its
3 * first release, each worked out from what the project has done, with
4 * where to do the ones it has not. Shown on the project's overview to its
5 * members until they are all done or it is dismissed.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily6 */
7
8export type ChecklistFacts = {
9 /** The project's address, `/<workspace>/<project>`. */
10 base: string;
11 /** Its default branch has a commit, or its source is a mirror. */
12 hasCode: boolean;
13 /** Deployments are switched on for it. */
14 deploysEnabled: boolean;
15 /** A production build has gone live, now or before. */
16 productionDeployed: boolean;
17 /** Custom domains it has; null when they could not be read. */
18 domains: number | null;
19 /** A preview has been built for a branch or pull request. */
20 previewOpened: boolean;
21 /** An AGENTS.md or CLAUDE.md at the root of its default branch; null when unknown. */
22 instructions: boolean | null;
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent23 /** g1t has been given an issue here. */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily24 agentAssigned: boolean;
25};
26
27export type ChecklistItem = {
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb9728 key: "code" | "deploy" | "domain" | "preview" | "checks" | "release" | "production" | "where" | "links" | "instructions" | "agent";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily29 title: string;
30 detail: string;
31 done: boolean;
32 to: string;
33 action: string;
34};
35
36export function productionChecklist(facts: ChecklistFacts): ChecklistItem[] {
37 const { base } = facts;
38 return [
39 {
40 key: "code",
41 title: "Connect a source or push code",
42 detail: "Push an existing project, or have your coding agent start one.",
43 done: facts.hasCode,
44 to: `${base}/code`,
45 action: "Push code",
46 },
47 {
48 key: "deploy",
49 title: "Deploy to production",
50 detail: facts.deploysEnabled
51 ? "Production builds from the default branch on every push."
52 : "Deployments are off until you turn them on. Then production builds from the default branch on every push.",
53 done: facts.productionDeployed,
54 to: facts.deploysEnabled ? `${base}/deployments` : `${base}/settings/deployments`,
55 action: facts.deploysEnabled ? "Deployments" : "Turn on",
56 },
57 {
58 key: "domain",
59 title: "Add a custom domain",
60 detail: "Serve production from a domain of your own.",
61 done: (facts.domains ?? 0) > 0,
62 to: `${base}/settings/domains`,
63 action: "Add",
64 },
65 {
66 key: "preview",
67 title: "Open a preview",
68 detail: "Every pull request gets a live preview of its branch.",
69 done: facts.previewOpened,
70 to: `${base}/pulls/new`,
71 action: "New pull request",
72 },
73 {
74 key: "instructions",
75 title: "Set up repository instructions",
76 detail: "Commit an AGENTS.md with how to build and test; every agent run reads it.",
77 done: facts.instructions === true,
78 to: `${base}/agents#instructions`,
79 action: "How",
80 },
81 {
82 key: "agent",
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent83 title: "Assign a first issue to g1t",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily84 detail: "It opens a pull request, makes the change and sees it through checks and review.",
85 done: facts.agentAssigned,
86 to: `${base}/issues/new`,
87 action: "New issue",
88 },
89 ];
90}
91
A project is an app or a library: libraries show their package and how to ship a release, not production92export type ReleaseFacts = Pick<ChecklistFacts, "base" | "hasCode" | "instructions" | "agentAssigned"> & {
93 /** It has a workflow, whose runs are its pull requests' checks; null when unknown. */
94 hasWorkflow: boolean | null;
95 /** A package its repository publishes has a version. */
96 released: boolean;
97 /** Where publishing a first version is explained: its package's page or its registry's guide. */
98 releaseTo: string;
99};
100
101/**
102 * The steps for a library or a tool, which ships as releases rather than
103 * deploying: the same first and last steps as production, with checks and
104 * a first version in between.
105 */
106export function releaseChecklist(facts: ReleaseFacts): ChecklistItem[] {
107 const shared = productionChecklist({ ...facts, deploysEnabled: false, productionDeployed: false, domains: null, previewOpened: false });
108 const step = (key: ChecklistItem["key"]) => shared.find((item) => item.key === key)!;
109 return [
110 step("code"),
111 {
112 key: "checks",
113 title: "Add checks on pull requests",
114 detail: "A workflow that builds and tests it. Its runs are every pull request's checks.",
115 done: facts.hasWorkflow === true,
116 to: `${facts.base}/actions`,
117 action: "Add CI",
118 },
119 {
120 key: "release",
121 title: "Tag a release or publish a package",
122 detail: "Publish a first version to the workspace's registry for others to install.",
123 done: facts.released,
124 to: facts.releaseTo,
125 action: "How",
126 },
127 step("instructions"),
128 step("agent"),
129 ];
130}
131
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97132export type StartFacts = Pick<ChecklistFacts, "base" | "hasCode" | "instructions" | "agentAssigned"> & {
133 /** It has a workflow, whose runs are its pull requests' checks; null when unknown. */
134 hasWorkflow: boolean | null;
135 /** Production's address, for an app deployed elsewhere. */
136 productionUrl: string | null;
137 /** Its homepage, docs or any other link is set. */
138 hasLinks: boolean;
139 /** Its docs address is set, or its homepage. */
140 hasDocsLink: boolean;
141};
142
143/** Where a project's own settings are, for the steps that are done there. */
144const settingsAt = (base: string, anchor: string) => `${base}/settings#${anchor}`;
145
146/**
147 * The steps for a project g1t does not deploy, by what it is. Every step
148 * applies to it and each is done from its own fact: nothing about turning
149 * on Deployments, domains or previews.
150 *
151 * - An app deployed elsewhere: its production address, then checks.
152 * - An app nobody has said where it runs: saying so, then checks.
153 * - Docs: where they are read.
154 * - Anything else: its links.
155 */
156export function startChecklist(kind: "elsewhere" | "unknown" | "docs" | "other", facts: StartFacts): ChecklistItem[] {
157 const shared = productionChecklist({ ...facts, deploysEnabled: false, productionDeployed: false, domains: null, previewOpened: false });
158 const step = (key: ChecklistItem["key"]) => shared.find((item) => item.key === key)!;
159 const checks: ChecklistItem = {
160 key: "checks",
161 title: "Add checks on pull requests",
162 detail: "A workflow that builds and tests it. Its runs are every pull request's checks.",
163 done: facts.hasWorkflow === true,
164 to: `${facts.base}/actions`,
165 action: "Add CI",
166 };
167 const middle: ChecklistItem[] =
168 kind === "elsewhere"
169 ? [
170 {
171 key: "production",
172 title: "Add production's address",
173 detail: "Where your own pipeline deploys it, so its overview links to production.",
174 done: facts.productionUrl != null,
175 to: settingsAt(facts.base, "kind"),
176 action: "Add",
177 },
178 checks,
179 ]
180 : kind === "unknown"
181 ? [
182 {
183 key: "where",
184 title: "Say where it runs",
185 detail: "Deployed on g1t, deployed elsewhere, or not deployed at all, such as a library.",
186 done: false,
187 to: settingsAt(facts.base, "kind"),
188 action: "Choose",
189 },
190 checks,
191 ]
192 : kind === "docs"
193 ? [
194 {
195 key: "links",
196 title: "Add where its docs are read",
197 detail: "A docs or homepage address, shown on its overview and wherever the project is listed.",
198 done: facts.hasDocsLink,
199 to: settingsAt(facts.base, "links"),
200 action: "Add",
201 },
202 ]
203 : [
204 {
205 key: "links",
206 title: "Add its links",
207 detail: "A homepage, docs, or any other address people go to for it.",
208 done: facts.hasLinks,
209 to: settingsAt(facts.base, "links"),
210 action: "Add",
211 },
212 ];
213 return [step("code"), ...middle, step("instructions"), step("agent")];
214}
215
216export type ChecklistPlan = "production" | "release" | "elsewhere" | "unknown" | "docs" | "other";
217
218/**
219 * Which steps a project gets, and their heading, from what it is and where
220 * it runs. Only what g1t deploys gets production's steps.
221 */
222export function checklistPlan(project: {
223 kind: "app" | "library" | "tool" | "docs" | "other";
224 runs: "g1t" | "elsewhere" | null;
225}): { plan: ChecklistPlan; title: string } {
226 if (project.runs === "g1t") return { plan: "production", title: "Get to production" };
227 if (project.kind === "library" || project.kind === "tool") return { plan: "release", title: "Ship a release" };
228 if (project.kind === "app") return { plan: project.runs === "elsewhere" ? "elsewhere" : "unknown", title: "Get started" };
229 return { plan: project.kind, title: "Get started" };
230}
231
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily232/** `3/6`, for the card's heading. */
233export function progress(items: ChecklistItem[]): { done: number; total: number; complete: boolean } {
234 const done = items.filter((item) => item.done).length;
235 return { done, total: items.length, complete: done === items.length };
236}
237
238/** A repository's root lists instructions for agents. */
239export function hasInstructions(names: string[]): boolean {
240 return names.some((name) => /^(agents|claude)\.md$/i.test(name));
241}
242
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent243/** Whether g1t has worked here, from its runs, pull requests or issues. */
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily244export function agentWasAssigned(input: {
245 runAgents: string[];
246 pullAgents: string[];
247 issues: { assignees: string[]; agent: string | null }[];
248}): boolean {
g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent249 const isAgent = (name: string | null) => name?.toLowerCase() === "g1t";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily250 return (
251 input.runAgents.some(isAgent) ||
252 input.pullAgents.some(isAgent) ||
253 input.issues.some((issue) => isAgent(issue.agent) || issue.assignees.some(isAgent))
254 );
255}
256
257// --- Dismissing it, per project, in this browser -----------------------------
258
259type Store = Pick<Storage, "getItem" | "setItem">;
260
261export function dismissKey(base: string): string {
262 return `g1t:checklist-dismissed:${base.toLowerCase()}`;
263}
264
265export function isDismissed(storage: () => Store | null | undefined, base: string): boolean {
266 try {
267 return storage()?.getItem(dismissKey(base)) === "1";
268 } catch {
269 return false;
270 }
271}
272
273export function dismiss(storage: () => Store | null | undefined, base: string): boolean {
274 try {
275 const store = storage();
276 if (!store) return false;
277 store.setItem(dismissKey(base), "1");
278 return true;
279 } catch {
280 return false;
281 }
282}

This file's history is long; its oldest lines are credited to the oldest commit read.