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.
- package/.forge.json +5 -0
- package/AGENTS.md +42 -0
- package/README.md +283 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +148 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/loader.js +44 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +57 -0
- package/dist/config/schema.js +15 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/gates/index.d.ts +11 -0
- package/dist/gates/index.js +106 -0
- package/dist/gates/index.js.map +1 -0
- package/dist/gates/lint-gate.d.ts +2 -0
- package/dist/gates/lint-gate.js +66 -0
- package/dist/gates/lint-gate.js.map +1 -0
- package/dist/gates/prd-gate.d.ts +7 -0
- package/dist/gates/prd-gate.js +193 -0
- package/dist/gates/prd-gate.js.map +1 -0
- package/dist/gates/runtime-gate.d.ts +5 -0
- package/dist/gates/runtime-gate.js +99 -0
- package/dist/gates/runtime-gate.js.map +1 -0
- package/dist/gates/tests-gate.d.ts +2 -0
- package/dist/gates/tests-gate.js +116 -0
- package/dist/gates/tests-gate.js.map +1 -0
- package/dist/gates/types-gate.d.ts +2 -0
- package/dist/gates/types-gate.js +59 -0
- package/dist/gates/types-gate.js.map +1 -0
- package/dist/gates/visual-gate.d.ts +6 -0
- package/dist/gates/visual-gate.js +118 -0
- package/dist/gates/visual-gate.js.map +1 -0
- package/dist/go/auto-chain.d.ts +107 -0
- package/dist/go/auto-chain.js +303 -0
- package/dist/go/auto-chain.js.map +1 -0
- package/dist/go/executor.d.ts +130 -0
- package/dist/go/executor.js +409 -0
- package/dist/go/executor.js.map +1 -0
- package/dist/go/finalize.d.ts +58 -0
- package/dist/go/finalize.js +200 -0
- package/dist/go/finalize.js.map +1 -0
- package/dist/go/linear-sync.d.ts +75 -0
- package/dist/go/linear-sync.js +239 -0
- package/dist/go/linear-sync.js.map +1 -0
- package/dist/go/verify-loop.d.ts +47 -0
- package/dist/go/verify-loop.js +172 -0
- package/dist/go/verify-loop.js.map +1 -0
- package/dist/hooks/pre-commit.d.ts +5 -0
- package/dist/hooks/pre-commit.js +69 -0
- package/dist/hooks/pre-commit.js.map +1 -0
- package/dist/linear/client.d.ts +108 -0
- package/dist/linear/client.js +388 -0
- package/dist/linear/client.js.map +1 -0
- package/dist/linear/issues.d.ts +20 -0
- package/dist/linear/issues.js +39 -0
- package/dist/linear/issues.js.map +1 -0
- package/dist/linear/milestones.d.ts +11 -0
- package/dist/linear/milestones.js +32 -0
- package/dist/linear/milestones.js.map +1 -0
- package/dist/linear/projects.d.ts +16 -0
- package/dist/linear/projects.js +50 -0
- package/dist/linear/projects.js.map +1 -0
- package/dist/reporter/human.d.ts +2 -0
- package/dist/reporter/human.js +63 -0
- package/dist/reporter/human.js.map +1 -0
- package/dist/reporter/json.d.ts +2 -0
- package/dist/reporter/json.js +4 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +109 -0
- package/dist/server.js.map +1 -0
- package/dist/spec/generator.d.ts +14 -0
- package/dist/spec/generator.js +206 -0
- package/dist/spec/generator.js.map +1 -0
- package/dist/spec/interview.d.ts +104 -0
- package/dist/spec/interview.js +342 -0
- package/dist/spec/interview.js.map +1 -0
- package/dist/spec/linear-sync.d.ts +48 -0
- package/dist/spec/linear-sync.js +125 -0
- package/dist/spec/linear-sync.js.map +1 -0
- package/dist/spec/scanner.d.ts +45 -0
- package/dist/spec/scanner.js +473 -0
- package/dist/spec/scanner.js.map +1 -0
- package/dist/spec/templates.d.ts +345 -0
- package/dist/spec/templates.js +86 -0
- package/dist/spec/templates.js.map +1 -0
- package/dist/state/reader.d.ts +29 -0
- package/dist/state/reader.js +116 -0
- package/dist/state/reader.js.map +1 -0
- package/dist/state/writer.d.ts +60 -0
- package/dist/state/writer.js +222 -0
- package/dist/state/writer.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.js +89 -0
- package/dist/utils/browser.js.map +1 -0
- package/hooks/pre-commit-verify.js +103 -0
- package/package.json +68 -0
- package/skills/README.md +33 -0
- package/skills/forge-go.md +332 -0
- package/skills/forge-spec.md +251 -0
- 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,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,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,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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"}
|