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
1,148 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.

Merge checks: statuses and check runs on every commit1import type { ChecksApi } from "./checks";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar2import type { CodeownersReport, PullCodeOwners } from "./codeowners";
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides3import type { User, Viewer } from "./identity";
4import type { PullBranchUpdate, RepoPath } from "./repos";
5import type { Result } from "./result";
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge6import type { MergeRules, RulesApi } from "./rules";
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides7
8/**
9 * The filter on lists of issues and pull requests. An open pull request is
10 * a draft or one ready for review; a closed one was merged or closed
11 * without merging.
12 */
13export type State = "open" | "closed";
14
15/** Why an issue was closed. */
16export type IssueReason = "completed" | "not_planned";
17
18/**
19 * Something that should change in a repository: a bug, a feature, a
20 * question. Opened by a person, an agent or an integration. Pull requests
21 * are made against it; the one that is merged resolves it.
22 *
23 * Issues and pull requests share one sequence of numbers per repository.
24 */
25export type Issue = {
26 id: string;
27 repoId: string;
28 /** Shown as `#12`. */
29 number: number;
30 title: string;
31 /** Markdown. Also what an agent is given to work from. */
32 body: string;
33 labels: string[];
34 state: State;
35 /** Set when closed. */
36 reason: IssueReason | null;
37 /** The number of the pull request whose merge closed this issue. */
38 resolvedBy: number | null;
39 /** Who opened it: a person, an integration, or g1t (`kind` `agent`) for one its agent filed while at work. */
40 author: User;
41 /**
42 * For an issue g1t's agent filed: the person it was working for. They may
43 * manage it as its author could. See `workOwner`.
44 */
45 requestedBy: User | null;
46 /** RFC 3339. */
47 createdAt: string;
48 /** RFC 3339. */
49 updatedAt: string;
50 /** RFC 3339. */
51 closedAt: string | null;
52 /** Pull requests made against this issue, in any state. */
53 pullCount: number;
54 commentCount: number;
55 /** Usernames of the people it is assigned to. */
56 assignees: string[];
57 /** The numbers of the issues that have to be merged before this one is worked on. */
58 blockedBy: number[];
59 /**
60 * Whether a g1t agent takes it as soon as it can: at once, or when what it
61 * is blocked by has merged.
62 */
63 queued: boolean;
64 /**
65 * The agent working on it now: the one behind its newest pull request
66 * that is still in progress in a fork, such as `g1t`.
67 */
68 agent: string | null;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar69 /** The milestone it is in, if any. */
70 milestone?: MilestoneRef | null;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides71};
72
73/**
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar74 * A label of a repository: a name, a color and what it means. Issues and
75 * pull requests carry labels by name; names are lowercase.
76 */
77export type Label = {
78 name: string;
79 /** Six hex digits, without `#`. */
80 color: string;
81 description: string;
82 /** How many issues carry it, open or closed. */
83 issues: number;
84 /** How many pull requests carry it, in any state. */
85 pulls: number;
86};
87
88/** A milestone, as an issue or pull request names it. */
89export type MilestoneRef = { number: number; title: string };
90
91/**
92 * A goal, with an optional due date, that issues and pull requests are
93 * gathered under. Its progress is how many of them are closed.
94 */
95export type Milestone = {
96 /** Numbered from 1 in each repository, apart from issues. */
97 number: number;
98 title: string;
99 /** Markdown. */
100 description: string;
101 /** `YYYY-MM-DD`. */
102 dueOn: string | null;
103 state: State;
104 /** Open issues and pull requests in it. */
105 openItems: number;
106 /** Closed issues, and merged or closed pull requests, in it. */
107 closedItems: number;
108 createdAt: string;
109 updatedAt: string;
110 closedAt: string | null;
111};
112
113/** One milestone and what is in it, newest first. */
114export type MilestoneDetail = { milestone: Milestone; issues: Issue[]; pulls: Pull[] };
115
116/** How `setLabels` changes an item's labels. */
117export type LabelChange = "set" | "add" | "remove";
118
119/**
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides120 * Whose an issue or a pull request is to answer for: whoever asked g1t for
121 * it, or its author. They may change, close and steer it, are never asked to
122 * review it and cannot approve it, and see it as theirs. Mirrors
123 * `Pull::owner` in the Rust contracts.
124 */
125export function workOwner(item: Pick<Pull, "author" | "requestedBy">): User {
126 return item.requestedBy ?? item.author;
127}
128
129/** `draft` is still being worked on; `open` is ready for review. */
130export type PullStatus = "draft" | "open" | "merged" | "closed";
131
132/** Where the agent runs: on g1t's sandboxes, or in someone's own session. */
133export type Runtime = "hosted" | "external";
134
135/**
136 * A proposed change. It is made either in a fork created for it, which is
137 * how agents work, or on a branch pushed to the repository itself.
138 */
139export type Pull = {
140 id: string;
141 repoId: string;
142 /** Shown as `#12`. */
143 number: number;
144 /** The number of the issue this is for, if any. */
145 issue: number | null;
146 title: string;
147 /** Markdown: what changed and why. Set when marked ready. */
148 body: string | null;
149 /** A label for the agent doing the work, e.g. `claude-code`. */
150 agent: string;
151 runtime: Runtime;
152 status: PullStatus;
153 /** The fork holding the change, unless it is on a branch. */
154 fork: RepoPath | null;
155 /** The fork's repository id. */
156 forkRepoId: string | null;
157 /** The branch of the repository holding the change, unless it is in a fork. */
158 branch: string | null;
159 headCommit: string | null;
160 /**
161 * For a merged pull request, what the branch pointed to before the merge.
162 * Comparing against it shows what the pull request changed.
163 */
164 mergeBase: string | null;
165 /** Username of whoever merged it. */
166 mergedBy: string | null;
167 /** RFC 3339. */
168 mergedAt: string | null;
169 /**
170 * Set on a pull request closed because another one for the same issue was
171 * merged: that one's number.
172 */
173 supersededBy: number | null;
174 /**
175 * Where the latest run of the issue's acceptance checks stands, if there
176 * has been one against the current head.
177 */
178 checkStatus: CheckStatus | null;
179 /** The files it changes, as of its latest push. */
180 files: ChangedFile[];
181 /** Usernames of the people it is assigned to. */
182 assignees: string[];
183 /**
184 * Those whose review was asked for: usernames, and `g1t` when a g1t
185 * agent was asked.
186 */
187 reviewers: string[];
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar188 /**
189 * Teams whose review was asked for, as `workspace/slug`. A team stays here
190 * after review assignment picks people from it, who are in `reviewers`.
191 */
192 teamReviewers?: string[];
193 /** The labels it carries, by name. */
194 labels?: string[];
195 /** The milestone it is in, if any. */
196 milestone?: MilestoneRef | null;
197 /**
198 * The branch it merges into. Lists and `getPull` name it; null only in
199 * what services pass between themselves, for the default branch.
200 */
201 base?: string | null;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides202 /** Who opened it: a person, or g1t (`kind` `agent`, username `g1t`) for a change g1t made. */
203 author: User;
204 /**
205 * For a change g1t made: the person who asked for it, by assigning an issue
206 * or handing g1t the work. They answer for it as its author would. See
207 * `workOwner`.
208 */
209 requestedBy: User | null;
210 /** RFC 3339. */
211 createdAt: string;
212 /** RFC 3339. */
213 updatedAt: string;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge214 /** Who last moved its head (a user id), and when; absent until a push after rulesets arrived. */
215 headPushedBy?: string;
216 headPushedAt?: string;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides217};
218
219/** One file a pull request changes, and by how much. */
220export type ChangedFile = { path: string; additions: number; deletions: number };
221
222/**
223 * Another pull request in progress that changes some of the same files. Two
224 * for the same issue are alternatives; two for different issues are heading
225 * for a conflict.
226 */
227export type Overlap = {
228 number: number;
229 title: string;
230 /** The number of the issue the other pull request is for. */
231 issue: number | null;
232 /** The files both change. */
233 paths: string[];
234};
235
236/**
237 * On a pull request, `failed` means the merge queue took it out, until its
238 * head moves. The other states are from runs of commands written on issues,
239 * which g1t no longer runs.
240 */
241export type CheckStatus = "queued" | "running" | "passed" | "failed" | "errored";
242
243/** How one command went, in a run recorded before checks were workflows. */
244export type CheckResult = {
245 command: string;
246 passed: boolean;
247 /** Null when the command was stopped for taking too long. */
248 exitCode: number | null;
249 /** What the command printed; the end of it, when there was a lot. */
250 output: string;
251 durationMs: number;
252};
253
254/**
255 * A record against a pull request's head: the merge queue taking it out,
256 * with why, or an earlier run of commands written on its issue.
257 */
258export type CheckRun = {
259 id: string;
260 /** The commit that was checked. */
261 headCommit: string;
262 status: CheckStatus;
263 results: CheckResult[];
264 /** Why the checks could not be run, when `status` is `errored`. */
265 error: string | null;
266 /** RFC 3339. */
267 createdAt: string;
268 /** RFC 3339. */
269 finishedAt: string | null;
270};
271
272/** A reviewer's decision on a pull request. */
273export type Verdict = "approve" | "request_changes";
274
275/**
276 * A comment on an issue or a pull request. On a pull request it can sit on
277 * one line of the change, and it can carry a reviewer's verdict.
278 */
279export type Comment = {
280 id: string;
281 /**
282 * Something a person or an agent wrote, or something that happened: an
283 * assignment, a review asked for, a close.
284 */
285 kind: "comment" | "event";
286 author: User;
287 /**
288 * Markdown. For an event, what its author did, as the rest of a sentence
289 * that starts with their name: "assigned ana".
290 */
291 body: string;
292 /** The file commented on, for a comment on a line. */
293 path: string | null;
294 /** The line of that file, as numbered after the change. */
295 line: number | null;
296 verdict: Verdict | null;
297 /** RFC 3339. */
298 createdAt: string;
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts299 /** When its text was last edited, RFC 3339; absent if it never was. */
300 editedAt?: string;
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar301 /**
302 * Set when one of the workspace's agents wrote it, as itself. `author`
303 * is then the agent (its id, its handle as `username`, kind `agent`):
304 * show it by `agent.displayName` with an Agent badge, never as a person.
305 */
306 agent?: AgentRef;
307 /** Who the agent acted for, whose access capped it. Set with `agent`. */
308 actingFor?: User;
309 /**
310 * An agent's review. Its `verdict` (none for a review that only
311 * comments) is shown but advisory: it never counts toward required
312 * approvals or code owners, and never blocks a merge.
313 */
314 advisory?: boolean;
315};
316
317/**
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.318 * One of a workspace's own agents, as a comment or review it wrote before
319 * g1t's agents were removed shows it: Margo (@margo), with the face drawn
320 * from `avatarSeed`. Kept so those comments still render.
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar321 */
322export type AgentRef = {
323 id: string;
324 handle: string;
325 displayName: string;
326 avatarSeed: string;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides327};
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar328
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides329export type NewComment = {
330 /** May be empty when approving. */
331 body: string;
332 path?: string;
333 line?: number;
334 verdict?: Verdict;
335};
336
337/** What a sandbox needs to carry out a check run. */
338export type CheckJob = {
339 runId: string;
340 /** Lets the sandbox, and nothing else, report this run's results. */
341 token: string;
342 commands: string[];
343 /** The repository holding the commit: the fork, or the repository itself. */
344 source: RepoPath;
345 commit: string;
346 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
347 author: User;
348 /** Username of whoever wrote the checks: the issue's author. */
349 requestedBy: string;
350 repo: RepoPath;
351 number: number;
352};
353
354export type CheckReport = { results?: CheckResult[]; error?: string; skip?: boolean };
355
356export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note";
357
358/** One step of an agent's session: the "why" behind a pull request's commits. */
359export type SessionEntry = {
360 seq: number;
361 kind: SessionEntryKind;
362 text: string;
363 /** For tool calls and results. */
364 tool: string | null;
365 /** The fork's head commit when this entry was recorded, if known. */
366 commit: string | null;
367 /** RFC 3339. */
368 at: string;
369};
370
371export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> &
372 Partial<Pick<SessionEntry, "tool" | "commit">>;
373
374export type IssueDetail = {
375 issue: Issue;
376 /** Every pull request made against it, oldest first. */
377 pulls: Pull[];
378 comments: Comment[];
379};
380
381export type PullDetail = {
382 pull: Pull;
383 /** The issue it is for, if any. */
384 issue: Issue | null;
385 comments: Comment[];
386 /** The latest record against its head: the merge queue taking it out. */
387 checks: CheckRun | null;
388 /** Other pull requests in progress that change the same files. */
389 overlaps: Overlap[];
390 /**
391 * Whether the branch it would merge into has moved on without it, so that
392 * it has to catch up before it can merge.
393 */
394 behind: boolean;
395 /**
396 * Where it stands on its way to being merged, for a pull request g1t is
397 * seeing through. Null on anyone else's.
398 */
399 lifecycle: Lifecycle | null;
400 /**
401 * A merge was asked for while it was behind: g1t is bringing it up to
402 * date and will then land it.
403 */
404 landing: boolean;
405 /** Why g1t stopped working on it, if it did. */
406 stalled: string | null;
407 /** What workflow runs said about its head commit, one per workflow. */
408 statuses?: CommitStatus[];
409 /**
410 * Whether it merges cleanly into the branch it targets, worked out ahead
411 * of time whenever either side moves.
412 */
413 mergeable?: Mergeable;
414 /** When `mergeable` is `conflicting`: the files that conflict. */
415 conflicts?: string[];
416 /** Earlier records like `checks`, newest first, without their output. */
417 earlierChecks?: CheckRun[];
418 /**
419 * The checks the default branch's protection requires, each as it stands
420 * on the head commit. Empty when none are required.
421 */
422 requiredChecks?: RequiredCheck[];
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar423 /**
424 * Who owns the files it changes (the CODEOWNERS file of the branch it
425 * merges into) and whose approval is still needed. Absent without one.
426 */
427 codeOwners?: PullCodeOwners | null;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge428 /**
429 * The rules of the branch it merges into it does not meet yet, for whoever
430 * is looking: what refuses the merge, what they may bypass, and what rulesets
431 * in evaluate would refuse. Absent once it is closed or merged.
432 */
433 rules?: MergeRules | null;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides434};
435
436/** Where a required check stands on a commit; `expected` when nothing has reported it yet. */
437export type RequiredState = "success" | "failure" | "pending" | "expected";
438
439/** One check a branch's protection requires, as it stands on a commit. */
440export type RequiredCheck = {
441 /** A workflow's name, such as `CI`, or another status's context. */
442 name: string;
443 state: RequiredState;
444 description: string | null;
445 /** Where to see more: the workflow run, for one a workflow reported. */
446 targetUrl: string | null;
447};
448
449/** A check name reported on a repository's commits lately, for choosing required checks. */
450export type SeenCheck = {
451 name: string;
452 /** The events it was reported for, such as `pull_request`; empty for a status that names none. */
453 events: string[];
454 /** RFC 3339. */
455 lastSeen: string;
456};
457
458/**
459 * A status context's check name: `CI / pull_request` is the `CI` check,
460 * reported for a `pull_request` event. A context that does not end in an
461 * event, such as `g1t / deploy`, is its own name. As the work service reads it.
462 */
463export function checkName(context: string): string {
464 const at = context.lastIndexOf(" / ");
465 if (at <= 0) return context;
466 return STATUS_EVENTS.has(context.slice(at + 3)) ? context.slice(0, at) : context;
467}
468
469const STATUS_EVENTS = new Set([
470 "push",
471 "pull_request",
472 "pull_request_target",
473 "pull_request_review",
474 "merge_group",
475 "workflow_dispatch",
476 "workflow_run",
477 "workflow_call",
478 "schedule",
479 "release",
480 "issues",
481 "issue_comment",
482 "repository_dispatch",
483]);
484
485/**
486 * Whether a pull request merges cleanly into the branch it targets:
487 * `unknown` when it was never worked out or could not be, `checking` while
488 * a probe merges the two.
489 */
490export type Mergeable = "clean" | "conflicting" | "unknown" | "checking";
491
492/** What a sandbox needs to find out whether a pull request merges cleanly. */
493export type MergecheckJob = {
494 pullId: string;
495 /** Lets the sandbox, and nothing else, report this probe. */
496 token: string;
497 repo: RepoPath;
498 number: number;
499 defaultBranch: string;
500 /** The default branch's commit to merge into. */
501 base: string;
502 /** The repository holding the change: its fork, or the repository. */
503 source: RepoPath;
504 /** The branch of `source` holding it. */
505 branch: string;
506 /** The change's commit. */
507 head: string;
508 /** Who the pull request is for (`workOwner`: whoever asked g1t for it, or its author), and so can read its source. */
509 author: User;
510};
511
512/** What a workflow run (or another tool) says about a commit. */
513export type CommitStatus = {
514 /** What reported it, such as `CI / push`. */
515 context: string;
516 state: "pending" | "success" | "failure" | "error";
517 description: string | null;
518 targetUrl: string | null;
519 updatedAt: string;
Merge checks: statuses and check runs on every commit520 /** The integration that reported it: `actions`, `deployments`, `security`, `g1t` or `api`. */
521 source?: string;
522 /** Set on the status a check run stands as: the check run's id. */
523 checkRunId?: string;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides524};
525
526/**
527 * A step on the way from an assigned issue to a pull request that is ready
528 * to merge. g1t takes each one without being asked: `working` (the agent is
529 * making the change), `checking`, `reviewing`, `revising` (the agent is
530 * addressing failed checks or a review), `catching_up` (merging in the
531 * branch it would land on), `answering` (woken to answer another agent),
532 * then `ready` for a person to merge. `needs_you`
533 * means g1t has stopped and a person decides what happens next.
534 */
535export type Stage =
536 | "working"
537 | "checking"
538 | "reviewing"
539 | "revising"
540 | "catching_up"
541 | "answering"
542 | "queued"
543 | "ready"
544 | "needs_you";
545
546export type Lifecycle = {
547 stage: Stage;
548 /** One sentence saying what is happening, or why it stopped. */
549 detail: string;
550 /** How many times the agent has been sent back to revise it. */
551 revisions: number;
552};
553
554/**
555 * How a repository wants its pull requests handled. A repository that has
556 * changed nothing has the defaults.
557 */
558export type RepoSettings = {
559 /**
560 * Land a g1t agent's pull request without a person once it is ready:
561 * checks passed and approved as the settings below require.
562 */
563 autoMerge: boolean;
564 /**
565 * The checks that must pass on a pull request's head before it may merge
566 * into the default branch, by name: a workflow's name (`CI`) or another
567 * status's context (`g1t / deploy`). The same for people and agents, and
568 * for the merge queue.
569 */
570 requiredChecks: string[];
571 /**
572 * Refuse to merge a pull request that does not contain the default
573 * branch's latest commits, so that what merges is what was checked. When
574 * off, merging one that is behind brings it up to date first.
575 */
576 requireUpToDate: boolean;
577 /**
578 * How many approving reviews a pull request needs before it may merge. A
579 * reviewer who has since asked for changes blocks it.
580 */
581 requiredApprovals: number;
582 /** Whether a g1t agent's approval counts towards `requiredApprovals`. */
583 countAgentApprovals: boolean;
584 /** Whether someone who may merge can bypass required checks that have not passed. */
585 allowIgnoringChecks: boolean;
586 /**
587 * Merge through a queue: pull requests are tested together with those
588 * ahead of them, and only a combination that passed reaches the default
589 * branch.
590 */
591 mergeQueue: boolean;
592 /**
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar593 * Refuse to merge until the code owners of every file it changes have
594 * approved, as many as each section asks. People only; `g1t` only where
595 * the file names `@g1t`.
596 */
597 requireCodeOwnerReview?: boolean;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides598 /** Username of the member who last changed the settings, if anyone has. */
599 updatedBy: string | null;
600 /** RFC 3339. */
601 updatedAt: string | null;
602};
603
604/** The settings a member can change. */
605export type RepoSettingsInput = Omit<RepoSettings, "updatedBy" | "updatedAt">;
606
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.607/**
608 * What the runner is to do next for a pull request: always nothing now,
609 * since no step of a pull request is carried out in a sandbox any more.
610 */
611export type Advance = { action: "none" };
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides612
613export type OpenIssueInput = {
614 title: string;
615 body: string;
616 labels?: string[];
617 /** Deprecated: commands, added to the body under "Definition of done". */
618 checks?: string[];
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar619 /** The number of the milestone to put it in. Needs the Triage role. */
620 milestone?: number;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides621};
622
623export type UpdateIssueInput = {
624 title?: string;
625 body?: string;
626 labels?: string[];
627 /** Usernames of the people it is assigned to; replaces the whole set. */
628 assignees?: string[];
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar629 /** The number of the milestone to put it in; 0 takes it out. */
630 milestone?: number;
Merge branch 'worktree-agent-a6ec0a89d7fe7a9ae'631 /** The issues of the same repository that have to land first; replaces the whole set. */
632 blocked_by?: number[];
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides633};
634
635export type OpenPullInput = {
636 /** The number of the issue this is for. */
637 issue?: number;
638 /** Defaults to the issue's title; required without an issue. */
639 title?: string;
640 /** What changed and why. Usually set later, when a draft is marked ready. */
641 body?: string;
642 /**
643 * A branch of the repository that already holds the change. The pull
644 * request is then ready for review at once and has no fork.
645 */
646 branch?: string;
647 agent: string;
648 runtime: Runtime;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar649 /** The branch to merge into: the default branch when left out. */
650 base?: string;
Open a pull request as a draft: it can't merge, and agents' review routines wait, until it is marked ready651 /**
652 * Opened from a branch as a draft, still being worked on: it can't merge,
653 * and agents' review routines wait, until it is marked ready.
654 */
655 draft?: boolean;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides656};
657
658/** One repository's pull requests from `pullsForRepos`, newest first. */
659export type RepoPulls = {
660 repoId: string;
661 /** Draft and open. */
662 open: Pull[];
663 /** Merged and closed. */
664 closed: Pull[];
665};
666
667/** Issues, pull requests, comments and sessions. */
Merge checks: statuses and check runs on every commit668export interface WorkApi extends RulesApi, ChecksApi {
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides669 openIssue(actor: User, repo: RepoPath, input: OpenIssueInput): Promise<Result<Issue>>;
670 /** Newest first. */
671 listIssues(
672 repo: RepoPath,
673 viewer: Viewer,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar674 filter?: { state?: State; label?: string; milestone?: number },
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides675 ): Promise<Result<Issue[]>>;
676 getIssue(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<IssueDetail>>;
677 /** The author or a member of the workspace may. */
678 updateIssue(actor: User, repo: RepoPath, number: number, input: UpdateIssueInput): Promise<Result<Issue>>;
679 closeIssue(actor: User, repo: RepoPath, number: number, reason?: IssueReason): Promise<Result<Issue>>;
680 reopenIssue(actor: User, repo: RepoPath, number: number): Promise<Result<Issue>>;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar681 /** A repository's labels, by name, each with how many issues and pull requests carry it. */
682 listLabels(repo: RepoPath, viewer: Viewer): Promise<Result<Label[]>>;
683 /**
684 * Creates a label, or with `name` changes one; renaming it renames it on
685 * everything that carries it. Needs the Triage role.
686 */
687 saveLabel(
688 actor: User,
689 repo: RepoPath,
690 label: { name?: string; newName?: string; color?: string; description?: string },
691 ): Promise<Result<Label>>;
692 /** Removes a label from the repository and everything carrying it. */
693 deleteLabel(actor: User, repo: RepoPath, name: string): Promise<Result<boolean>>;
694 /** Adds the default labels the repository does not have yet; returns them all. */
695 addDefaultLabels(actor: User, repo: RepoPath): Promise<Result<Label[]>>;
696 /**
697 * The labels of an issue or a pull request, replaced, added to or taken
698 * from. Labels the repository lacks are created for someone with the
699 * Triage role. Returns its labels now.
700 */
701 setLabels(
702 actor: User,
703 repo: RepoPath,
704 number: number,
705 labels: string[],
706 change?: LabelChange,
707 ): Promise<Result<string[]>>;
708 /** Open ones by due date, then closed ones; both unless `state` says. */
709 listMilestones(repo: RepoPath, viewer: Viewer, state?: State): Promise<Result<Milestone[]>>;
710 getMilestone(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<MilestoneDetail>>;
711 /** Creates a milestone, or with `number` changes the fields given. Needs the Triage role. */
712 saveMilestone(
713 actor: User,
714 repo: RepoPath,
715 milestone: { number?: number; title?: string; description?: string; dueOn?: string; state?: State },
716 ): Promise<Result<Milestone>>;
717 deleteMilestone(actor: User, repo: RepoPath, number: number): Promise<Result<boolean>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides718 /** How many issues and pull requests are open. */
719 counts(repo: RepoPath, viewer: Viewer): Promise<Result<{ issues: number; pulls: number }>>;
720
721 /**
722 * On an issue or a pull request. On a pull request it may name a line of
723 * the change and carry a verdict; nobody can give a verdict on their own.
724 */
725 addComment(actor: User, repo: RepoPath, number: number, comment: NewComment): Promise<Result<Comment>>;
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts726 /**
727 * Changes a comment's text. Its author may, and so may anyone with the
728 * Maintain role or higher; notes of what happened cannot be changed.
729 */
730 editComment(actor: User, repo: RepoPath, commentId: string, body: string): Promise<Result<Comment>>;
731 /**
732 * Deletes a comment: its author, or anyone with the Maintain role or
733 * higher. A review that gave a verdict cannot be deleted, only edited.
734 */
735 deleteComment(actor: User, repo: RepoPath, commentId: string): Promise<Result<boolean>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides736
737 /**
738 * Always refused now: a pull request's checks are the workflows run on
739 * it. Kept for a runner from before.
740 */
741 startChecks(pullId: string): Promise<Result<CheckJob>>;
742 /**
743 * What a sandbox says about a run from before checks were workflows: so
744 * one still finishing is recorded.
745 */
746 reportChecks(runId: string, token: string, report: CheckReport): Promise<Result<CheckRun>>;
747 /**
748 * Claims the probe of whether a pull request merges cleanly that a
749 * `pull.mergecheck` event asked for. Refused when it is no longer wanted
750 * or the repository has as many running as it may. For the runner service.
751 */
752 startMergecheck(pullId: string): Promise<Result<MergecheckJob>>;
753 /** Records that a probe could not be carried out. For the runner service. */
754 failMergecheck(pullId: string, token: string, error: string): Promise<Result<Mergeable>>;
755
756 /**
757 * Works out the next step for a pull request g1t is seeing through and,
758 * if there is one to take now, claims it, so that it is taken once
759 * however often this is called. For the runner service.
760 */
761 advance(pullId: string): Promise<Advance>;
762 /** Records that a step could not be carried out, so a person is asked. */
763 stall(pullId: string, reason: string): Promise<boolean>;
764 /** Ids of the open pull requests g1t is seeing through. */
765 managedPulls(repoId?: string): Promise<string[]>;
766
767 /** A repository's merge queue: what is in it, in order, and what recently left. */
768 queue(repo: RepoPath, viewer: Viewer): Promise<Result<QueueView>>;
769 /**
770 * The next batch of combined states to test for a repository, one per
771 * entry; empty while a batch is being tested or nothing waits.
772 */
773 queueBuild(repoId: string): Promise<QueueJob[]>;
774 /** Reports that a combined state could not be built or checked. */
775 failQueue(entryId: string, token: string, error: string): Promise<Result<QueueState>>;
776 /** Takes a pull request out of the merge queue. Members only. */
777 removeFromQueue(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
778
779 getSettings(repo: RepoPath, viewer: Viewer): Promise<Result<RepoSettings>>;
780 /** The check names reported on the repository's commits in the last 30 days, most recent first. */
781 seenChecks(repo: RepoPath, viewer: Viewer): Promise<Result<SeenCheck[]>>;
782 /** Members of the repository's workspace only. */
783 updateSettings(actor: User, repo: RepoPath, settings: RepoSettingsInput): Promise<Result<RepoSettings>>;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar784 /** The CODEOWNERS file at a branch (the default when left out), checked. Needs Read. */
785 codeownersErrors(repo: RepoPath, viewer: Viewer, ref?: string | null): Promise<Result<CodeownersReport>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides786
787 /**
788 * Opens a pull request: a draft with a fork to push to, or, given a
789 * branch, one ready for review.
790 */
791 openPull(actor: User, repo: RepoPath, input: OpenPullInput): Promise<Result<Pull>>;
792 /** Newest first. */
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar793 listPulls(
794 repo: RepoPath,
795 viewer: Viewer,
796 state?: State,
797 filter?: { label?: string; milestone?: number; base?: string },
798 ): Promise<Result<Pull[]>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides799 /**
800 * The newest `limit` open and closed pull requests of each repository,
801 * in one call. Repositories the viewer cannot read, and forks, are left
802 * out: ask those with `listPulls`.
803 */
804 pullsForRepos(repoIds: string[], viewer: Viewer, limit: number): Promise<RepoPulls[]>;
805 getPull(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<PullDetail>>;
806 /**
807 * Changes who a pull request is assigned to and whose review is asked
808 * for; each list given replaces the whole set. Asking for `g1t`'s
809 * review does not by itself start one: the runner's `review` does.
810 */
811 updatePull(
812 actor: User,
813 repo: RepoPath,
814 number: number,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar815 changes: {
816 assignees?: string[];
817 reviewers?: string[];
818 labels?: string[];
819 /** 0 takes it out of its milestone. */
820 milestone?: number;
821 /** The branch it merges into. Needs the Write role. */
822 base?: string;
823 },
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides824 ): Promise<Result<Pull>>;
825 /**
826 * Brings a pull request up to date with the default branch in seconds,
827 * without a sandbox, when the two changed different files: the merge
828 * commit is pushed to its branch as `actor`, who must be whoever opened
829 * it (for a fork) or a member (for a branch). Otherwise `needs_agent`, and
830 * nothing is pushed: the runner's `update` is the way on.
831 */
832 catchUpPull(actor: User, repo: RepoPath, number: number): Promise<Result<PullBranchUpdate>>;
833 /** Marks a draft ready for review and sets its description. */
834 readyPull(actor: User, repo: RepoPath, number: number, summary: string): Promise<Result<Pull>>;
835 closePull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
Merge Actions: cross-repo workflows and actions, release and deployment triggers, step timeouts836 /** Opens a closed pull request again, as the draft it was if it was closed as one. Never a merged one. */
837 reopenPull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
838 /** Turns an open pull request back into a draft; it leaves the merge queue. */
839 convertPullToDraft(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides840 /**
841 * Lands the pull request on the repository's default branch. Unless
842 * `keepIssueOpen`, that resolves the issue it was for: the issue closes
843 * naming this pull request, and the others still in progress for it close
844 * as superseded. Only members of the repository's workspace may merge.
845 *
846 * If the default branch has moved, the pull request is brought up to date
847 * first and lands when that is done; it comes back still open, and
848 * `PullDetail.landing` is true meanwhile. A repository that requires pull
849 * requests to be up to date refuses instead.
850 */
851 mergePull(
852 actor: User,
853 repo: RepoPath,
854 number: number,
855 options?: {
856 keepIssueOpen?: boolean;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge857 /** Merge although required checks have not passed, where the rule requiring them allows it. */
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides858 ignoreChecks?: boolean;
Merge rulesets: branch and tag rules, agent-first, enforced on push and merge859 /** Merge past rules a ruleset lets the actor bypass. Recorded as a bypass. */
860 bypassRules?: boolean;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides861 },
862 ): Promise<Result<Pull>>;
863 /**
864 * Drafts and open pull requests the viewer started, most recently active
865 * first, each with where it stands if g1t is seeing it through.
866 */
867 listActivePulls(
868 viewer: Viewer,
869 ): Promise<{ pull: Pull; issue: Issue | null; lifecycle: Lifecycle | null }[]>;
870 /**
871 * The issues and pull requests a person opened, a page at a time, only
872 * on repositories the viewer may read. Not found for no such account.
873 */
874 byAuthor(username: string, viewer: Viewer, filter?: AuthoredFilter): Promise<Result<Authored>>;
Chat controls, public profiles, shadcn selects, and no Docs tab in a project875 /**
876 * What a person did each day of the last year (issues and pull requests
877 * opened, reviews given), only on repositories the viewer may read: the
878 * calendar on their profile. Not found for no such account.
879 */
880 contributions(username: string, viewer: Viewer): Promise<Result<Contributions>>;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides881
882
883 /** Open issues assigned to the viewer, most recently changed first. */
884 listAssignedIssues(viewer: Viewer): Promise<Issue[]>;
885
886 appendSession(actor: User, repo: RepoPath, number: number, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
887 readSession(repo: RepoPath, number: number, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
888}
889
890/**
891 * What to pass `ReposApi.compare` to see what a pull request changes.
892 *
893 * A fork is compared as a whole. A branch is compared by name while the
894 * pull request is open, and by the commit it was merged or closed at
895 * afterwards, so later pushes to the branch do not change the record.
896 */
897export function pullComparison(pull: Pull): {
898 repoId: string;
899 base: string | null;
900 head: string | null;
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar901 /** The branch it merges into, which it is compared from. */
902 baseBranch: string | null;
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides903} {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar904 const baseBranch = pull.base ?? null;
905 if (pull.forkRepoId) return { repoId: pull.forkRepoId, base: pull.mergeBase, head: null, baseBranch };
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides906 const settled = pull.status === "merged" || pull.status === "closed";
907 return {
908 repoId: pull.repoId,
909 base: pull.mergeBase,
910 head: (settled && pull.headCommit) || pull.branch,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar911 baseBranch,
Security: postcss-selector-parser 7.1.6 and basic-ftp 6.2.2, pinned by overrides912 };
913}
914
915
916export type QueueState = "waiting" | "testing" | "passed" | "failed" | "landed" | "removed";
917
918/** One pull request's place in a merge queue. */
919export type QueueEntry = {
920 id: string;
921 number: number;
922 title: string;
923 agent: string;
924 state: QueueState;
925 /** The pull requests merged ahead of it in the state being tested, in order. */
926 ahead: number[];
927 baseCommit: string | null;
928 combinedCommit: string | null;
929 error: string | null;
930 results: CheckResult[];
931 /** Username of whoever merged it into the queue: a person, or `g1t`. */
932 enqueuedBy: string;
933 /** RFC 3339. */
934 createdAt: string;
935 /** RFC 3339. */
936 finishedAt: string | null;
937};
938
939/** A repository's merge queue: what is in it, in order, and what recently left. */
940export type QueueView = {
941 enabled: boolean;
942 active: QueueEntry[];
943 /** Newest first. */
944 recent: QueueEntry[];
945};
946
947export type QueueStackItem = {
948 number: number;
949 title: string;
950 /** The repository holding the change, and its branch. */
951 source: RepoPath;
952 branch: string;
953 commit: string;
954};
955
956/** What a sandbox needs to build and check one combined state. */
957export type QueueJob = {
958 entryId: string;
959 token: string;
960 repo: RepoPath;
961 defaultBranch: string;
962 baseCommit: string;
963 branch: string;
964 stack: QueueStackItem[];
965 /** Always empty: the state is checked by the merge_group workflows run on it. */
966 checks: string[];
967 /** Always empty, as `checks`. */
968 contractChecks: string[];
969 actor: User;
970};
971
972// --- A person's work ---------------------------------------------------------
973// Mirrors the `Authored*` types in `crates/contracts/src/work.rs`.
974
975export type AuthoredKind = "issue" | "pull";
976/** `closed` takes in merged pull requests too; `merged` is only those. */
977export type AuthoredState = "open" | "closed" | "merged";
978/** `created` is newest first, `updated` most recently changed, `oldest` oldest first. */
979export type AuthoredSort = "created" | "updated" | "oldest";
980
981/** The most items one `byAuthor` page holds. */
982export const AUTHORED_PAGE = 25;
983
984export type AuthoredFilter = {
985 kind?: AuthoredKind;
986 state?: AuthoredState;
987 /** `namespace/name`. */
988 repo?: string;
989 sort?: AuthoredSort;
990 /** The `next` of the page before. */
991 before?: string;
992 limit?: number;
993};
994
995export type AuthoredItem = {
996 kind: AuthoredKind;
997 repo: RepoPath;
998 number: number;
999 title: string;
1000 /** A merged pull request is closed. */
1001 state: State;
1002 /** A pull request's own status; null on an issue. */
1003 status: PullStatus | null;
1004 /** Why an issue was closed. */
1005 reason: IssueReason | null;
1006 draft: boolean;
1007 merged: boolean;
1008 /** RFC 3339. */
1009 createdAt: string;
1010 /** RFC 3339. */
1011 updatedAt: string;
1012 /** RFC 3339. */
1013 mergedAt: string | null;
1014};
1015
1016/** Over every repository the viewer may read, whatever the filters. */
1017export type AuthoredCounts = {
1018 pullsMerged: number;
1019 pullsOpen: number;
1020 pulls: number;
1021 issues: number;
1022 issuesOpen: number;
1023};
1024
1025export type Authored = {
1026 items: AuthoredItem[];
1027 /** Pass as `before` for the next page; null on the last. */
1028 next: string | null;
1029 counts: AuthoredCounts;
1030 /** The repositories they worked in that the viewer may read, most work first. */
1031 repos: { repo: RepoPath; count: number }[];
1032};
Chat controls, public profiles, shadcn selects, and no Docs tab in a project1033
1034// Mirrors `Contributions` in `crates/contracts/src/work.rs`.
1035
1036/** How many days `contributions` covers: today and the 364 before it. */
1037export const CONTRIBUTION_DAYS = 365;
1038
1039/** One day with something on it; days with nothing are left out. */
Cards you act on in chat; agents comment and review as themselves; names shown cleanly; commits on the calendar1040export type ContributionDay = {
1041 /** `YYYY-MM-DD`, UTC. */
1042 date: string;
1043 /** Everything that day, commits included. */
1044 count: number;
1045 /** How many of `count` are commits they pushed to a default branch. */
1046 commits?: number;
1047};
Chat controls, public profiles, shadcn selects, and no Docs tab in a project1048
1049/** A person's year, as far as the viewer may see. */
1050export type Contributions = {
1051 /** Oldest first. */
1052 days: ContributionDay[];
1053 total: number;
1054 /** The first day counted, `YYYY-MM-DD`; the last is today (UTC). */
1055 from: string;
1056};
Merge branch 'worktree-agent-a6ec0a89d7fe7a9ae'1057
1058// --- Coordinating work: sessions, claims and dependencies -----------------
1059// Mirrors `crates/contracts/src/work/coordination.rs`.
1060
1061/** The harness a session runs in. */
1062export type Harness = "claude_code" | "codex" | "cursor" | "copilot" | "other";
1063
1064/** Active: heard from in 15 minutes; idle: not since; ended: ended, or quiet for 2 hours. */
1065export type SessionState = "active" | "idle" | "ended";
1066
1067/** One run of someone's own harness on a repository. */
1068export type WorkSession = {
1069 /** `ses_…`. */
1070 id: string;
1071 repoId: string;
1072 issue: number | null;
1073 pull: number | null;
1074 branch: string | null;
1075 actor: { id: string; username: string };
1076 harness: Harness;
1077 harnessSessionId: string | null;
1078 title: string | null;
1079 state: SessionState;
1080 headCommit: string | null;
1081 startedAt: string;
1082 lastSeenAt: string;
1083 endedAt: string | null;
1084 /** What the harness said, or `timed_out`. */
1085 endReason: string | null;
1086};
1087
1088export type ClaimState = "active" | "released" | "expired" | "handed_off";
1089
1090/** An advisory, expiring "I'm working on #N". Never a lock. */
1091export type Claim = {
1092 /** `clm_…`. */
1093 id: string;
1094 repoId: string;
1095 issue: number;
1096 sessionId: string | null;
1097 actor: { id: string; username: string };
1098 harness: Harness | null;
1099 note: string | null;
1100 state: ClaimState;
1101 createdAt: string;
1102 expiresAt: string;
1103 endedAt: string | null;
1104 endReason: string | null;
1105 /** For a hand-off: who it was handed to; null for anyone. */
1106 handoffTo: string | null;
1107};
1108
1109/** `session.started` and `session.ended`. */
1110export type SessionEventData = { repoId: string; number?: number; issue?: number; session: WorkSession };
1111
1112/** `work.claimed`, `work.released`, `work.claim_expired` and `work.handoff_requested`. */
1113export type ClaimEventData = {
1114 repoId: string;
1115 number: number;
1116 issue: number;
1117 claim: Claim;
1118 others?: Claim[];
1119 tookOver?: string;
1120 reason?: string;
1121 from?: string;
1122 to?: string;
1123 note?: string;
1124};
1125
1126/** `dependency.changed`. */
1127export type DependencyChangedData = {
1128 repoId: string;
1129 number: number;
1130 issue: number;
1131 blockedBy: number[];
1132 added: number[];
1133 removed: number[];
1134 cleared: number[];
1135 remaining: number[];
1136 mergedPull?: number;
1137};
1138
1139/** `overlap.detected`, sent once for each side of a new overlap. */
1140export type OverlapDetectedData = {
1141 repoId: string;
1142 number: number;
1143 pull: number;
1144 otherPull: number;
1145 files: string[];
1146 issue?: number;
1147 otherIssue?: number;
1148};

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