g1t/packages/contracts/src/work.ts

197 lines6,861 bytesCodeBlame
1import type { User, Viewer } from "./identity";
2import type { RepoPath } from "./repos";
3import type { Result } from "./result";
4
5/**
6 * The filter on lists of issues and pull requests. An open pull request is
7 * a draft or one ready for review; a closed one was merged or closed
8 * without merging.
9 */
10export type State = "open" | "closed";
11
12/** Why an issue was closed. */
13export type IssueReason = "completed" | "not_planned";
14
15/**
16 * Something that should change in a repository: a bug, a feature, a
17 * question. Opened by a person, an agent or an integration. Pull requests
18 * are made against it; the one that is merged resolves it.
19 *
20 * Issues and pull requests share one sequence of numbers per repository.
21 */
22export type Issue = {
23 id: string;
24 repoId: string;
25 /** Shown as `#12`. */
26 number: number;
27 title: string;
28 /** Markdown. Also what an agent is given to work from. */
29 body: string;
30 labels: string[];
31 /** Commands that must pass for a pull request to be accepted. */
32 checks: string[];
33 state: State;
34 /** Set when closed. */
35 reason: IssueReason | null;
36 /** The number of the pull request whose merge closed this issue. */
37 resolvedBy: number | null;
38 author: User;
39 /** RFC 3339. */
40 createdAt: string;
41 /** RFC 3339. */
42 updatedAt: string;
43 /** RFC 3339. */
44 closedAt: string | null;
45 /** Pull requests made against this issue, in any state. */
46 pullCount: number;
47 commentCount: number;
48};
49
50/** `draft` is still being worked on; `open` is ready for review. */
51export type PullStatus = "draft" | "open" | "merged" | "closed";
52
53/** Where the agent runs: on g1t's sandboxes, or in someone's own session. */
54export type Runtime = "hosted" | "external";
55
56/** A proposed change, made in its own fork by an agent or a person. */
57export type Pull = {
58 id: string;
59 repoId: string;
60 /** Shown as `#12`. */
61 number: number;
62 /** The number of the issue this is for, if any. */
63 issue: number | null;
64 title: string;
65 /** Markdown: what changed and why. Set when marked ready. */
66 body: string | null;
67 /** A label for the agent doing the work, e.g. `claude-code`. */
68 agent: string;
69 runtime: Runtime;
70 status: PullStatus;
71 fork: RepoPath;
72 /** The fork's repository id. */
73 forkRepoId: string;
74 headCommit: string | null;
75 /**
76 * For a merged pull request, what the branch pointed to before the merge.
77 * Comparing against it shows what the pull request changed.
78 */
79 mergeBase: string | null;
80 /** Username of whoever merged it. */
81 mergedBy: string | null;
82 /** RFC 3339. */
83 mergedAt: string | null;
84 /**
85 * Set on a pull request closed because another one for the same issue was
86 * merged: that one's number.
87 */
88 supersededBy: number | null;
89 author: User;
90 /** RFC 3339. */
91 createdAt: string;
92 /** RFC 3339. */
93 updatedAt: string;
94};
95
96export type Comment = {
97 id: string;
98 author: User;
99 /** Markdown. */
100 body: string;
101 /** RFC 3339. */
102 createdAt: string;
103};
104
105export type SessionEntryKind = "prompt" | "message" | "tool_call" | "tool_result" | "note";
106
107/** One step of an agent's session: the "why" behind a pull request's commits. */
108export type SessionEntry = {
109 seq: number;
110 kind: SessionEntryKind;
111 text: string;
112 /** For tool calls and results. */
113 tool: string | null;
114 /** The fork's head commit when this entry was recorded, if known. */
115 commit: string | null;
116 /** RFC 3339. */
117 at: string;
118};
119
120export type NewSessionEntry = Pick<SessionEntry, "kind" | "text"> &
121 Partial<Pick<SessionEntry, "tool" | "commit">>;
122
123export type IssueDetail = {
124 issue: Issue;
125 /** Every pull request made against it, oldest first. */
126 pulls: Pull[];
127 comments: Comment[];
128};
129
130export type PullDetail = {
131 pull: Pull;
132 /** The issue it is for, if any. */
133 issue: Issue | null;
134 comments: Comment[];
135};
136
137export type OpenIssueInput = {
138 title: string;
139 body: string;
140 labels?: string[];
141 checks?: string[];
142};
143
144export type UpdateIssueInput = { title?: string; body?: string; labels?: string[] };
145
146export type OpenPullInput = {
147 /** The number of the issue this is for. */
148 issue?: number;
149 /** Defaults to the issue's title; required without an issue. */
150 title?: string;
151 agent: string;
152 runtime: Runtime;
153};
154
155/** Issues, pull requests, comments and sessions. */
156export interface WorkApi {
157 openIssue(actor: User, repo: RepoPath, input: OpenIssueInput): Promise<Result<Issue>>;
158 /** Newest first. */
159 listIssues(
160 repo: RepoPath,
161 viewer: Viewer,
162 filter?: { state?: State; label?: string },
163 ): Promise<Result<Issue[]>>;
164 getIssue(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<IssueDetail>>;
165 /** The author or a member of the workspace may. */
166 updateIssue(actor: User, repo: RepoPath, number: number, input: UpdateIssueInput): Promise<Result<Issue>>;
167 closeIssue(actor: User, repo: RepoPath, number: number, reason?: IssueReason): Promise<Result<Issue>>;
168 reopenIssue(actor: User, repo: RepoPath, number: number): Promise<Result<Issue>>;
169 /** The default labels, then every other label in use on the repository. */
170 listLabels(repo: RepoPath, viewer: Viewer): Promise<Result<string[]>>;
171 /** How many issues and pull requests are open. */
172 counts(repo: RepoPath, viewer: Viewer): Promise<Result<{ issues: number; pulls: number }>>;
173
174 /** On an issue or a pull request. */
175 addComment(actor: User, repo: RepoPath, number: number, body: string): Promise<Result<Comment>>;
176
177 /** Forks the repo and returns the draft pull request to push to. */
178 openPull(actor: User, repo: RepoPath, input: OpenPullInput): Promise<Result<Pull>>;
179 /** Newest first. */
180 listPulls(repo: RepoPath, viewer: Viewer, state?: State): Promise<Result<Pull[]>>;
181 getPull(repo: RepoPath, number: number, viewer: Viewer): Promise<Result<PullDetail>>;
182 /** Marks a draft ready for review and sets its description. */
183 readyPull(actor: User, repo: RepoPath, number: number, summary: string): Promise<Result<Pull>>;
184 closePull(actor: User, repo: RepoPath, number: number): Promise<Result<Pull>>;
185 /**
186 * Lands the pull request on the repository's default branch. Unless
187 * `keepIssueOpen`, that resolves the issue it was for: the issue closes
188 * naming this pull request, and the others still in progress for it close
189 * as superseded. Only members of the repository's workspace may merge.
190 */
191 mergePull(actor: User, repo: RepoPath, number: number, keepIssueOpen?: boolean): Promise<Result<Pull>>;
192 /** Drafts and open pull requests the viewer started, most recently active first. */
193 listActivePulls(viewer: Viewer): Promise<{ pull: Pull; issue: Issue | null }[]>;
194
195 appendSession(actor: User, repo: RepoPath, number: number, entries: NewSessionEntry[]): Promise<Result<{ count: number }>>;
196 readSession(repo: RepoPath, number: number, viewer: Viewer, afterSeq?: number): Promise<Result<SessionEntry[]>>;
197}