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
64 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.

The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor1/**
2 * Putting an agent on something in one step, from a form: the issue new
3 * page with "Assign g1t now", and Mission control's composer. Pure,
4 * so it is tested on its own; the routes call `env.RUNNER.delegate`.
5 */
6import type { AgentStart, DelegateInput, RepoPath } from "@g1t/contracts";
7
8/** What a form asks for, read the way both forms name their fields. */
9export function delegateForm(form: FormData): DelegateInput {
10 const text = (name: string) => String(form.get(name) ?? "");
11 return {
12 title: text("title").trim(),
13 body: text("body").trim(),
14 labels: [...form.getAll("label").map(String), ...text("labels").split(",")].map((label) => label.trim()).filter(Boolean),
15 };
16}
17
18/** The project a composer chose, written `owner/name`, if it is one of `repos`. */
19export function chosenRepo(value: FormDataEntryValue | null, repos: RepoPath[]): RepoPath | null {
20 const [namespace, name, extra] = String(value ?? "").split("/");
21 if (!namespace || !name || extra != null) return null;
22 return repos.find((repo) => repo.namespace.toLowerCase() === namespace.toLowerCase() && repo.name.toLowerCase() === name.toLowerCase()) ?? null;
23}
24
25/** Where to land once the issue is open: on it, while the agent works or waits. */
26export function issuePath(repo: RepoPath, number: number): string {
27 return `/${repo.namespace}/${repo.name}/issues/${number}`;
28}
29
30/** What a form says when the issue was opened but the agent did not start. */
31export type NotStarted = {
32 /** The issue that was opened. */
33 to: string;
34 number: number;
35 message: string;
36 fix: { label: string; to: string } | null;
37};
38
39const FIX_LABEL: Record<string, string> = {
40 not_paid: "Start the plan or the trial",
41 trial_used: "Start the plan",
42 limit: "Raise the limit",
43 issue_cap: "Raise the cap per issue",
44 no_model: "Connect a model",
45};
46
47/**
48 * The agent's start, for a form: null when it started or is queued, so the
49 * form goes on to the issue; otherwise what to say and where to fix it.
50 */
51export function notStarted(agent: AgentStart, repo: RepoPath, number: number): NotStarted | null {
52 if (agent.status !== "not_started") return null;
53 // The fix is on g1t.sh itself: followed within the site.
54 const fixTo = agent.fixUrl?.replace(/^https:\/\/g1t\.sh(?=\/)/, "") ?? null;
55 return {
56 to: issuePath(repo, number),
57 number,
58 // The fix is offered as a link of its own, so the address the message ends with is left off.
59 message: fixTo
60 ? (agent.message ?? "g1t did not start.").replace(/:\s*(?:https:\/\/g1t\.sh)?\/[\w./#-]+\s*$/, ".")
61 : (agent.message ?? "g1t did not start."),
62 fix: fixTo ? { label: FIX_LABEL[agent.code ?? ""] ?? "Fix it", to: fixTo } : null,
63 };
64}

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