forge-cc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.forge.json +5 -0
  2. package/AGENTS.md +42 -0
  3. package/README.md +283 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +148 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/loader.d.ts +2 -0
  8. package/dist/config/loader.js +44 -0
  9. package/dist/config/loader.js.map +1 -0
  10. package/dist/config/schema.d.ts +57 -0
  11. package/dist/config/schema.js +15 -0
  12. package/dist/config/schema.js.map +1 -0
  13. package/dist/gates/index.d.ts +11 -0
  14. package/dist/gates/index.js +106 -0
  15. package/dist/gates/index.js.map +1 -0
  16. package/dist/gates/lint-gate.d.ts +2 -0
  17. package/dist/gates/lint-gate.js +66 -0
  18. package/dist/gates/lint-gate.js.map +1 -0
  19. package/dist/gates/prd-gate.d.ts +7 -0
  20. package/dist/gates/prd-gate.js +193 -0
  21. package/dist/gates/prd-gate.js.map +1 -0
  22. package/dist/gates/runtime-gate.d.ts +5 -0
  23. package/dist/gates/runtime-gate.js +99 -0
  24. package/dist/gates/runtime-gate.js.map +1 -0
  25. package/dist/gates/tests-gate.d.ts +2 -0
  26. package/dist/gates/tests-gate.js +116 -0
  27. package/dist/gates/tests-gate.js.map +1 -0
  28. package/dist/gates/types-gate.d.ts +2 -0
  29. package/dist/gates/types-gate.js +59 -0
  30. package/dist/gates/types-gate.js.map +1 -0
  31. package/dist/gates/visual-gate.d.ts +6 -0
  32. package/dist/gates/visual-gate.js +118 -0
  33. package/dist/gates/visual-gate.js.map +1 -0
  34. package/dist/go/auto-chain.d.ts +107 -0
  35. package/dist/go/auto-chain.js +303 -0
  36. package/dist/go/auto-chain.js.map +1 -0
  37. package/dist/go/executor.d.ts +130 -0
  38. package/dist/go/executor.js +409 -0
  39. package/dist/go/executor.js.map +1 -0
  40. package/dist/go/finalize.d.ts +58 -0
  41. package/dist/go/finalize.js +200 -0
  42. package/dist/go/finalize.js.map +1 -0
  43. package/dist/go/linear-sync.d.ts +75 -0
  44. package/dist/go/linear-sync.js +239 -0
  45. package/dist/go/linear-sync.js.map +1 -0
  46. package/dist/go/verify-loop.d.ts +47 -0
  47. package/dist/go/verify-loop.js +172 -0
  48. package/dist/go/verify-loop.js.map +1 -0
  49. package/dist/hooks/pre-commit.d.ts +5 -0
  50. package/dist/hooks/pre-commit.js +69 -0
  51. package/dist/hooks/pre-commit.js.map +1 -0
  52. package/dist/linear/client.d.ts +108 -0
  53. package/dist/linear/client.js +388 -0
  54. package/dist/linear/client.js.map +1 -0
  55. package/dist/linear/issues.d.ts +20 -0
  56. package/dist/linear/issues.js +39 -0
  57. package/dist/linear/issues.js.map +1 -0
  58. package/dist/linear/milestones.d.ts +11 -0
  59. package/dist/linear/milestones.js +32 -0
  60. package/dist/linear/milestones.js.map +1 -0
  61. package/dist/linear/projects.d.ts +16 -0
  62. package/dist/linear/projects.js +50 -0
  63. package/dist/linear/projects.js.map +1 -0
  64. package/dist/reporter/human.d.ts +2 -0
  65. package/dist/reporter/human.js +63 -0
  66. package/dist/reporter/human.js.map +1 -0
  67. package/dist/reporter/json.d.ts +2 -0
  68. package/dist/reporter/json.js +4 -0
  69. package/dist/reporter/json.js.map +1 -0
  70. package/dist/server.d.ts +2 -0
  71. package/dist/server.js +109 -0
  72. package/dist/server.js.map +1 -0
  73. package/dist/spec/generator.d.ts +14 -0
  74. package/dist/spec/generator.js +206 -0
  75. package/dist/spec/generator.js.map +1 -0
  76. package/dist/spec/interview.d.ts +104 -0
  77. package/dist/spec/interview.js +342 -0
  78. package/dist/spec/interview.js.map +1 -0
  79. package/dist/spec/linear-sync.d.ts +48 -0
  80. package/dist/spec/linear-sync.js +125 -0
  81. package/dist/spec/linear-sync.js.map +1 -0
  82. package/dist/spec/scanner.d.ts +45 -0
  83. package/dist/spec/scanner.js +473 -0
  84. package/dist/spec/scanner.js.map +1 -0
  85. package/dist/spec/templates.d.ts +345 -0
  86. package/dist/spec/templates.js +86 -0
  87. package/dist/spec/templates.js.map +1 -0
  88. package/dist/state/reader.d.ts +29 -0
  89. package/dist/state/reader.js +116 -0
  90. package/dist/state/reader.js.map +1 -0
  91. package/dist/state/writer.d.ts +60 -0
  92. package/dist/state/writer.js +222 -0
  93. package/dist/state/writer.js.map +1 -0
  94. package/dist/types.d.ts +64 -0
  95. package/dist/types.js +2 -0
  96. package/dist/types.js.map +1 -0
  97. package/dist/utils/browser.d.ts +10 -0
  98. package/dist/utils/browser.js +89 -0
  99. package/dist/utils/browser.js.map +1 -0
  100. package/hooks/pre-commit-verify.js +103 -0
  101. package/package.json +68 -0
  102. package/skills/README.md +33 -0
  103. package/skills/forge-go.md +332 -0
  104. package/skills/forge-spec.md +251 -0
  105. package/skills/forge-triage.md +133 -0
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Typed wrapper around the Linear GraphQL API.
3
+ * Used by lifecycle modules (projects.ts, milestones.ts, issues.ts)
4
+ * and the execution engine for programmatic Linear management.
5
+ */
6
+ const LINEAR_API_URL = "https://api.linear.app/graphql";
7
+ const MAX_RETRIES = 3;
8
+ const INITIAL_BACKOFF_MS = 500;
9
+ const REQUEST_TIMEOUT_MS = 30_000; // 30 seconds per request
10
+ // ---------------------------------------------------------------------------
11
+ // Error
12
+ // ---------------------------------------------------------------------------
13
+ export class LinearClientError extends Error {
14
+ statusCode;
15
+ errors;
16
+ constructor(message, statusCode, errors) {
17
+ super(message);
18
+ this.statusCode = statusCode;
19
+ this.errors = errors;
20
+ this.name = "LinearClientError";
21
+ }
22
+ }
23
+ // ---------------------------------------------------------------------------
24
+ // Client
25
+ // ---------------------------------------------------------------------------
26
+ export class LinearClient {
27
+ apiKey;
28
+ constructor(apiKey) {
29
+ const key = apiKey ?? process.env.LINEAR_API_KEY;
30
+ if (!key) {
31
+ throw new LinearClientError("Linear API key not found. Set the LINEAR_API_KEY environment variable or pass it to the constructor.");
32
+ }
33
+ this.apiKey = key;
34
+ }
35
+ // -------------------------------------------------------------------------
36
+ // Projects
37
+ // -------------------------------------------------------------------------
38
+ async listProjects(opts) {
39
+ const filter = {};
40
+ if (opts?.query) {
41
+ filter.name = { containsIgnoreCase: opts.query };
42
+ }
43
+ if (opts?.state) {
44
+ filter.state = { eq: opts.state };
45
+ }
46
+ const hasFilter = Object.keys(filter).length > 0;
47
+ const filterVar = hasFilter ? ", $filter: ProjectFilter" : "";
48
+ const filterArg = hasFilter ? ", filter: $filter" : "";
49
+ const query = `
50
+ query ListProjects($after: String${filterVar}) {
51
+ projects(first: 50, after: $after${filterArg}) {
52
+ pageInfo { hasNextPage endCursor }
53
+ nodes {
54
+ id name description state url
55
+ }
56
+ }
57
+ }
58
+ `;
59
+ return this.paginate(query, "projects", {
60
+ ...(hasFilter ? { filter } : {}),
61
+ });
62
+ }
63
+ async createProject(input) {
64
+ const mutation = `
65
+ mutation CreateProject($input: ProjectCreateInput!) {
66
+ projectCreate(input: $input) {
67
+ success
68
+ project { id name description state url }
69
+ }
70
+ }
71
+ `;
72
+ const variables = {
73
+ input: {
74
+ name: input.name,
75
+ ...(input.description != null && { description: input.description }),
76
+ teamIds: input.teamIds,
77
+ ...(input.state != null && { state: input.state }),
78
+ },
79
+ };
80
+ const data = await this.request(mutation, variables);
81
+ const result = data.projectCreate;
82
+ if (!result?.success) {
83
+ throw new LinearClientError("Failed to create project");
84
+ }
85
+ return result.project;
86
+ }
87
+ async updateProject(id, input) {
88
+ const mutation = `
89
+ mutation UpdateProject($id: String!, $input: ProjectUpdateInput!) {
90
+ projectUpdate(id: $id, input: $input) {
91
+ success
92
+ project { id name description state url }
93
+ }
94
+ }
95
+ `;
96
+ const data = await this.request(mutation, { id, input });
97
+ const result = data.projectUpdate;
98
+ if (!result?.success) {
99
+ throw new LinearClientError(`Failed to update project ${id}`);
100
+ }
101
+ return result.project;
102
+ }
103
+ // -------------------------------------------------------------------------
104
+ // Milestones (Project Milestones)
105
+ // -------------------------------------------------------------------------
106
+ async listMilestones(projectId) {
107
+ const query = `
108
+ query ListMilestones($projectId: String!, $after: String) {
109
+ project(id: $projectId) {
110
+ projectMilestones(first: 50, after: $after) {
111
+ pageInfo { hasNextPage endCursor }
112
+ nodes {
113
+ id name description progress sortOrder
114
+ }
115
+ }
116
+ }
117
+ }
118
+ `;
119
+ // Custom pagination because the nodes are nested under project
120
+ const all = [];
121
+ let after = null;
122
+ for (;;) {
123
+ const data = await this.request(query, { projectId, after });
124
+ const connection = data.project?.projectMilestones;
125
+ if (!connection)
126
+ break;
127
+ all.push(...connection.nodes);
128
+ if (connection.pageInfo.hasNextPage) {
129
+ after = connection.pageInfo.endCursor;
130
+ }
131
+ else {
132
+ break;
133
+ }
134
+ }
135
+ return all;
136
+ }
137
+ async createMilestone(input) {
138
+ const mutation = `
139
+ mutation CreateMilestone($input: ProjectMilestoneCreateInput!) {
140
+ projectMilestoneCreate(input: $input) {
141
+ success
142
+ projectMilestone { id name description progress sortOrder }
143
+ }
144
+ }
145
+ `;
146
+ const variables = {
147
+ input: {
148
+ projectId: input.projectId,
149
+ name: input.name,
150
+ ...(input.description != null && { description: input.description }),
151
+ ...(input.targetDate != null && { targetDate: input.targetDate }),
152
+ },
153
+ };
154
+ const data = await this.request(mutation, variables);
155
+ const result = data.projectMilestoneCreate;
156
+ if (!result?.success) {
157
+ throw new LinearClientError("Failed to create milestone");
158
+ }
159
+ return result.projectMilestone;
160
+ }
161
+ // -------------------------------------------------------------------------
162
+ // Issues
163
+ // -------------------------------------------------------------------------
164
+ async listIssues(opts) {
165
+ const filter = {};
166
+ if (opts?.projectId) {
167
+ filter.project = { id: { eq: opts.projectId } };
168
+ }
169
+ if (opts?.milestoneId) {
170
+ filter.projectMilestone = { id: { eq: opts.milestoneId } };
171
+ }
172
+ if (opts?.state) {
173
+ filter.state = { name: { eqIgnoreCase: opts.state } };
174
+ }
175
+ const hasFilter = Object.keys(filter).length > 0;
176
+ const filterVar = hasFilter ? ", $filter: IssueFilter" : "";
177
+ const filterArg = hasFilter ? ", filter: $filter" : "";
178
+ const query = `
179
+ query ListIssues($after: String${filterVar}) {
180
+ issues(first: 50, after: $after${filterArg}) {
181
+ pageInfo { hasNextPage endCursor }
182
+ nodes {
183
+ id identifier title description url
184
+ state { name }
185
+ project { id }
186
+ projectMilestone { id }
187
+ }
188
+ }
189
+ }
190
+ `;
191
+ const raw = await this.paginate(query, "issues", hasFilter ? { filter } : {});
192
+ return raw.map((node) => this.mapIssue(node));
193
+ }
194
+ async createIssue(input) {
195
+ const mutation = `
196
+ mutation CreateIssue($input: IssueCreateInput!) {
197
+ issueCreate(input: $input) {
198
+ success
199
+ issue {
200
+ id identifier title description url
201
+ state { name }
202
+ project { id }
203
+ projectMilestone { id }
204
+ }
205
+ }
206
+ }
207
+ `;
208
+ const issueInput = {
209
+ title: input.title,
210
+ teamId: input.teamId,
211
+ ...(input.description != null && { description: input.description }),
212
+ ...(input.projectId != null && { projectId: input.projectId }),
213
+ ...(input.milestoneId != null && {
214
+ projectMilestoneId: input.milestoneId,
215
+ }),
216
+ ...(input.priority != null && { priority: input.priority }),
217
+ };
218
+ // state is a name string; Linear expects stateId. The caller should resolve
219
+ // this upstream, but if provided we pass it as-is and let Linear resolve.
220
+ if (input.state != null) {
221
+ issueInput.stateId = input.state;
222
+ }
223
+ const data = await this.request(mutation, { input: issueInput });
224
+ const result = data.issueCreate;
225
+ if (!result?.success) {
226
+ throw new LinearClientError("Failed to create issue");
227
+ }
228
+ return this.mapIssue(result.issue);
229
+ }
230
+ async updateIssue(id, input) {
231
+ const mutation = `
232
+ mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {
233
+ issueUpdate(id: $id, input: $input) {
234
+ success
235
+ issue {
236
+ id identifier title description url
237
+ state { name }
238
+ project { id }
239
+ projectMilestone { id }
240
+ }
241
+ }
242
+ }
243
+ `;
244
+ const issueInput = {};
245
+ if (input.title != null)
246
+ issueInput.title = input.title;
247
+ if (input.description != null)
248
+ issueInput.description = input.description;
249
+ if (input.state != null)
250
+ issueInput.stateId = input.state;
251
+ if (input.milestoneId != null)
252
+ issueInput.projectMilestoneId = input.milestoneId;
253
+ if (input.priority != null)
254
+ issueInput.priority = input.priority;
255
+ const data = await this.request(mutation, { id, input: issueInput });
256
+ const result = data.issueUpdate;
257
+ if (!result?.success) {
258
+ throw new LinearClientError(`Failed to update issue ${id}`);
259
+ }
260
+ return this.mapIssue(result.issue);
261
+ }
262
+ async createComment(issueId, body) {
263
+ const mutation = `
264
+ mutation CreateComment($input: CommentCreateInput!) {
265
+ commentCreate(input: $input) {
266
+ success
267
+ }
268
+ }
269
+ `;
270
+ const data = await this.request(mutation, {
271
+ input: { issueId, body },
272
+ });
273
+ if (!data.commentCreate?.success) {
274
+ throw new LinearClientError(`Failed to create comment on issue ${issueId}`);
275
+ }
276
+ }
277
+ // -------------------------------------------------------------------------
278
+ // Teams
279
+ // -------------------------------------------------------------------------
280
+ async listTeams() {
281
+ const query = `
282
+ query ListTeams($after: String) {
283
+ teams(first: 50, after: $after) {
284
+ pageInfo { hasNextPage endCursor }
285
+ nodes { id name key }
286
+ }
287
+ }
288
+ `;
289
+ return this.paginate(query, "teams", {});
290
+ }
291
+ // -------------------------------------------------------------------------
292
+ // Internals
293
+ // -------------------------------------------------------------------------
294
+ /**
295
+ * Execute a GraphQL request against the Linear API with retry + backoff.
296
+ */
297
+ async request(query, variables = {}) {
298
+ let lastError;
299
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
300
+ if (attempt > 0) {
301
+ const delay = INITIAL_BACKOFF_MS * 2 ** (attempt - 1);
302
+ await new Promise((resolve) => setTimeout(resolve, delay));
303
+ }
304
+ let res;
305
+ try {
306
+ const controller = new AbortController();
307
+ const timeoutId = globalThis.setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
308
+ try {
309
+ res = await fetch(LINEAR_API_URL, {
310
+ method: "POST",
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ Authorization: this.apiKey,
314
+ },
315
+ body: JSON.stringify({ query, variables }),
316
+ signal: controller.signal,
317
+ });
318
+ }
319
+ finally {
320
+ clearTimeout(timeoutId);
321
+ }
322
+ }
323
+ catch (err) {
324
+ if (err instanceof DOMException && err.name === "AbortError" || (err instanceof Error && err.name === "AbortError")) {
325
+ lastError = new LinearClientError(`Linear API request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`);
326
+ }
327
+ else {
328
+ lastError =
329
+ err instanceof Error ? err : new Error(String(err));
330
+ }
331
+ continue;
332
+ }
333
+ if (res.status === 429 || res.status >= 500) {
334
+ lastError = new LinearClientError(`Linear API returned ${res.status}`, res.status);
335
+ continue;
336
+ }
337
+ if (!res.ok) {
338
+ const body = await res.text().catch(() => "");
339
+ throw new LinearClientError(`Linear API error ${res.status}: ${body}`, res.status);
340
+ }
341
+ const json = (await res.json());
342
+ if (json.errors?.length) {
343
+ throw new LinearClientError(`GraphQL errors: ${json.errors.map((e) => e.message).join("; ")}`, undefined, json.errors);
344
+ }
345
+ if (!json.data) {
346
+ throw new LinearClientError("No data in Linear API response");
347
+ }
348
+ return json.data;
349
+ }
350
+ throw lastError ?? new LinearClientError("Request failed after retries");
351
+ }
352
+ /**
353
+ * Auto-paginate a connection query. The query MUST accept `$after: String`
354
+ * and the root field must return `{ pageInfo { hasNextPage endCursor } nodes { ... } }`.
355
+ */
356
+ async paginate(query, rootField, variables) {
357
+ const all = [];
358
+ let after = null;
359
+ for (;;) {
360
+ const data = await this.request(query, { ...variables, after });
361
+ const connection = data[rootField];
362
+ if (!connection)
363
+ break;
364
+ all.push(...connection.nodes);
365
+ if (connection.pageInfo.hasNextPage) {
366
+ after = connection.pageInfo.endCursor;
367
+ }
368
+ else {
369
+ break;
370
+ }
371
+ }
372
+ return all;
373
+ }
374
+ /** Normalize a raw issue node from GraphQL into our flat LinearIssue shape. */
375
+ mapIssue(node) {
376
+ return {
377
+ id: node.id,
378
+ identifier: node.identifier,
379
+ title: node.title,
380
+ description: node.description ?? undefined,
381
+ state: node.state?.name ?? "Unknown",
382
+ projectId: node.project?.id ?? undefined,
383
+ milestoneId: node.projectMilestone?.id ?? undefined,
384
+ url: node.url,
385
+ };
386
+ }
387
+ }
388
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/linear/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { LinearClient, type LinearIssue } from "./client.js";
2
+ /** Valid issue states */
3
+ export declare const ISSUE_STATES: readonly ["Backlog", "Todo", "In Progress", "In Review", "Done", "Canceled"];
4
+ export type IssueState = (typeof ISSUE_STATES)[number];
5
+ /** Create an issue under a project milestone */
6
+ export declare function createMilestoneIssue(client: LinearClient, input: {
7
+ title: string;
8
+ description?: string;
9
+ teamId: string;
10
+ projectId: string;
11
+ milestoneId: string;
12
+ priority?: number;
13
+ }): Promise<LinearIssue>;
14
+ /** Transition all issues in a milestone to a target state */
15
+ export declare function transitionMilestoneIssues(client: LinearClient, projectId: string, milestoneId: string, targetState: string): Promise<{
16
+ updated: number;
17
+ issues: LinearIssue[];
18
+ }>;
19
+ /** Add a progress comment to an issue */
20
+ export declare function addProgressComment(client: LinearClient, issueId: string, message: string): Promise<void>;
@@ -0,0 +1,39 @@
1
+ /** Valid issue states */
2
+ export const ISSUE_STATES = [
3
+ "Backlog",
4
+ "Todo",
5
+ "In Progress",
6
+ "In Review",
7
+ "Done",
8
+ "Canceled",
9
+ ];
10
+ /** Create an issue under a project milestone */
11
+ export async function createMilestoneIssue(client, input) {
12
+ return client.createIssue({
13
+ title: input.title,
14
+ description: input.description,
15
+ teamId: input.teamId,
16
+ projectId: input.projectId,
17
+ milestoneId: input.milestoneId,
18
+ priority: input.priority,
19
+ });
20
+ }
21
+ /** Transition all issues in a milestone to a target state */
22
+ export async function transitionMilestoneIssues(client, projectId, milestoneId, targetState) {
23
+ const issues = await client.listIssues({ projectId, milestoneId });
24
+ const updatedIssues = [];
25
+ for (const issue of issues) {
26
+ if (issue.state !== targetState) {
27
+ const updated = await client.updateIssue(issue.id, {
28
+ state: targetState,
29
+ });
30
+ updatedIssues.push(updated);
31
+ }
32
+ }
33
+ return { updated: updatedIssues.length, issues: updatedIssues };
34
+ }
35
+ /** Add a progress comment to an issue */
36
+ export async function addProgressComment(client, issueId, message) {
37
+ await client.createComment(issueId, message);
38
+ }
39
+ //# sourceMappingURL=issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issues.js","sourceRoot":"","sources":["../../src/linear/issues.ts"],"names":[],"mappings":"AAEA,yBAAyB;AACzB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,SAAS;IACT,MAAM;IACN,aAAa;IACb,WAAW;IACX,MAAM;IACN,UAAU;CACF,CAAC;AAGX,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,KAOC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAC;AACL,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAoB,EACpB,SAAiB,EACjB,WAAmB,EACnB,WAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;IAEnE,MAAM,aAAa,GAAkB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE;gBACjD,KAAK,EAAE,WAAW;aACnB,CAAC,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAClE,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAoB,EACpB,OAAe,EACf,OAAe;IAEf,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { LinearClient, type LinearMilestone } from "./client.js";
2
+ /** Create a milestone under a project */
3
+ export declare function createProjectMilestone(client: LinearClient, projectId: string, name: string, description?: string, targetDate?: string): Promise<LinearMilestone>;
4
+ /** Get milestone progress: total and completed issue counts */
5
+ export declare function getMilestoneProgress(client: LinearClient, projectId: string, milestoneName: string): Promise<{
6
+ milestone: LinearMilestone;
7
+ totalIssues: number;
8
+ completedIssues: number;
9
+ }>;
10
+ /** Find a milestone by name within a project */
11
+ export declare function findMilestoneByName(client: LinearClient, projectId: string, name: string): Promise<LinearMilestone | null>;
@@ -0,0 +1,32 @@
1
+ /** Create a milestone under a project */
2
+ export async function createProjectMilestone(client, projectId, name, description, targetDate) {
3
+ return client.createMilestone({
4
+ projectId,
5
+ name,
6
+ description,
7
+ targetDate,
8
+ });
9
+ }
10
+ /** Get milestone progress: total and completed issue counts */
11
+ export async function getMilestoneProgress(client, projectId, milestoneName) {
12
+ const milestone = await findMilestoneByName(client, projectId, milestoneName);
13
+ if (!milestone) {
14
+ throw new Error(`Milestone not found: "${milestoneName}" in project ${projectId}`);
15
+ }
16
+ const issues = await client.listIssues({
17
+ projectId,
18
+ milestoneId: milestone.id,
19
+ });
20
+ const completedIssues = issues.filter((i) => i.state === "Done" || i.state === "Canceled").length;
21
+ return {
22
+ milestone,
23
+ totalIssues: issues.length,
24
+ completedIssues,
25
+ };
26
+ }
27
+ /** Find a milestone by name within a project */
28
+ export async function findMilestoneByName(client, projectId, name) {
29
+ const milestones = await client.listMilestones(projectId);
30
+ return milestones.find((m) => m.name === name) ?? null;
31
+ }
32
+ //# sourceMappingURL=milestones.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestones.js","sourceRoot":"","sources":["../../src/linear/milestones.ts"],"names":[],"mappings":"AAEA,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAoB,EACpB,SAAiB,EACjB,IAAY,EACZ,WAAoB,EACpB,UAAmB;IAEnB,OAAO,MAAM,CAAC,eAAe,CAAC;QAC5B,SAAS;QACT,IAAI;QACJ,WAAW;QACX,UAAU;KACX,CAAC,CAAC;AACL,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAoB,EACpB,SAAiB,EACjB,aAAqB;IAMrB,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IAC9E,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,yBAAyB,aAAa,gBAAgB,SAAS,EAAE,CAClE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC;QACrC,SAAS;QACT,WAAW,EAAE,SAAS,CAAC,EAAE;KAC1B,CAAC,CAAC;IACH,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,CACpD,CAAC,MAAM,CAAC;IAET,OAAO;QACL,SAAS;QACT,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAoB,EACpB,SAAiB,EACjB,IAAY;IAEZ,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACzD,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { LinearClient, type LinearProject } from "./client.js";
2
+ /** Valid project states in forward-only order */
3
+ export declare const PROJECT_STATES: readonly ["Backlog", "Planned", "In Progress", "In Review", "Done"];
4
+ export type ProjectState = (typeof PROJECT_STATES)[number];
5
+ /**
6
+ * Validate that a project state transition is forward-only.
7
+ * Backlog -> Planned -> In Progress -> In Review -> Done.
8
+ * Same-state transitions are not valid (no-op).
9
+ */
10
+ export declare function isValidTransition(from: string, to: string): boolean;
11
+ /** Create a new project in Backlog state (used during triage) */
12
+ export declare function createTriageProject(client: LinearClient, name: string, description: string, teamIds: string[]): Promise<LinearProject>;
13
+ /** Transition a project to a target state with forward-only validation */
14
+ export declare function transitionProject(client: LinearClient, projectId: string, targetState: string): Promise<LinearProject>;
15
+ /** Find an existing project by exact name (for dedup during triage) */
16
+ export declare function findProjectByName(client: LinearClient, name: string): Promise<LinearProject | null>;
@@ -0,0 +1,50 @@
1
+ /** Valid project states in forward-only order */
2
+ export const PROJECT_STATES = [
3
+ "Backlog",
4
+ "Planned",
5
+ "In Progress",
6
+ "In Review",
7
+ "Done",
8
+ ];
9
+ /** State index map for transition validation */
10
+ const stateIndex = new Map(PROJECT_STATES.map((s, i) => [s, i]));
11
+ /**
12
+ * Validate that a project state transition is forward-only.
13
+ * Backlog -> Planned -> In Progress -> In Review -> Done.
14
+ * Same-state transitions are not valid (no-op).
15
+ */
16
+ export function isValidTransition(from, to) {
17
+ const fromIdx = stateIndex.get(from);
18
+ const toIdx = stateIndex.get(to);
19
+ if (fromIdx === undefined || toIdx === undefined)
20
+ return false;
21
+ return toIdx > fromIdx;
22
+ }
23
+ /** Create a new project in Backlog state (used during triage) */
24
+ export async function createTriageProject(client, name, description, teamIds) {
25
+ return client.createProject({
26
+ name,
27
+ description,
28
+ teamIds,
29
+ state: "Backlog",
30
+ });
31
+ }
32
+ /** Transition a project to a target state with forward-only validation */
33
+ export async function transitionProject(client, projectId, targetState) {
34
+ // Fetch current project to validate transition
35
+ const projects = await client.listProjects();
36
+ const project = projects.find((p) => p.id === projectId);
37
+ if (!project) {
38
+ throw new Error(`Project not found: ${projectId}`);
39
+ }
40
+ if (!isValidTransition(project.state, targetState)) {
41
+ throw new Error(`Invalid project transition: ${project.state} -> ${targetState}`);
42
+ }
43
+ return client.updateProject(projectId, { state: targetState });
44
+ }
45
+ /** Find an existing project by exact name (for dedup during triage) */
46
+ export async function findProjectByName(client, name) {
47
+ const projects = await client.listProjects({ query: name });
48
+ return projects.find((p) => p.name === name) ?? null;
49
+ }
50
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/linear/projects.ts"],"names":[],"mappings":"AAEA,iDAAiD;AACjD,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,SAAS;IACT,SAAS;IACT,aAAa;IACb,WAAW;IACX,MAAM;CACE,CAAC;AAGX,gDAAgD;AAChD,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CACrC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,EAAU;IACxD,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,OAAO,KAAK,GAAG,OAAO,CAAC;AACzB,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAoB,EACpB,IAAY,EACZ,WAAmB,EACnB,OAAiB;IAEjB,OAAO,MAAM,CAAC,aAAa,CAAC;QAC1B,IAAI;QACJ,WAAW;QACX,OAAO;QACP,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAoB,EACpB,SAAiB,EACjB,WAAmB;IAEnB,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;IACzD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,KAAK,OAAO,WAAW,EAAE,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAoB,EACpB,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACvD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { PipelineResult } from "../types.js";
2
+ export declare function formatHumanReport(result: PipelineResult): string;
@@ -0,0 +1,63 @@
1
+ export function formatHumanReport(result) {
2
+ const lines = [];
3
+ // Header
4
+ const status = result.passed ? "PASSED" : "FAILED";
5
+ lines.push("## Verification Report");
6
+ lines.push(`**Status:** ${status}`);
7
+ lines.push(`**Iterations:** ${result.iteration}/${result.maxIterations}`);
8
+ const totalMs = result.gates.reduce((sum, g) => sum + g.duration_ms, 0);
9
+ lines.push(`**Duration:** ${formatDuration(totalMs)}`);
10
+ lines.push("");
11
+ // Gate results
12
+ lines.push("### Gates");
13
+ for (const gate of result.gates) {
14
+ const icon = gate.passed ? "[x]" : "[ ]";
15
+ const statusText = gate.passed ? "PASS" : "FAIL";
16
+ const dur = formatDuration(gate.duration_ms);
17
+ let suffix = "";
18
+ if (!gate.passed && gate.errors.length > 0) {
19
+ suffix = ` — ${gate.errors.length} error${gate.errors.length === 1 ? "" : "s"}`;
20
+ }
21
+ else if (gate.passed && gate.warnings.length > 0) {
22
+ suffix = ` — ${gate.warnings.length} warning${gate.warnings.length === 1 ? "" : "s"}`;
23
+ }
24
+ lines.push(`- ${icon} ${gate.gate}: ${statusText} (${dur})${suffix}`);
25
+ }
26
+ lines.push("");
27
+ // Errors section
28
+ const gatesWithErrors = result.gates.filter((g) => g.errors.length > 0);
29
+ if (gatesWithErrors.length > 0) {
30
+ lines.push("### Errors");
31
+ for (const gate of gatesWithErrors) {
32
+ lines.push(`#### ${gate.gate}`);
33
+ for (const err of gate.errors) {
34
+ const loc = err.file
35
+ ? `${err.file}${err.line ? `:${err.line}` : ""}`
36
+ : "";
37
+ const prefix = loc ? `${loc}: ` : "";
38
+ lines.push(`- ${prefix}${err.message}`);
39
+ if (err.remediation) {
40
+ lines.push(` > Fix: ${err.remediation}`);
41
+ }
42
+ }
43
+ lines.push("");
44
+ }
45
+ }
46
+ // Warnings section
47
+ const gatesWithWarnings = result.gates.filter((g) => g.warnings.length > 0);
48
+ if (gatesWithWarnings.length > 0) {
49
+ lines.push("### Warnings");
50
+ for (const gate of gatesWithWarnings) {
51
+ lines.push(`#### ${gate.gate}`);
52
+ for (const warning of gate.warnings) {
53
+ lines.push(`- ${warning}`);
54
+ }
55
+ lines.push("");
56
+ }
57
+ }
58
+ return lines.join("\n");
59
+ }
60
+ function formatDuration(ms) {
61
+ return `${(ms / 1000).toFixed(1)}s`;
62
+ }
63
+ //# sourceMappingURL=human.js.map