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.
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 1 | /** |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 2 | * 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. | |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 6 | */ |
| 7 | ||
| 8 | export 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-agent | 23 | /** g1t has been given an issue here. */ |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 24 | agentAssigned: boolean; |
| 25 | }; | |
| 26 | ||
| 27 | export type ChecklistItem = { | |
| A project is an app or a library: libraries show their package and how to ship a release, not production | 28 | key: "code" | "deploy" | "domain" | "preview" | "checks" | "release" | "instructions" | "agent"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 29 | title: string; |
| 30 | detail: string; | |
| 31 | done: boolean; | |
| 32 | to: string; | |
| 33 | action: string; | |
| 34 | }; | |
| 35 | ||
| 36 | export 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-agent | 83 | title: "Assign a first issue to g1t", |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 84 | 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 production | 92 | export 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 | */ | |
| 106 | export 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 | ||
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 132 | /** `3/6`, for the card's heading. */ |
| 133 | export function progress(items: ChecklistItem[]): { done: number; total: number; complete: boolean } { | |
| 134 | const done = items.filter((item) => item.done).length; | |
| 135 | return { done, total: items.length, complete: done === items.length }; | |
| 136 | } | |
| 137 | ||
| 138 | /** A repository's root lists instructions for agents. */ | |
| 139 | export function hasInstructions(names: string[]): boolean { | |
| 140 | return names.some((name) => /^(agents|claude)\.md$/i.test(name)); | |
| 141 | } | |
| 142 | ||
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 143 | /** Whether g1t has worked here, from its runs, pull requests or issues. */ |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 144 | export function agentWasAssigned(input: { |
| 145 | runAgents: string[]; | |
| 146 | pullAgents: string[]; | |
| 147 | issues: { assignees: string[]; agent: string | null }[]; | |
| 148 | }): boolean { | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 149 | const isAgent = (name: string | null) => name?.toLowerCase() === "g1t"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 150 | return ( |
| 151 | input.runAgents.some(isAgent) || | |
| 152 | input.pullAgents.some(isAgent) || | |
| 153 | input.issues.some((issue) => isAgent(issue.agent) || issue.assignees.some(isAgent)) | |
| 154 | ); | |
| 155 | } | |
| 156 | ||
| 157 | // --- Dismissing it, per project, in this browser ----------------------------- | |
| 158 | ||
| 159 | type Store = Pick<Storage, "getItem" | "setItem">; | |
| 160 | ||
| 161 | export function dismissKey(base: string): string { | |
| 162 | return `g1t:checklist-dismissed:${base.toLowerCase()}`; | |
| 163 | } | |
| 164 | ||
| 165 | export function isDismissed(storage: () => Store | null | undefined, base: string): boolean { | |
| 166 | try { | |
| 167 | return storage()?.getItem(dismissKey(base)) === "1"; | |
| 168 | } catch { | |
| 169 | return false; | |
| 170 | } | |
| 171 | } | |
| 172 | ||
| 173 | export function dismiss(storage: () => Store | null | undefined, base: string): boolean { | |
| 174 | try { | |
| 175 | const store = storage(); | |
| 176 | if (!store) return false; | |
| 177 | store.setItem(dismissKey(base), "1"); | |
| 178 | return true; | |
| 179 | } catch { | |
| 180 | return false; | |
| 181 | } | |
| 182 | } |