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,106 @@
1
+ import { closeBrowser } from "../utils/browser.js";
2
+ import { verifyTypes } from "./types-gate.js";
3
+ import { verifyLint } from "./lint-gate.js";
4
+ import { verifyTests } from "./tests-gate.js";
5
+ import { verifyVisual } from "./visual-gate.js";
6
+ import { verifyRuntime } from "./runtime-gate.js";
7
+ import { verifyPrd } from "./prd-gate.js";
8
+ /** Gate registry -- maps gate name to its function */
9
+ export const gateRegistry = {
10
+ types: (input) => verifyTypes(input.projectDir),
11
+ lint: (input) => verifyLint(input.projectDir),
12
+ tests: (input) => verifyTests(input.projectDir),
13
+ visual: (input) => verifyVisual(input.projectDir, input.pages ?? [], {
14
+ devServerCommand: input.devServerCommand,
15
+ devServerPort: input.devServerPort,
16
+ }),
17
+ runtime: (input) => verifyRuntime(input.projectDir, input.apiEndpoints ?? [], {
18
+ devServerCommand: input.devServerCommand,
19
+ devServerPort: input.devServerPort,
20
+ }),
21
+ prd: (input) => verifyPrd(input.projectDir, input.prdPath ?? "", input.baseBranch),
22
+ };
23
+ /** Run the full verification pipeline */
24
+ export async function runPipeline(input) {
25
+ const { gates: requestedGates, maxIterations = 3, } = input;
26
+ // Determine which gates to run
27
+ const gatesToRun = requestedGates ?? ["types", "lint", "tests"];
28
+ const results = [];
29
+ try {
30
+ for (const gateName of gatesToRun) {
31
+ const gateFn = gateRegistry[gateName];
32
+ if (!gateFn) {
33
+ results.push({
34
+ gate: gateName,
35
+ passed: false,
36
+ errors: [{ message: `Unknown gate: ${gateName}` }],
37
+ warnings: [],
38
+ duration_ms: 0,
39
+ });
40
+ continue;
41
+ }
42
+ const result = await runGateSafe(gateName, () => gateFn(input));
43
+ results.push(result);
44
+ // Early exit: if all core gates (types, lint, tests) fail, skip remaining
45
+ const coreGates = results.filter(r => ["types", "lint", "tests"].includes(r.gate));
46
+ if (coreGates.length === 3 && coreGates.every(r => !r.passed)) {
47
+ // Add skipped gates
48
+ for (const remaining of gatesToRun.slice(gatesToRun.indexOf(gateName) + 1)) {
49
+ results.push({
50
+ gate: remaining,
51
+ passed: false,
52
+ errors: [],
53
+ warnings: ["Skipped due to core gate failures"],
54
+ duration_ms: 0,
55
+ });
56
+ }
57
+ break;
58
+ }
59
+ }
60
+ }
61
+ finally {
62
+ try {
63
+ await closeBrowser();
64
+ }
65
+ catch { /* non-fatal */ }
66
+ }
67
+ const passed = results.every(g => g.passed);
68
+ return {
69
+ passed,
70
+ iteration: 1,
71
+ maxIterations,
72
+ gates: results,
73
+ report: "", // Reporter agent in Wave 2 will handle this
74
+ };
75
+ }
76
+ const GATE_TIMEOUT_MS = 120_000; // 2 minutes per gate
77
+ async function runGateSafe(name, fn) {
78
+ const start = Date.now();
79
+ try {
80
+ const result = await Promise.race([
81
+ fn(),
82
+ new Promise((_, reject) => globalThis.setTimeout(() => reject(new Error(`Gate "${name}" timed out after ${GATE_TIMEOUT_MS / 1000}s`)), GATE_TIMEOUT_MS)),
83
+ ]);
84
+ return result;
85
+ }
86
+ catch (err) {
87
+ const duration = Date.now() - start;
88
+ const message = err instanceof Error ? err.message : String(err);
89
+ const isTimeout = message.includes("timed out");
90
+ return {
91
+ gate: name,
92
+ passed: false,
93
+ errors: [{ message: isTimeout ? message : `Gate "${name}" crashed: ${message}` }],
94
+ warnings: [],
95
+ duration_ms: duration,
96
+ };
97
+ }
98
+ }
99
+ // Re-export individual gates for direct use
100
+ export { verifyTypes } from "./types-gate.js";
101
+ export { verifyLint } from "./lint-gate.js";
102
+ export { verifyTests } from "./tests-gate.js";
103
+ export { verifyVisual } from "./visual-gate.js";
104
+ export { verifyRuntime } from "./runtime-gate.js";
105
+ export { verifyPrd } from "./prd-gate.js";
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gates/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { GateResult } from "../types.js";
2
+ export declare function verifyLint(projectDir: string): Promise<GateResult>;
@@ -0,0 +1,66 @@
1
+ import { execSync } from "node:child_process";
2
+ /**
3
+ * Biome diagnostics often look like:
4
+ * path/to/file.ts:10:5 lint/rule ...
5
+ * or header lines with ━━ separators
6
+ */
7
+ const BIOME_LOC_RE = /^(.+?):(\d+):\d+\s+(.+)$/;
8
+ export async function verifyLint(projectDir) {
9
+ const start = Date.now();
10
+ const errors = [];
11
+ const warnings = [];
12
+ try {
13
+ execSync("npx biome check", {
14
+ cwd: projectDir,
15
+ stdio: "pipe",
16
+ timeout: 60_000,
17
+ });
18
+ return { gate: "lint", passed: true, errors, warnings, duration_ms: Date.now() - start };
19
+ }
20
+ catch (err) {
21
+ const stdout = err instanceof Error && "stdout" in err
22
+ ? String(err.stdout)
23
+ : "";
24
+ const stderr = err instanceof Error && "stderr" in err
25
+ ? String(err.stderr)
26
+ : "";
27
+ const output = `${stdout}\n${stderr}`;
28
+ const rawErrors = [];
29
+ for (const line of output.split("\n")) {
30
+ const trimmed = line.trim();
31
+ if (!trimmed)
32
+ continue;
33
+ if (trimmed.includes(" ━━") ||
34
+ trimmed.toLowerCase().includes("error") ||
35
+ trimmed.includes("×")) {
36
+ const match = BIOME_LOC_RE.exec(trimmed);
37
+ if (match) {
38
+ rawErrors.push({
39
+ file: match[1],
40
+ line: Number.parseInt(match[2], 10),
41
+ message: match[3],
42
+ });
43
+ }
44
+ else {
45
+ rawErrors.push({ message: trimmed });
46
+ }
47
+ }
48
+ }
49
+ // Cap at 50 errors to avoid massive output
50
+ const cappedErrors = rawErrors.slice(0, 50);
51
+ if (rawErrors.length > 50) {
52
+ cappedErrors.push({ message: `... and ${rawErrors.length - 50} more errors` });
53
+ }
54
+ if (cappedErrors.length === 0) {
55
+ cappedErrors.push({ message: "biome check exited with non-zero status but no errors were parsed" });
56
+ }
57
+ return {
58
+ gate: "lint",
59
+ passed: false,
60
+ errors: cappedErrors,
61
+ warnings,
62
+ duration_ms: Date.now() - start,
63
+ };
64
+ }
65
+ }
66
+ //# sourceMappingURL=lint-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint-gate.js","sourceRoot":"","sources":["../../src/gates/lint-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { GateResult } from "../types.js";
2
+ /**
3
+ * Reads the git diff and compares against PRD acceptance criteria
4
+ * to check coverage. This is a heuristic check -- it helps catch
5
+ * obvious omissions but cannot verify behavior.
6
+ */
7
+ export declare function verifyPrd(projectDir: string, prdPath: string, baseBranch?: string): Promise<GateResult>;
@@ -0,0 +1,193 @@
1
+ import { execSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ /**
4
+ * Reads the git diff and compares against PRD acceptance criteria
5
+ * to check coverage. This is a heuristic check -- it helps catch
6
+ * obvious omissions but cannot verify behavior.
7
+ */
8
+ export async function verifyPrd(projectDir, prdPath, baseBranch = "main") {
9
+ const start = Date.now();
10
+ const errors = [];
11
+ const warnings = [];
12
+ try {
13
+ // Read PRD content
14
+ const prdContent = readFileSync(prdPath, "utf-8");
15
+ // Extract acceptance criteria
16
+ const criteria = extractCriteria(prdContent);
17
+ if (criteria.length === 0) {
18
+ warnings.push("No acceptance criteria found in PRD (looked for checkboxes and criteria headings)");
19
+ return {
20
+ gate: "prd",
21
+ passed: true,
22
+ errors,
23
+ warnings,
24
+ duration_ms: Date.now() - start,
25
+ };
26
+ }
27
+ // Get changed file names from diff
28
+ let changedFiles;
29
+ try {
30
+ const nameOnly = execSync(`git diff ${baseBranch}...HEAD --name-only`, { cwd: projectDir, encoding: "utf-8", timeout: 30_000 });
31
+ changedFiles = nameOnly
32
+ .split("\n")
33
+ .map((f) => f.trim())
34
+ .filter(Boolean);
35
+ }
36
+ catch {
37
+ changedFiles = [];
38
+ warnings.push("Could not get git diff -- branch may not have diverged from base");
39
+ }
40
+ // Get stat summary for additional context
41
+ let diffStat = "";
42
+ try {
43
+ diffStat = execSync(`git diff ${baseBranch}...HEAD --stat`, {
44
+ cwd: projectDir,
45
+ encoding: "utf-8",
46
+ timeout: 30_000,
47
+ });
48
+ }
49
+ catch {
50
+ // Non-critical -- we still have changedFiles
51
+ }
52
+ if (changedFiles.length === 0 && diffStat === "") {
53
+ warnings.push("No changes detected against base branch -- all criteria marked unclear");
54
+ for (const criterion of criteria) {
55
+ warnings.push(`? ${criterion} -- no changes to evaluate against`);
56
+ }
57
+ return {
58
+ gate: "prd",
59
+ passed: false,
60
+ errors: [{ message: `No changes found to evaluate against ${criteria.length} criteria` }],
61
+ warnings,
62
+ duration_ms: Date.now() - start,
63
+ };
64
+ }
65
+ // Evaluate each criterion against the diff
66
+ for (const criterion of criteria) {
67
+ const result = evaluateCriterion(criterion, changedFiles);
68
+ if (result.status === "covered") {
69
+ warnings.push(`\u2713 ${criterion} -- likely covered (matched: ${result.matchedFiles.join(", ")})`);
70
+ }
71
+ else if (result.status === "unclear") {
72
+ warnings.push(`? ${criterion} -- could not determine coverage`);
73
+ }
74
+ else {
75
+ errors.push({
76
+ message: `\u2717 ${criterion} -- no matching changes found`,
77
+ remediation: "Ensure the relevant code changes are committed and address this criterion",
78
+ });
79
+ }
80
+ }
81
+ }
82
+ catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ errors.push({ message: `PRD verification failed: ${message}` });
85
+ }
86
+ return {
87
+ gate: "prd",
88
+ passed: errors.length === 0,
89
+ errors,
90
+ warnings,
91
+ duration_ms: Date.now() - start,
92
+ };
93
+ }
94
+ /**
95
+ * Extract acceptance criteria from PRD markdown content.
96
+ * Looks for:
97
+ * 1. Checkbox lines: `- [ ] ...` or `- [x] ...`
98
+ * 2. Lines under "## Acceptance Criteria" or "## User Stories" headings
99
+ */
100
+ function extractCriteria(content) {
101
+ const lines = content.split("\n");
102
+ const criteria = [];
103
+ const seen = new Set();
104
+ let inCriteriaSection = false;
105
+ for (const line of lines) {
106
+ const trimmed = line.trim();
107
+ // Check for criteria section headings
108
+ if (/^#{1,3}\s+(acceptance\s+criteria|user\s+stories)/i.test(trimmed)) {
109
+ inCriteriaSection = true;
110
+ continue;
111
+ }
112
+ // End criteria section at next heading
113
+ if (inCriteriaSection && /^#{1,3}\s+/.test(trimmed) && !/^#{1,3}\s+(acceptance\s+criteria|user\s+stories)/i.test(trimmed)) {
114
+ inCriteriaSection = false;
115
+ continue;
116
+ }
117
+ // Checkbox lines anywhere in the document
118
+ const checkboxMatch = trimmed.match(/^-\s+\[[ x]\]\s+(.+)/i);
119
+ if (checkboxMatch) {
120
+ const text = checkboxMatch[1].trim();
121
+ if (!seen.has(text)) {
122
+ seen.add(text);
123
+ criteria.push(text);
124
+ }
125
+ continue;
126
+ }
127
+ // Bullet points under a criteria section heading
128
+ if (inCriteriaSection) {
129
+ const bulletMatch = trimmed.match(/^[-*]\s+(.+)/);
130
+ if (bulletMatch) {
131
+ const text = bulletMatch[1].trim();
132
+ if (!seen.has(text)) {
133
+ seen.add(text);
134
+ criteria.push(text);
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return criteria;
140
+ }
141
+ /**
142
+ * Heuristic evaluation of whether a criterion is covered by the diff.
143
+ * Extracts keywords from the criterion and matches against changed file paths.
144
+ */
145
+ function evaluateCriterion(criterion, changedFiles) {
146
+ // Extract meaningful keywords (3+ chars, not common words)
147
+ const stopWords = new Set([
148
+ "the", "and", "for", "are", "but", "not", "you", "all", "can", "has",
149
+ "her", "was", "one", "our", "out", "its", "his", "how", "its", "may",
150
+ "who", "did", "get", "let", "say", "she", "too", "use", "way", "each",
151
+ "which", "their", "will", "other", "about", "many", "then", "them",
152
+ "been", "have", "from", "with", "they", "this", "that", "what", "when",
153
+ "make", "like", "just", "over", "such", "take", "into", "than", "most",
154
+ "also", "should", "would", "could", "must", "shall", "might", "does",
155
+ "display", "show", "page", "user", "view", "click", "able", "ensure",
156
+ "given", "when", "then",
157
+ ]);
158
+ const keywords = criterion
159
+ .toLowerCase()
160
+ .replace(/[^a-z0-9\s-]/g, " ")
161
+ .split(/\s+/)
162
+ .filter((w) => w.length >= 3 && !stopWords.has(w));
163
+ if (keywords.length === 0) {
164
+ return { status: "unclear", matchedFiles: [] };
165
+ }
166
+ // Match keywords against file paths
167
+ const matchedFiles = [];
168
+ for (const file of changedFiles) {
169
+ const fileLower = file.toLowerCase();
170
+ // Extract file name parts (split on /, ., -, _)
171
+ const fileParts = fileLower.split(/[/.\-_]/).filter(Boolean);
172
+ for (const keyword of keywords) {
173
+ if (fileLower.includes(keyword) ||
174
+ fileParts.some((part) => part.includes(keyword) || keyword.includes(part))) {
175
+ matchedFiles.push(file);
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ if (matchedFiles.length > 0) {
181
+ // Cap displayed files at 3 for readability
182
+ const displayFiles = matchedFiles.length > 3
183
+ ? [...matchedFiles.slice(0, 3), `+${matchedFiles.length - 3} more`]
184
+ : matchedFiles;
185
+ return { status: "covered", matchedFiles: displayFiles };
186
+ }
187
+ // If no files matched but there are very few keywords, mark as unclear
188
+ if (keywords.length <= 2) {
189
+ return { status: "unclear", matchedFiles: [] };
190
+ }
191
+ return { status: "not_covered", matchedFiles: [] };
192
+ }
193
+ //# sourceMappingURL=prd-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prd-gate.js","sourceRoot":"","sources":["../../src/gates/prd-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { GateResult } from "../types.js";
2
+ export declare function verifyRuntime(projectDir: string, endpoints: string[], options?: {
3
+ devServerCommand?: string;
4
+ devServerPort?: number;
5
+ }): Promise<GateResult>;
@@ -0,0 +1,99 @@
1
+ import { startDevServer, stopDevServer, waitForServer, } from "../utils/browser.js";
2
+ export async function verifyRuntime(projectDir, endpoints, options) {
3
+ const start = Date.now();
4
+ const port = options?.devServerPort ?? 3000;
5
+ const errors = [];
6
+ const warnings = [];
7
+ try {
8
+ // Start dev server
9
+ try {
10
+ await startDevServer(projectDir, options?.devServerCommand, port);
11
+ }
12
+ catch (err) {
13
+ const message = err instanceof Error ? err.message : "Unknown dev server error";
14
+ return {
15
+ gate: "runtime",
16
+ passed: false,
17
+ errors: [{ message: `Dev server failed to start: ${message}` }],
18
+ warnings,
19
+ duration_ms: Date.now() - start,
20
+ };
21
+ }
22
+ // Ensure server is reachable
23
+ const ready = await waitForServer(port);
24
+ if (!ready) {
25
+ return {
26
+ gate: "runtime",
27
+ passed: false,
28
+ errors: [{ message: `Dev server not reachable on port ${port}` }],
29
+ warnings,
30
+ duration_ms: Date.now() - start,
31
+ };
32
+ }
33
+ // Test each endpoint
34
+ for (const endpoint of endpoints) {
35
+ let method;
36
+ let path;
37
+ // Parse "GET /api/foo" or "POST /api/foo" or just "/api/foo"
38
+ const spaceIndex = endpoint.indexOf(" ");
39
+ if (spaceIndex !== -1 && spaceIndex < endpoint.indexOf("/")) {
40
+ method = endpoint.substring(0, spaceIndex).toUpperCase();
41
+ path = endpoint.substring(spaceIndex + 1).trim();
42
+ }
43
+ else {
44
+ method = "GET";
45
+ path = endpoint.trim();
46
+ }
47
+ const label = `${method} ${path}`;
48
+ try {
49
+ const response = await fetch(`http://localhost:${port}${path}`, {
50
+ method,
51
+ });
52
+ if (response.status >= 200 && response.status < 300) {
53
+ // Success -- try to parse as JSON for informational warning
54
+ try {
55
+ const json = await response.json();
56
+ const keys = Object.keys(json);
57
+ warnings.push(`${label} -> ${response.status} (JSON, ${keys.length} keys)`);
58
+ }
59
+ catch {
60
+ // Not JSON -- still a success, just note it
61
+ warnings.push(`${label} -> ${response.status} (non-JSON response)`);
62
+ }
63
+ }
64
+ else {
65
+ errors.push({
66
+ message: `${label} -> ${response.status} ${response.statusText}`,
67
+ });
68
+ }
69
+ }
70
+ catch (err) {
71
+ const message = err instanceof Error ? err.message : "Request failed";
72
+ errors.push({
73
+ message: `${label} -> FAILED: ${message}`,
74
+ });
75
+ }
76
+ }
77
+ return {
78
+ gate: "runtime",
79
+ passed: errors.length === 0,
80
+ errors,
81
+ warnings,
82
+ duration_ms: Date.now() - start,
83
+ };
84
+ }
85
+ catch (err) {
86
+ const message = err instanceof Error ? err.message : "Unknown error in verifyRuntime";
87
+ return {
88
+ gate: "runtime",
89
+ passed: false,
90
+ errors: [{ message }],
91
+ warnings,
92
+ duration_ms: Date.now() - start,
93
+ };
94
+ }
95
+ finally {
96
+ await stopDevServer();
97
+ }
98
+ }
99
+ //# sourceMappingURL=runtime-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-gate.js","sourceRoot":"","sources":["../../src/gates/runtime-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { GateResult } from "../types.js";
2
+ export declare function verifyTests(projectDir: string): Promise<GateResult>;
@@ -0,0 +1,116 @@
1
+ import { execSync } from "node:child_process";
2
+ /**
3
+ * Common test failure patterns with file/line info:
4
+ * FAIL src/foo.test.ts > suite > test name
5
+ * at src/foo.test.ts:42:10
6
+ */
7
+ const TEST_FILE_RE = /^FAIL\s+(.+?)(?:\s+>|$)/;
8
+ const STACKTRACE_RE = /at\s+.*?([^\s(]+):(\d+):\d+/;
9
+ export async function verifyTests(projectDir) {
10
+ const start = Date.now();
11
+ const errors = [];
12
+ const warnings = [];
13
+ // Check if the test script exists in package.json
14
+ try {
15
+ const pkgRaw = execSync("node -e \"process.stdout.write(JSON.stringify(require('./package.json')))\"", {
16
+ cwd: projectDir,
17
+ stdio: "pipe",
18
+ timeout: 10_000,
19
+ });
20
+ const pkg = JSON.parse(String(pkgRaw));
21
+ if (!pkg.scripts?.test) {
22
+ return {
23
+ gate: "tests",
24
+ passed: true,
25
+ errors: [],
26
+ warnings: ["No test script found"],
27
+ duration_ms: Date.now() - start,
28
+ };
29
+ }
30
+ }
31
+ catch {
32
+ return {
33
+ gate: "tests",
34
+ passed: true,
35
+ errors: [],
36
+ warnings: ["No test script found"],
37
+ duration_ms: Date.now() - start,
38
+ };
39
+ }
40
+ try {
41
+ const result = execSync("npm run test -- --run", {
42
+ cwd: projectDir,
43
+ stdio: "pipe",
44
+ timeout: 300_000,
45
+ });
46
+ const output = String(result);
47
+ // Parse test summary from Vitest output
48
+ const summaryMatch = output.match(/Tests\s+(\d+)\s+passed(?:\s*\|\s*(\d+)\s+failed)?/);
49
+ if (summaryMatch) {
50
+ const passed = summaryMatch[1];
51
+ const failed = summaryMatch[2] ?? "0";
52
+ warnings.push(`${passed} passed, ${failed} failed`);
53
+ }
54
+ return {
55
+ gate: "tests",
56
+ passed: true,
57
+ errors: [],
58
+ warnings,
59
+ duration_ms: Date.now() - start,
60
+ };
61
+ }
62
+ catch (err) {
63
+ const stdout = err instanceof Error && "stdout" in err
64
+ ? String(err.stdout)
65
+ : "";
66
+ const stderr = err instanceof Error && "stderr" in err
67
+ ? String(err.stderr)
68
+ : "";
69
+ const output = `${stdout}\n${stderr}`;
70
+ let lastFailFile;
71
+ for (const line of output.split("\n")) {
72
+ const trimmed = line.trim();
73
+ if (!trimmed)
74
+ continue;
75
+ // Track which test file we're in
76
+ const failMatch = TEST_FILE_RE.exec(trimmed);
77
+ if (failMatch) {
78
+ lastFailFile = failMatch[1];
79
+ }
80
+ // Try to extract stack trace location
81
+ const stackMatch = STACKTRACE_RE.exec(trimmed);
82
+ if (stackMatch) {
83
+ lastFailFile = stackMatch[1];
84
+ }
85
+ if (trimmed.includes("FAIL") ||
86
+ trimmed.includes("AssertionError") ||
87
+ trimmed.includes("AssertionError") ||
88
+ trimmed.includes("Expected") ||
89
+ trimmed.includes("Received")) {
90
+ errors.push({
91
+ file: lastFailFile,
92
+ line: stackMatch ? Number.parseInt(stackMatch[2], 10) : undefined,
93
+ message: trimmed,
94
+ });
95
+ }
96
+ }
97
+ // Also try to extract the summary even on failure
98
+ const summaryMatch = output.match(/Tests\s+(?:(\d+)\s+passed\s*\|\s*)?(\d+)\s+failed/);
99
+ if (summaryMatch) {
100
+ const passed = summaryMatch[1] ?? "0";
101
+ const failed = summaryMatch[2];
102
+ warnings.push(`${passed} passed, ${failed} failed`);
103
+ }
104
+ if (errors.length === 0) {
105
+ errors.push({ message: "Test runner exited with non-zero status" });
106
+ }
107
+ return {
108
+ gate: "tests",
109
+ passed: false,
110
+ errors,
111
+ warnings,
112
+ duration_ms: Date.now() - start,
113
+ };
114
+ }
115
+ }
116
+ //# sourceMappingURL=tests-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests-gate.js","sourceRoot":"","sources":["../../src/gates/tests-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { GateResult } from "../types.js";
2
+ export declare function verifyTypes(projectDir: string): Promise<GateResult>;