forge-cc 0.1.41 → 1.0.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 (62) hide show
  1. package/README.md +454 -338
  2. package/dist/cli.js +194 -935
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/loader.d.ts +1 -1
  5. package/dist/config/loader.js +49 -56
  6. package/dist/config/loader.js.map +1 -1
  7. package/dist/config/schema.d.ts +37 -125
  8. package/dist/config/schema.js +13 -28
  9. package/dist/config/schema.js.map +1 -1
  10. package/dist/doctor.d.ts +10 -0
  11. package/dist/doctor.js +148 -0
  12. package/dist/doctor.js.map +1 -0
  13. package/dist/gates/index.d.ts +14 -12
  14. package/dist/gates/index.js +53 -105
  15. package/dist/gates/index.js.map +1 -1
  16. package/dist/gates/lint-gate.d.ts +2 -2
  17. package/dist/gates/lint-gate.js +60 -66
  18. package/dist/gates/lint-gate.js.map +1 -1
  19. package/dist/gates/tests-gate.d.ts +2 -4
  20. package/dist/gates/tests-gate.js +75 -203
  21. package/dist/gates/tests-gate.js.map +1 -1
  22. package/dist/gates/types-gate.d.ts +2 -2
  23. package/dist/gates/types-gate.js +53 -59
  24. package/dist/gates/types-gate.js.map +1 -1
  25. package/dist/linear/client.d.ts +31 -108
  26. package/dist/linear/client.js +88 -388
  27. package/dist/linear/client.js.map +1 -1
  28. package/dist/linear/sync.d.ts +15 -0
  29. package/dist/linear/sync.js +102 -0
  30. package/dist/linear/sync.js.map +1 -0
  31. package/dist/runner/loop.d.ts +4 -0
  32. package/dist/runner/loop.js +168 -0
  33. package/dist/runner/loop.js.map +1 -0
  34. package/dist/runner/prompt.d.ts +14 -0
  35. package/dist/runner/prompt.js +59 -0
  36. package/dist/runner/prompt.js.map +1 -0
  37. package/dist/runner/update.d.ts +1 -0
  38. package/dist/runner/update.js +72 -0
  39. package/dist/runner/update.js.map +1 -0
  40. package/dist/server.d.ts +6 -2
  41. package/dist/server.js +43 -101
  42. package/dist/server.js.map +1 -1
  43. package/dist/setup.d.ts +5 -0
  44. package/dist/setup.js +208 -0
  45. package/dist/setup.js.map +1 -0
  46. package/dist/state/cache.d.ts +3 -0
  47. package/dist/state/cache.js +23 -0
  48. package/dist/state/cache.js.map +1 -0
  49. package/dist/state/status.d.ts +66 -0
  50. package/dist/state/status.js +96 -0
  51. package/dist/state/status.js.map +1 -0
  52. package/dist/types.d.ts +46 -114
  53. package/dist/worktree/manager.d.ts +6 -103
  54. package/dist/worktree/manager.js +25 -296
  55. package/dist/worktree/manager.js.map +1 -1
  56. package/hooks/pre-commit-verify.js +109 -109
  57. package/package.json +3 -2
  58. package/skills/forge-go.md +20 -13
  59. package/skills/forge-setup.md +149 -388
  60. package/skills/forge-spec.md +367 -342
  61. package/skills/forge-triage.md +179 -133
  62. package/skills/forge-update.md +87 -93
@@ -1,219 +1,91 @@
1
- import { execSync } from "node:child_process";
2
- import { buildTestRemediation, buildTestCoverageRemediation } from "./remediation.js";
3
- import { analyzeTestCoverage } from "./test-analysis.js";
4
- import { loadConfig } from "../config/loader.js";
5
- /**
6
- * Common test failure patterns with file/line info:
7
- * FAIL src/foo.test.ts > suite > test name
8
- * at src/foo.test.ts:42:10
9
- */
10
- const TEST_FILE_RE = /^FAIL\s+(.+?)(?:\s+>|$)/;
11
- const STACKTRACE_RE = /at\s+.*?([^\s(]+):(\d+):\d+/;
12
- export async function verifyTests(projectDir, options) {
13
- const start = Date.now();
14
- const errors = [];
15
- const warnings = [];
16
- // Load config and run test analysis
17
- const config = loadConfig(options?.configRoot ?? projectDir);
18
- const testingConfig = config.testing;
19
- const analysis = await analyzeTestCoverage(projectDir);
20
- // Detect whether a test script exists in package.json
21
- let hasTestScript = false;
1
+ import { spawn } from "node:child_process";
2
+ import { readFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ /** Detect test runner from package.json dependencies. */
5
+ async function detectRunner(projectDir) {
22
6
  try {
23
- const pkgRaw = execSync("node -e \"process.stdout.write(JSON.stringify(require('./package.json')))\"", {
24
- cwd: projectDir,
25
- stdio: "pipe",
26
- timeout: 10_000,
27
- });
28
- const pkg = JSON.parse(String(pkgRaw));
29
- hasTestScript = !!pkg.scripts?.test;
7
+ const content = await readFile(join(projectDir, "package.json"), "utf-8");
8
+ const pkg = JSON.parse(content);
9
+ const deps = {
10
+ ...pkg.dependencies,
11
+ ...pkg.devDependencies,
12
+ };
13
+ if ("vitest" in deps)
14
+ return "vitest";
15
+ if ("jest" in deps)
16
+ return "jest";
30
17
  }
31
18
  catch {
32
- // No package.json or invalid hasTestScript stays false
33
- }
34
- // -----------------------------------------------------------------------
35
- // Baseline check: If zero test files AND no test script, FAIL immediately
36
- // -----------------------------------------------------------------------
37
- if (analysis.coverage.testFiles === 0 && !hasTestScript) {
38
- const categoryNames = analysis.categories.map(c => c.name).join(", ");
39
- const msg = `No tests found. ${analysis.coverage.sourceFiles} source file${analysis.coverage.sourceFiles === 1 ? "" : "s"} across ${analysis.categories.length} categor${analysis.categories.length === 1 ? "y" : "ies"} (${categoryNames || "none"}) have no test coverage. Run \`/forge:setup\` to scaffold tests.`;
40
- const error = { message: msg };
41
- error.remediation = buildTestCoverageRemediation(error);
42
- errors.push(error);
43
- return {
44
- gate: "tests",
45
- passed: false,
46
- errors,
47
- warnings,
48
- duration_ms: Date.now() - start,
49
- };
19
+ // No package.json — fall through
50
20
  }
51
- // Also baseline-fail if test files exist but no test script to run them
52
- if (analysis.coverage.testFiles === 0 && hasTestScript) {
53
- // Test script exists but no test files found — still baseline fail
54
- const msg = `No test files found. A test script exists in package.json but no test files were detected. Run \`/forge:setup\` to scaffold tests.`;
55
- const error = { message: msg };
56
- error.remediation = buildTestCoverageRemediation(error);
57
- errors.push(error);
21
+ return "unknown";
22
+ }
23
+ async function runTestRunner(projectDir) {
24
+ const start = Date.now();
25
+ const runner = await detectRunner(projectDir);
26
+ if (runner === "unknown") {
58
27
  return {
59
28
  gate: "tests",
60
29
  passed: false,
61
- errors,
62
- warnings,
63
- duration_ms: Date.now() - start,
30
+ errors: [{ file: "", line: 0, message: "No test runner detected (install vitest or jest)" }],
31
+ durationMs: Date.now() - start,
64
32
  };
65
33
  }
66
- // -----------------------------------------------------------------------
67
- // Run tests (if a test script exists)
68
- // -----------------------------------------------------------------------
69
- let testsRanSuccessfully = true;
70
- if (hasTestScript) {
71
- try {
72
- const result = execSync("npm run test -- --run", {
73
- cwd: projectDir,
74
- stdio: "pipe",
75
- timeout: 300_000,
76
- });
77
- const output = String(result);
78
- // Parse test summary from Vitest output
79
- const summaryMatch = output.match(/Tests\s+(\d+)\s+passed(?:\s*\|\s*(\d+)\s+failed)?/);
80
- if (summaryMatch) {
81
- const passed = summaryMatch[1];
82
- const failed = summaryMatch[2] ?? "0";
83
- warnings.push(`${passed} passed, ${failed} failed`);
84
- }
85
- }
86
- catch (err) {
87
- testsRanSuccessfully = false;
88
- const stdout = err instanceof Error && "stdout" in err
89
- ? String(err.stdout)
90
- : "";
91
- const stderr = err instanceof Error && "stderr" in err
92
- ? String(err.stderr)
93
- : "";
94
- const output = `${stdout}\n${stderr}`;
95
- let lastFailFile;
96
- for (const line of output.split("\n")) {
97
- const trimmed = line.trim();
98
- if (!trimmed)
99
- continue;
100
- // Track which test file we're in
101
- const failMatch = TEST_FILE_RE.exec(trimmed);
102
- if (failMatch) {
103
- lastFailFile = failMatch[1];
104
- }
105
- // Try to extract stack trace location
106
- const stackMatch = STACKTRACE_RE.exec(trimmed);
107
- if (stackMatch) {
108
- lastFailFile = stackMatch[1];
109
- }
110
- if (trimmed.includes("FAIL") ||
111
- trimmed.includes("AssertionError") ||
112
- trimmed.includes("AssertionError") ||
113
- trimmed.includes("Expected") ||
114
- trimmed.includes("Received")) {
34
+ const cmd = runner === "vitest" ? ["vitest", "run"] : ["jest", "--ci"];
35
+ return new Promise((resolve) => {
36
+ const child = spawn("npx", cmd, {
37
+ cwd: projectDir,
38
+ shell: true,
39
+ stdio: ["ignore", "pipe", "pipe"],
40
+ });
41
+ let stdout = "";
42
+ let stderr = "";
43
+ child.stdout.on("data", (data) => {
44
+ stdout += data.toString();
45
+ });
46
+ child.stderr.on("data", (data) => {
47
+ stderr += data.toString();
48
+ });
49
+ child.on("close", (code) => {
50
+ const errors = [];
51
+ if (code !== 0) {
52
+ const output = stdout + stderr;
53
+ const failRegex = /FAIL\s+(.+)/g;
54
+ let match;
55
+ while ((match = failRegex.exec(output)) !== null) {
115
56
  errors.push({
116
- file: lastFailFile,
117
- line: stackMatch ? Number.parseInt(stackMatch[2], 10) : undefined,
118
- message: trimmed,
57
+ file: match[1].trim(),
58
+ line: 0,
59
+ message: `Test suite failed: ${match[1].trim()}`,
119
60
  });
120
61
  }
121
- }
122
- // Also try to extract the summary even on failure
123
- const summaryMatch = output.match(/Tests\s+(?:(\d+)\s+passed\s*\|\s*)?(\d+)\s+failed/);
124
- if (summaryMatch) {
125
- const passed = summaryMatch[1] ?? "0";
126
- const failed = summaryMatch[2];
127
- warnings.push(`${passed} passed, ${failed} failed`);
128
- }
129
- if (errors.length === 0) {
130
- errors.push({ message: "Test runner exited with non-zero status" });
131
- }
132
- // Enrich errors with remediation hints
133
- for (const error of errors) {
134
- error.remediation = buildTestRemediation(error);
135
- }
136
- }
137
- }
138
- // -----------------------------------------------------------------------
139
- // Enforcement check: Verify changed files have corresponding tests
140
- // -----------------------------------------------------------------------
141
- if (testingConfig?.enforce) {
142
- const changedSourceFiles = getChangedSourceFiles(projectDir);
143
- if (changedSourceFiles.length > 0) {
144
- const untestedSet = new Set(analysis.coverage.untestedFiles);
145
- for (const file of changedSourceFiles) {
146
- const normalized = file.replace(/\\/g, "/");
147
- if (untestedSet.has(normalized)) {
148
- const error = {
149
- file: normalized,
150
- message: `Missing test file for changed source: ${normalized}`,
151
- };
152
- error.remediation = buildTestCoverageRemediation(error);
153
- errors.push(error);
154
- }
155
- }
156
- }
157
- }
158
- // -----------------------------------------------------------------------
159
- // Thin coverage advisory
160
- // -----------------------------------------------------------------------
161
- if (analysis.coverage.testFiles > 0 && analysis.coverage.ratio < 0.3) {
162
- warnings.push(`Thin test coverage: ratio ${analysis.coverage.ratio} (${analysis.coverage.testFiles} test file${analysis.coverage.testFiles === 1 ? "" : "s"} for ${analysis.coverage.sourceFiles} source file${analysis.coverage.sourceFiles === 1 ? "" : "s"}). Consider adding tests for untested files.`);
163
- }
164
- const passed = testsRanSuccessfully && errors.length === 0;
165
- return {
166
- gate: "tests",
167
- passed,
168
- errors,
169
- warnings,
170
- duration_ms: Date.now() - start,
171
- };
172
- }
173
- /**
174
- * Get source files changed relative to HEAD~1 or the staging area.
175
- * Returns paths relative to projectDir, normalized with forward slashes.
176
- */
177
- function getChangedSourceFiles(projectDir) {
178
- const files = [];
179
- // Try git diff against HEAD~1 first, fall back to cached diff
180
- for (const cmd of [
181
- "git diff --name-only HEAD~1",
182
- "git diff --cached --name-only",
183
- ]) {
184
- try {
185
- const output = execSync(cmd, {
186
- cwd: projectDir,
187
- stdio: "pipe",
188
- timeout: 10_000,
189
- }).toString().trim();
190
- if (output) {
191
- for (const line of output.split("\n")) {
192
- const trimmed = line.trim();
193
- if (!trimmed)
194
- continue;
195
- // Only include source files (not test files, not configs)
196
- if (isSourceFilePath(trimmed)) {
197
- files.push(trimmed.replace(/\\/g, "/"));
198
- }
62
+ if (errors.length === 0) {
63
+ errors.push({
64
+ file: "",
65
+ line: 0,
66
+ message: `Test runner exited with code ${code}`,
67
+ });
199
68
  }
200
- break; // Use the first successful command
201
69
  }
202
- }
203
- catch {
204
- // Command failed — try the next one
205
- }
206
- }
207
- return files;
208
- }
209
- /** Check if a path looks like a source file (TS/JS, not a test, not a declaration). */
210
- function isSourceFilePath(filePath) {
211
- if (!/\.(ts|tsx|js|jsx)$/.test(filePath))
212
- return false;
213
- if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filePath))
214
- return false;
215
- if (filePath.endsWith(".d.ts"))
216
- return false;
217
- return true;
70
+ resolve({
71
+ gate: "tests",
72
+ passed: code === 0,
73
+ errors,
74
+ durationMs: Date.now() - start,
75
+ });
76
+ });
77
+ child.on("error", (err) => {
78
+ resolve({
79
+ gate: "tests",
80
+ passed: false,
81
+ errors: [{ file: "", line: 0, message: err.message }],
82
+ durationMs: Date.now() - start,
83
+ });
84
+ });
85
+ });
218
86
  }
87
+ export const testsGate = {
88
+ name: "tests",
89
+ run: runTestRunner,
90
+ };
219
91
  //# sourceMappingURL=tests-gate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tests-gate.js","sourceRoot":"","sources":["../../src/gates/tests-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tests-gate.js","sourceRoot":"","sources":["../../src/gates/tests-gate.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import type { GateResult } from "../types.js";
2
- export declare function verifyTypes(projectDir: string): Promise<GateResult>;
1
+ import type { Gate } from "./index.js";
2
+ export declare const typesGate: Gate;
@@ -1,64 +1,58 @@
1
- import { execSync } from "node:child_process";
2
- import { buildTypeRemediation } from "./remediation.js";
3
- /** Regex to parse tsc error lines: src/foo.ts(10,5): error TS2322: ... */
4
- const TSC_ERROR_RE = /^(.+?)\((\d+),\d+\):\s*(.+)$/;
5
- export async function verifyTypes(projectDir) {
6
- const start = Date.now();
1
+ import { spawn } from "node:child_process";
2
+ /** Parse tsc output lines into structured errors. */
3
+ function parseTscOutput(output) {
7
4
  const errors = [];
8
- const warnings = [];
9
- try {
10
- execSync("npx tsc --noEmit", {
11
- cwd: projectDir,
12
- stdio: "pipe",
13
- timeout: 120_000,
5
+ // tsc error format: file(line,col): error TSxxxx: message
6
+ const regex = /^(.+?)\((\d+),(\d+)\):\s+error\s+TS\d+:\s+(.+)$/gm;
7
+ let match;
8
+ while ((match = regex.exec(output)) !== null) {
9
+ errors.push({
10
+ file: match[1],
11
+ line: parseInt(match[2], 10),
12
+ column: parseInt(match[3], 10),
13
+ message: match[4],
14
14
  });
15
- return {
16
- gate: "types",
17
- passed: true,
18
- errors,
19
- warnings,
20
- duration_ms: Date.now() - start,
21
- };
22
- }
23
- catch (err) {
24
- const output = err instanceof Error && "stdout" in err
25
- ? String(err.stdout)
26
- : "";
27
- for (const line of output.split("\n")) {
28
- const trimmed = line.trim();
29
- if (!trimmed)
30
- continue;
31
- if (trimmed.toLowerCase().includes("warning")) {
32
- warnings.push(trimmed);
33
- }
34
- else if (trimmed.includes("error TS")) {
35
- const match = TSC_ERROR_RE.exec(trimmed);
36
- if (match) {
37
- errors.push({
38
- file: match[1],
39
- line: Number.parseInt(match[2], 10),
40
- message: match[3],
41
- });
42
- }
43
- else {
44
- errors.push({ message: trimmed });
45
- }
46
- }
47
- }
48
- if (errors.length === 0) {
49
- errors.push({ message: "tsc exited with non-zero status but no TS errors were parsed" });
50
- }
51
- // Enrich errors with remediation hints
52
- for (const error of errors) {
53
- error.remediation = buildTypeRemediation(error);
54
- }
55
- return {
56
- gate: "types",
57
- passed: false,
58
- errors,
59
- warnings,
60
- duration_ms: Date.now() - start,
61
- };
62
15
  }
16
+ return errors;
17
+ }
18
+ function runTsc(projectDir) {
19
+ return new Promise((resolve) => {
20
+ const start = Date.now();
21
+ const child = spawn("npx", ["tsc", "--noEmit"], {
22
+ cwd: projectDir,
23
+ shell: true,
24
+ stdio: ["ignore", "pipe", "pipe"],
25
+ });
26
+ let stdout = "";
27
+ let stderr = "";
28
+ child.stdout.on("data", (data) => {
29
+ stdout += data.toString();
30
+ });
31
+ child.stderr.on("data", (data) => {
32
+ stderr += data.toString();
33
+ });
34
+ child.on("close", (code) => {
35
+ const output = stdout + stderr;
36
+ const errors = parseTscOutput(output);
37
+ resolve({
38
+ gate: "types",
39
+ passed: code === 0,
40
+ errors,
41
+ durationMs: Date.now() - start,
42
+ });
43
+ });
44
+ child.on("error", (err) => {
45
+ resolve({
46
+ gate: "types",
47
+ passed: false,
48
+ errors: [{ file: "", line: 0, message: err.message }],
49
+ durationMs: Date.now() - start,
50
+ });
51
+ });
52
+ });
63
53
  }
54
+ export const typesGate = {
55
+ name: "types",
56
+ run: runTsc,
57
+ };
64
58
  //# sourceMappingURL=types-gate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-gate.js","sourceRoot":"","sources":["../../src/gates/types-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,0EAA0E;AAC1E,MAAM,YAAY,GAAG,8BAA8B,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,CAAC;QACH,QAAQ,CAAC,kBAAkB,EAAE;YAC3B,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,OAAO;YACL,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,IAAI;YACZ,MAAM;YACN,QAAQ;YACR,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;SAChC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,MAAM,GACV,GAAG,YAAY,KAAK,IAAI,QAAQ,IAAI,GAAG;YACrC,CAAC,CAAC,MAAM,CAAE,GAA0B,CAAC,MAAM,CAAC;YAC5C,CAAC,CAAC,EAAE,CAAC;QAET,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;iBAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBACxC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACzC,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;wBACd,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;wBACnC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;qBAClB,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,8DAA8D,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,CAAC,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC;QAED,OAAO;YACL,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,KAAK;YACb,MAAM;YACN,QAAQ;YACR,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;SAChC,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"types-gate.js","sourceRoot":"","sources":["../../src/gates/types-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAI3C,qDAAqD;AACrD,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,0DAA0D;IAC1D,MAAM,KAAK,GAAG,mDAAmD,CAAC;IAClE,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACd,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAC9B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;SAClB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,UAAkB;IAChC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE;YAC9C,GAAG,EAAE,UAAU;YACf,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACvC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;YACvC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAC/B,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;YACtC,OAAO,CAAC;gBACN,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,IAAI,KAAK,CAAC;gBAClB,MAAM;gBACN,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,OAAO,CAAC;gBACN,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,KAAK;gBACb,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;gBACrD,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;aAC/B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,IAAI,EAAE,OAAO;IACb,GAAG,EAAE,MAAM;CACZ,CAAC"}
@@ -1,114 +1,37 @@
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
- export interface LinearProject {
7
- id: string;
8
- name: string;
9
- description?: string;
10
- state: string;
11
- url: string;
12
- }
13
- export interface LinearMilestone {
14
- id: string;
15
- name: string;
16
- description?: string;
17
- progress: number;
18
- sortOrder: number;
19
- }
20
- export interface LinearIssue {
21
- id: string;
22
- identifier: string;
23
- title: string;
24
- description?: string;
25
- state: string;
26
- projectId?: string;
27
- milestoneId?: string;
1
+ export interface ForgeLinearClientOptions {
2
+ apiKey: string;
28
3
  teamId?: string;
29
- url: string;
30
- }
31
- export interface LinearTeam {
32
- id: string;
33
- name: string;
34
- key: string;
35
- }
36
- export interface CreateProjectInput {
37
- name: string;
38
- description?: string;
39
- teamIds: string[];
40
- state?: string;
41
- }
42
- export interface UpdateProjectInput {
43
- name?: string;
44
- description?: string;
45
- state?: string;
46
- }
47
- export interface CreateMilestoneInput {
48
- projectId: string;
49
- name: string;
50
- description?: string;
51
- targetDate?: string;
52
- }
53
- export interface CreateIssueInput {
54
- title: string;
55
- description?: string;
56
- teamId: string;
57
- projectId?: string;
58
- milestoneId?: string;
59
- priority?: number;
60
- state?: string;
61
4
  }
62
- export interface UpdateIssueInput {
63
- title?: string;
64
- description?: string;
65
- state?: string;
66
- stateId?: string;
67
- milestoneId?: string;
68
- priority?: number;
69
- }
70
- export declare class LinearClientError extends Error {
71
- readonly statusCode?: number | undefined;
72
- readonly errors?: Array<{
73
- message: string;
74
- }> | undefined;
75
- constructor(message: string, statusCode?: number | undefined, errors?: Array<{
76
- message: string;
77
- }> | undefined);
78
- }
79
- export declare class LinearClient {
80
- private readonly apiKey;
81
- constructor(apiKey?: string);
82
- listProjects(opts?: {
83
- query?: string;
84
- state?: string;
85
- }): Promise<LinearProject[]>;
86
- createProject(input: CreateProjectInput): Promise<LinearProject>;
87
- updateProject(id: string, input: UpdateProjectInput): Promise<LinearProject>;
88
- listMilestones(projectId: string): Promise<LinearMilestone[]>;
89
- createMilestone(input: CreateMilestoneInput): Promise<LinearMilestone>;
90
- listIssues(opts?: {
91
- projectId?: string;
92
- milestoneId?: string;
93
- state?: string;
94
- }): Promise<LinearIssue[]>;
95
- createIssue(input: CreateIssueInput): Promise<LinearIssue>;
96
- updateIssue(id: string, input: UpdateIssueInput): Promise<LinearIssue>;
97
- createComment(issueId: string, body: string): Promise<void>;
98
- listTeams(): Promise<LinearTeam[]>;
99
- listWorkflowStates(teamId: string): Promise<Array<{
5
+ /**
6
+ * Thin wrapper around @linear/sdk's LinearClient, scoped to a team.
7
+ * All public methods degrade gracefully on API errors (warn, don't crash).
8
+ */
9
+ export declare class ForgeLinearClient {
10
+ private readonly client;
11
+ private readonly teamId;
12
+ constructor(opts: ForgeLinearClientOptions);
13
+ /** Resolve a workflow state UUID by its display name for a given team. */
14
+ resolveStateId(teamId: string, stateName: string): Promise<string>;
15
+ /** Update an issue's workflow state. */
16
+ updateIssueState(issueId: string, stateId: string): Promise<void>;
17
+ /** Update a project's status (state name in Linear projects). */
18
+ updateProjectState(projectId: string, stateId: string): Promise<void>;
19
+ /** List all teams visible to the authenticated user. */
20
+ listTeams(): Promise<Array<{
21
+ id: string;
22
+ name: string;
23
+ key: string;
24
+ }>>;
25
+ /** List issues belonging to a project. Returns identifier (e.g. "MSIG-123") and title. */
26
+ listIssuesByProject(projectId: string): Promise<Array<{
27
+ id: string;
28
+ identifier: string;
29
+ title: string;
30
+ }>>;
31
+ /** List projects filtered by team. */
32
+ listProjects(teamId: string): Promise<Array<{
100
33
  id: string;
101
34
  name: string;
35
+ state: string;
102
36
  }>>;
103
- /**
104
- * Execute a GraphQL request against the Linear API with retry + backoff.
105
- */
106
- private request;
107
- /**
108
- * Auto-paginate a connection query. The query MUST accept `$after: String`
109
- * and the root field must return `{ pageInfo { hasNextPage endCursor } nodes { ... } }`.
110
- */
111
- private paginate;
112
- /** Normalize a raw issue node from GraphQL into our flat LinearIssue shape. */
113
- private mapIssue;
114
37
  }