forge-cc 0.1.13 → 0.1.15
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/dist/config/loader.js +24 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +67 -7
- package/dist/config/schema.js +9 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/gates/remediation.d.ts +8 -0
- package/dist/gates/remediation.js +27 -0
- package/dist/gates/remediation.js.map +1 -1
- package/dist/gates/tests-gate.js +177 -79
- package/dist/gates/tests-gate.js.map +1 -1
- package/dist/setup/templates.d.ts +2 -0
- package/dist/setup/templates.js +3 -0
- package/dist/setup/templates.js.map +1 -1
- package/dist/setup/test-planner.d.ts +38 -0
- package/dist/setup/test-planner.js +91 -0
- package/dist/setup/test-planner.js.map +1 -0
- package/dist/setup/test-scaffold.js +1 -1
- package/dist/spec/templates.d.ts +12 -4
- package/dist/spec/templates.js +1 -0
- package/dist/spec/templates.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
- package/skills/forge-go.md +32 -20
- package/skills/forge-setup.md +71 -0
- package/skills/forge-spec.md +4 -0
package/dist/config/loader.js
CHANGED
|
@@ -16,8 +16,30 @@ export function loadConfig(projectDir) {
|
|
|
16
16
|
// Auto-detect from package.json
|
|
17
17
|
return autoDetectConfig(projectDir);
|
|
18
18
|
}
|
|
19
|
+
function detectTestDir(projectDir) {
|
|
20
|
+
for (const dir of ["tests", "__tests__", "test"]) {
|
|
21
|
+
if (existsSync(join(projectDir, dir))) {
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return "tests";
|
|
26
|
+
}
|
|
27
|
+
function detectTestingConfig(projectDir, allDeps) {
|
|
28
|
+
const runner = allDeps.vitest ? "vitest" : allDeps.jest ? "jest" : null;
|
|
29
|
+
if (!runner)
|
|
30
|
+
return undefined;
|
|
31
|
+
return {
|
|
32
|
+
enforce: false,
|
|
33
|
+
runner,
|
|
34
|
+
testDir: detectTestDir(projectDir),
|
|
35
|
+
sourceDir: "src",
|
|
36
|
+
structural: true,
|
|
37
|
+
categories: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
19
40
|
function autoDetectConfig(projectDir) {
|
|
20
41
|
const gates = [];
|
|
42
|
+
let testing;
|
|
21
43
|
try {
|
|
22
44
|
const pkgPath = join(projectDir, "package.json");
|
|
23
45
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -31,6 +53,7 @@ function autoDetectConfig(projectDir) {
|
|
|
31
53
|
gates.push("lint");
|
|
32
54
|
if (pkg.scripts?.test)
|
|
33
55
|
gates.push("tests");
|
|
56
|
+
testing = detectTestingConfig(projectDir, allDeps);
|
|
34
57
|
}
|
|
35
58
|
catch {
|
|
36
59
|
// No package.json or invalid — use defaults
|
|
@@ -39,6 +62,6 @@ function autoDetectConfig(projectDir) {
|
|
|
39
62
|
if (gates.length === 0) {
|
|
40
63
|
gates.push("types", "lint", "tests");
|
|
41
64
|
}
|
|
42
|
-
return forgeConfigSchema.parse({ gates });
|
|
65
|
+
return forgeConfigSchema.parse({ gates, testing });
|
|
43
66
|
}
|
|
44
67
|
//# sourceMappingURL=loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAEnD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YAC1D,OAAO,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,yCAAyC,OAAO,iCAAiC,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,OAAO,gBAAgB,CAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,gBAAgB,CAAC,UAAkB;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -19,6 +19,28 @@ export declare const reviewConfigSchema: z.ZodObject<{
|
|
|
19
19
|
}, {
|
|
20
20
|
blocking?: boolean | undefined;
|
|
21
21
|
}>;
|
|
22
|
+
export declare const testingConfigSchema: z.ZodObject<{
|
|
23
|
+
enforce: z.ZodDefault<z.ZodBoolean>;
|
|
24
|
+
runner: z.ZodDefault<z.ZodEnum<["vitest", "jest", "none"]>>;
|
|
25
|
+
testDir: z.ZodDefault<z.ZodString>;
|
|
26
|
+
sourceDir: z.ZodDefault<z.ZodString>;
|
|
27
|
+
structural: z.ZodDefault<z.ZodBoolean>;
|
|
28
|
+
categories: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
runner: "vitest" | "jest" | "none";
|
|
31
|
+
categories: string[];
|
|
32
|
+
enforce: boolean;
|
|
33
|
+
testDir: string;
|
|
34
|
+
sourceDir: string;
|
|
35
|
+
structural: boolean;
|
|
36
|
+
}, {
|
|
37
|
+
runner?: "vitest" | "jest" | "none" | undefined;
|
|
38
|
+
categories?: string[] | undefined;
|
|
39
|
+
enforce?: boolean | undefined;
|
|
40
|
+
testDir?: string | undefined;
|
|
41
|
+
sourceDir?: string | undefined;
|
|
42
|
+
structural?: boolean | undefined;
|
|
43
|
+
}>;
|
|
22
44
|
export declare const forgeConfigSchema: z.ZodObject<{
|
|
23
45
|
gates: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
24
46
|
maxIterations: z.ZodDefault<z.ZodNumber>;
|
|
@@ -45,25 +67,51 @@ export declare const forgeConfigSchema: z.ZodObject<{
|
|
|
45
67
|
}, {
|
|
46
68
|
blocking?: boolean | undefined;
|
|
47
69
|
}>>;
|
|
70
|
+
testing: z.ZodOptional<z.ZodObject<{
|
|
71
|
+
enforce: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
runner: z.ZodDefault<z.ZodEnum<["vitest", "jest", "none"]>>;
|
|
73
|
+
testDir: z.ZodDefault<z.ZodString>;
|
|
74
|
+
sourceDir: z.ZodDefault<z.ZodString>;
|
|
75
|
+
structural: z.ZodDefault<z.ZodBoolean>;
|
|
76
|
+
categories: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
runner: "vitest" | "jest" | "none";
|
|
79
|
+
categories: string[];
|
|
80
|
+
enforce: boolean;
|
|
81
|
+
testDir: string;
|
|
82
|
+
sourceDir: string;
|
|
83
|
+
structural: boolean;
|
|
84
|
+
}, {
|
|
85
|
+
runner?: "vitest" | "jest" | "none" | undefined;
|
|
86
|
+
categories?: string[] | undefined;
|
|
87
|
+
enforce?: boolean | undefined;
|
|
88
|
+
testDir?: string | undefined;
|
|
89
|
+
sourceDir?: string | undefined;
|
|
90
|
+
structural?: boolean | undefined;
|
|
91
|
+
}>>;
|
|
48
92
|
}, "strip", z.ZodTypeAny, {
|
|
49
93
|
gates: string[];
|
|
50
94
|
maxIterations: number;
|
|
51
95
|
verifyFreshness: number;
|
|
52
|
-
prdPath?: string | undefined;
|
|
53
|
-
review?: {
|
|
54
|
-
blocking: boolean;
|
|
55
|
-
} | undefined;
|
|
56
96
|
devServer?: {
|
|
57
97
|
port: number;
|
|
58
98
|
command: string;
|
|
59
99
|
readyPattern?: string | undefined;
|
|
60
100
|
} | undefined;
|
|
61
|
-
linearProject?: string | undefined;
|
|
62
|
-
}, {
|
|
63
101
|
prdPath?: string | undefined;
|
|
102
|
+
linearProject?: string | undefined;
|
|
64
103
|
review?: {
|
|
65
|
-
blocking
|
|
104
|
+
blocking: boolean;
|
|
105
|
+
} | undefined;
|
|
106
|
+
testing?: {
|
|
107
|
+
runner: "vitest" | "jest" | "none";
|
|
108
|
+
categories: string[];
|
|
109
|
+
enforce: boolean;
|
|
110
|
+
testDir: string;
|
|
111
|
+
sourceDir: string;
|
|
112
|
+
structural: boolean;
|
|
66
113
|
} | undefined;
|
|
114
|
+
}, {
|
|
67
115
|
gates?: string[] | undefined;
|
|
68
116
|
maxIterations?: number | undefined;
|
|
69
117
|
verifyFreshness?: number | undefined;
|
|
@@ -72,6 +120,18 @@ export declare const forgeConfigSchema: z.ZodObject<{
|
|
|
72
120
|
command: string;
|
|
73
121
|
readyPattern?: string | undefined;
|
|
74
122
|
} | undefined;
|
|
123
|
+
prdPath?: string | undefined;
|
|
75
124
|
linearProject?: string | undefined;
|
|
125
|
+
review?: {
|
|
126
|
+
blocking?: boolean | undefined;
|
|
127
|
+
} | undefined;
|
|
128
|
+
testing?: {
|
|
129
|
+
runner?: "vitest" | "jest" | "none" | undefined;
|
|
130
|
+
categories?: string[] | undefined;
|
|
131
|
+
enforce?: boolean | undefined;
|
|
132
|
+
testDir?: string | undefined;
|
|
133
|
+
sourceDir?: string | undefined;
|
|
134
|
+
structural?: boolean | undefined;
|
|
135
|
+
} | undefined;
|
|
76
136
|
}>;
|
|
77
137
|
export type ForgeConfigInput = z.input<typeof forgeConfigSchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -7,6 +7,14 @@ export const devServerSchema = z.object({
|
|
|
7
7
|
export const reviewConfigSchema = z.object({
|
|
8
8
|
blocking: z.boolean().default(false),
|
|
9
9
|
});
|
|
10
|
+
export const testingConfigSchema = z.object({
|
|
11
|
+
enforce: z.boolean().default(true),
|
|
12
|
+
runner: z.enum(["vitest", "jest", "none"]).default("vitest"),
|
|
13
|
+
testDir: z.string().default("tests"),
|
|
14
|
+
sourceDir: z.string().default("src"),
|
|
15
|
+
structural: z.boolean().default(true),
|
|
16
|
+
categories: z.array(z.string()).default([]),
|
|
17
|
+
});
|
|
10
18
|
export const forgeConfigSchema = z.object({
|
|
11
19
|
gates: z.array(z.string()).default(["types", "lint", "tests"]),
|
|
12
20
|
maxIterations: z.number().int().positive().default(5),
|
|
@@ -15,5 +23,6 @@ export const forgeConfigSchema = z.object({
|
|
|
15
23
|
prdPath: z.string().optional(),
|
|
16
24
|
linearProject: z.string().optional(),
|
|
17
25
|
review: reviewConfigSchema.optional(),
|
|
26
|
+
testing: testingConfigSchema.optional(),
|
|
18
27
|
});
|
|
19
28
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9D,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IAC7D,SAAS,EAAE,eAAe,CAAC,QAAQ,EAAE;IACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAClC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC5D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IACpC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IACrC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9D,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IAC7D,SAAS,EAAE,eAAe,CAAC,QAAQ,EAAE;IACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,EAAE;IACrC,OAAO,EAAE,mBAAmB,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC"}
|
|
@@ -36,3 +36,11 @@ export declare function buildVisualRemediation(error: GateError): string;
|
|
|
36
36
|
* next-step guidance so fix agents know exactly where to look.
|
|
37
37
|
*/
|
|
38
38
|
export declare function buildReviewRemediation(error: GateError): string;
|
|
39
|
+
/**
|
|
40
|
+
* Build actionable remediation text for a test coverage error.
|
|
41
|
+
*
|
|
42
|
+
* Handles missing test files (enforcement mode) and zero-coverage baseline
|
|
43
|
+
* failures. Returns file-specific instructions when a source file is
|
|
44
|
+
* identified, or a general scaffolding instruction otherwise.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildTestCoverageRemediation(error: GateError): string;
|
|
@@ -372,6 +372,33 @@ export function buildReviewRemediation(error) {
|
|
|
372
372
|
return `${location}Review finding: ${error.message} — address the issue and re-run the review gate.`;
|
|
373
373
|
}
|
|
374
374
|
// ---------------------------------------------------------------------------
|
|
375
|
+
// Test Coverage Remediation
|
|
376
|
+
// ---------------------------------------------------------------------------
|
|
377
|
+
/**
|
|
378
|
+
* Build actionable remediation text for a test coverage error.
|
|
379
|
+
*
|
|
380
|
+
* Handles missing test files (enforcement mode) and zero-coverage baseline
|
|
381
|
+
* failures. Returns file-specific instructions when a source file is
|
|
382
|
+
* identified, or a general scaffolding instruction otherwise.
|
|
383
|
+
*/
|
|
384
|
+
export function buildTestCoverageRemediation(error) {
|
|
385
|
+
const location = formatLocation(error);
|
|
386
|
+
// Missing test file for a specific source file (enforcement mode)
|
|
387
|
+
if (error.file && /missing.*test|no.*test.*file/i.test(error.message)) {
|
|
388
|
+
return `${location}Create a test file for this source file to satisfy enforcement. Run \`/forge:setup\` to scaffold tests automatically.`;
|
|
389
|
+
}
|
|
390
|
+
// Zero coverage / baseline failure
|
|
391
|
+
if (/no tests found|zero.*coverage/i.test(error.message)) {
|
|
392
|
+
return `${location}No tests exist in this project. Run \`/forge:setup\` to scaffold a test suite with the correct runner and directory structure.`;
|
|
393
|
+
}
|
|
394
|
+
// Thin coverage warning
|
|
395
|
+
if (/thin.*coverage|low.*ratio/i.test(error.message)) {
|
|
396
|
+
return `${location}Test coverage is very low. Prioritize adding tests for critical paths (API routes, business logic) first.`;
|
|
397
|
+
}
|
|
398
|
+
// Generic coverage remediation
|
|
399
|
+
return `${location}Add test coverage for the identified file. Run \`/forge:setup\` to scaffold tests.`;
|
|
400
|
+
}
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
375
402
|
// Shared Utilities
|
|
376
403
|
// ---------------------------------------------------------------------------
|
|
377
404
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediation.js","sourceRoot":"","sources":["../../src/gates/remediation.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remediation.js","sourceRoot":"","sources":["../../src/gates/remediation.ts"],"names":[],"mappings":"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"}
|
package/dist/gates/tests-gate.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { buildTestRemediation } from "./remediation.js";
|
|
2
|
+
import { buildTestRemediation, buildTestCoverageRemediation } from "./remediation.js";
|
|
3
|
+
import { analyzeTestCoverage } from "./test-analysis.js";
|
|
4
|
+
import { loadConfig } from "../config/loader.js";
|
|
3
5
|
/**
|
|
4
6
|
* Common test failure patterns with file/line info:
|
|
5
7
|
* FAIL src/foo.test.ts > suite > test name
|
|
@@ -11,7 +13,12 @@ export async function verifyTests(projectDir) {
|
|
|
11
13
|
const start = Date.now();
|
|
12
14
|
const errors = [];
|
|
13
15
|
const warnings = [];
|
|
14
|
-
//
|
|
16
|
+
// Load config and run test analysis
|
|
17
|
+
const config = loadConfig(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;
|
|
15
22
|
try {
|
|
16
23
|
const pkgRaw = execSync("node -e \"process.stdout.write(JSON.stringify(require('./package.json')))\"", {
|
|
17
24
|
cwd: projectDir,
|
|
@@ -19,103 +26,194 @@ export async function verifyTests(projectDir) {
|
|
|
19
26
|
timeout: 10_000,
|
|
20
27
|
});
|
|
21
28
|
const pkg = JSON.parse(String(pkgRaw));
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
gate: "tests",
|
|
25
|
-
passed: true,
|
|
26
|
-
errors: [],
|
|
27
|
-
warnings: ["No test script found"],
|
|
28
|
-
duration_ms: Date.now() - start,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
29
|
+
hasTestScript = !!pkg.scripts?.test;
|
|
31
30
|
}
|
|
32
31
|
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);
|
|
33
43
|
return {
|
|
34
44
|
gate: "tests",
|
|
35
|
-
passed:
|
|
36
|
-
errors
|
|
37
|
-
warnings
|
|
45
|
+
passed: false,
|
|
46
|
+
errors,
|
|
47
|
+
warnings,
|
|
38
48
|
duration_ms: Date.now() - start,
|
|
39
49
|
};
|
|
40
50
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Parse test summary from Vitest output
|
|
49
|
-
const summaryMatch = output.match(/Tests\s+(\d+)\s+passed(?:\s*\|\s*(\d+)\s+failed)?/);
|
|
50
|
-
if (summaryMatch) {
|
|
51
|
-
const passed = summaryMatch[1];
|
|
52
|
-
const failed = summaryMatch[2] ?? "0";
|
|
53
|
-
warnings.push(`${passed} passed, ${failed} failed`);
|
|
54
|
-
}
|
|
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);
|
|
55
58
|
return {
|
|
56
59
|
gate: "tests",
|
|
57
|
-
passed:
|
|
58
|
-
errors
|
|
60
|
+
passed: false,
|
|
61
|
+
errors,
|
|
59
62
|
warnings,
|
|
60
63
|
duration_ms: Date.now() - start,
|
|
61
64
|
};
|
|
62
65
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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`);
|
|
80
84
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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")) {
|
|
115
|
+
errors.push({
|
|
116
|
+
file: lastFailFile,
|
|
117
|
+
line: stackMatch ? Number.parseInt(stackMatch[2], 10) : undefined,
|
|
118
|
+
message: trimmed,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
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`);
|
|
85
128
|
}
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
file: lastFailFile,
|
|
93
|
-
line: stackMatch ? Number.parseInt(stackMatch[2], 10) : undefined,
|
|
94
|
-
message: trimmed,
|
|
95
|
-
});
|
|
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);
|
|
96
135
|
}
|
|
97
136
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
+
}
|
|
104
156
|
}
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
}
|
|
199
|
+
}
|
|
200
|
+
break; // Use the first successful command
|
|
201
|
+
}
|
|
107
202
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
error.remediation = buildTestRemediation(error);
|
|
203
|
+
catch {
|
|
204
|
+
// Command failed — try the next one
|
|
111
205
|
}
|
|
112
|
-
return {
|
|
113
|
-
gate: "tests",
|
|
114
|
-
passed: false,
|
|
115
|
-
errors,
|
|
116
|
-
warnings,
|
|
117
|
-
duration_ms: Date.now() - start,
|
|
118
|
-
};
|
|
119
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;
|
|
120
218
|
}
|
|
121
219
|
//# sourceMappingURL=tests-gate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tests-gate.js","sourceRoot":"","sources":["../../src/gates/tests-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"tests-gate.js","sourceRoot":"","sources":["../../src/gates/tests-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { TestingConfig } from "../types.js";
|
|
1
2
|
export interface SetupContext {
|
|
2
3
|
projectName: string;
|
|
3
4
|
techStack: string;
|
|
4
5
|
description: string;
|
|
5
6
|
gates: string[];
|
|
6
7
|
date: string;
|
|
8
|
+
testing?: TestingConfig;
|
|
7
9
|
}
|
|
8
10
|
export declare function forgeConfigTemplate(ctx: SetupContext): string;
|
|
9
11
|
export declare function claudeMdTemplate(ctx: SetupContext): string;
|
package/dist/setup/templates.js
CHANGED
|
@@ -6,6 +6,9 @@ export function forgeConfigTemplate(ctx) {
|
|
|
6
6
|
gates: ctx.gates,
|
|
7
7
|
maxIterations: 5,
|
|
8
8
|
};
|
|
9
|
+
if (ctx.testing) {
|
|
10
|
+
config.testing = ctx.testing;
|
|
11
|
+
}
|
|
9
12
|
return JSON.stringify(config, null, 2) + "\n";
|
|
10
13
|
}
|
|
11
14
|
// ── Project CLAUDE.md ───────────────────────────────────────────────
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/setup/templates.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,4EAA4E;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/setup/templates.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,4EAA4E;AAa5E,uEAAuE;AAEvE,MAAM,UAAU,mBAAmB,CAAC,GAAiB;IACnD,MAAM,MAAM,GAA4B;QACtC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,aAAa,EAAE,CAAC;KACjB,CAAC;IACF,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChB,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC/B,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAChD,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,gBAAgB,CAAC,GAAiB;IAChD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9D,OAAO,KAAK,GAAG,CAAC,WAAW;;;EAG3B,GAAG,CAAC,WAAW;;YAEL,GAAG,CAAC,SAAS;;;;;;;mDAO0B,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAqCtD,SAAS;;;;CAIxB,CAAC;AACF,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,eAAe,CAAC,GAAiB;IAC/C,OAAO,aAAa,GAAG,CAAC,WAAW;;;;;;;;sCAQC,GAAG,CAAC,IAAI;;;;;CAK7C,CAAC;AACF,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,iBAAiB,CAAC,GAAiB;IACjD,OAAO,eAAe,GAAG,CAAC,WAAW;;;;;;;;;;CAUtC,CAAC;AACF,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,iBAAiB,CAAC,GAAiB;IACjD,OAAO,uBAAuB,GAAG,CAAC,WAAW;;;;;;CAM9C,CAAC;AACF,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;CAcR,CAAC;AACF,CAAC;AAED,uEAAuE;AAEvE,MAAM,UAAU,mBAAmB;IACjC,OAAO;CACR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TestAnalysisReport } from "../gates/test-analysis.js";
|
|
2
|
+
import type { TestingConfig } from "../types.js";
|
|
3
|
+
import type { ScaffoldPlan } from "./test-scaffold.js";
|
|
4
|
+
export interface TestPlanningData {
|
|
5
|
+
/** The test analysis report from scanning the project */
|
|
6
|
+
report: TestAnalysisReport;
|
|
7
|
+
/** Human-readable summary of findings */
|
|
8
|
+
summary: string;
|
|
9
|
+
/** Per-category summaries for user confirmation prompts */
|
|
10
|
+
categorySummaries: CategorySummary[];
|
|
11
|
+
}
|
|
12
|
+
export interface CategorySummary {
|
|
13
|
+
name: string;
|
|
14
|
+
sourceCount: number;
|
|
15
|
+
testCount: number;
|
|
16
|
+
untestedCount: number;
|
|
17
|
+
description: string;
|
|
18
|
+
}
|
|
19
|
+
export interface TestPlannerResult {
|
|
20
|
+
/** The test analysis report from scanning the project */
|
|
21
|
+
report: TestAnalysisReport;
|
|
22
|
+
/** Categories the user confirmed for test scaffolding */
|
|
23
|
+
confirmedCategories: string[];
|
|
24
|
+
/** The chosen test runner */
|
|
25
|
+
runner: "vitest" | "jest";
|
|
26
|
+
/** The test directory */
|
|
27
|
+
testDir: string;
|
|
28
|
+
/** Whether structural tests are included */
|
|
29
|
+
structural: boolean;
|
|
30
|
+
/** The generated testing config to persist to .forge.json */
|
|
31
|
+
testingConfig: TestingConfig;
|
|
32
|
+
/** The scaffold plan (ready to execute) */
|
|
33
|
+
scaffoldPlan: ScaffoldPlan;
|
|
34
|
+
}
|
|
35
|
+
/** Analyze the project and prepare the test planning data. */
|
|
36
|
+
export declare function analyzeForTestPlanning(projectDir: string): Promise<TestPlanningData>;
|
|
37
|
+
/** Build the final plan + config after user confirms categories. */
|
|
38
|
+
export declare function buildTestPlan(report: TestAnalysisReport, confirmedCategories: string[], runner: "vitest" | "jest", testDir: string, structural: boolean, projectDir: string): Promise<TestPlannerResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ── Test Planner ────────────────────────────────────────────────────
|
|
2
|
+
// Interactive test planning module for /forge:setup integration.
|
|
3
|
+
// Analyzes project test coverage, then builds a scaffold plan + config
|
|
4
|
+
// from user-confirmed categories.
|
|
5
|
+
import { analyzeTestCoverage } from "../gates/test-analysis.js";
|
|
6
|
+
import { buildScaffoldPlan } from "./test-scaffold.js";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Category Description Helpers
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
function describeCategoryName(name) {
|
|
11
|
+
switch (name) {
|
|
12
|
+
case "api-routes":
|
|
13
|
+
return "API routes and endpoint handlers";
|
|
14
|
+
case "components":
|
|
15
|
+
return "UI components (React/TSX)";
|
|
16
|
+
case "utils":
|
|
17
|
+
return "Utility functions and helpers";
|
|
18
|
+
case "middleware":
|
|
19
|
+
return "Middleware and interceptors";
|
|
20
|
+
case "models":
|
|
21
|
+
return "Data models, schemas, and entities";
|
|
22
|
+
case "other":
|
|
23
|
+
return "Other source files";
|
|
24
|
+
default:
|
|
25
|
+
return name;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Analysis Phase
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/** Analyze the project and prepare the test planning data. */
|
|
32
|
+
export async function analyzeForTestPlanning(projectDir) {
|
|
33
|
+
const report = await analyzeTestCoverage(projectDir);
|
|
34
|
+
const categorySummaries = report.categories.map((cat) => ({
|
|
35
|
+
name: cat.name,
|
|
36
|
+
sourceCount: cat.sourceFiles.length,
|
|
37
|
+
testCount: cat.testFiles.length,
|
|
38
|
+
untestedCount: cat.untestedFiles.length,
|
|
39
|
+
description: describeCategoryName(cat.name),
|
|
40
|
+
}));
|
|
41
|
+
const lines = [];
|
|
42
|
+
lines.push(`Framework: ${report.framework.appFramework}`);
|
|
43
|
+
lines.push(`Test runner: ${report.framework.testRunner}`);
|
|
44
|
+
lines.push(`Coverage: ${report.coverage.testFiles} test files / ${report.coverage.sourceFiles} source files (ratio: ${report.coverage.ratio})`);
|
|
45
|
+
if (report.framework.detectedPatterns.length > 0) {
|
|
46
|
+
lines.push(`Detected: ${report.framework.detectedPatterns.join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
if (categorySummaries.length > 0) {
|
|
49
|
+
lines.push("");
|
|
50
|
+
lines.push("Categories:");
|
|
51
|
+
for (const cat of categorySummaries) {
|
|
52
|
+
lines.push(` - ${cat.name}: ${cat.sourceCount} source, ${cat.testCount} tested, ${cat.untestedCount} untested`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
report,
|
|
57
|
+
summary: lines.join("\n"),
|
|
58
|
+
categorySummaries,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Plan Building Phase
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/** Build the final plan + config after user confirms categories. */
|
|
65
|
+
export async function buildTestPlan(report, confirmedCategories, runner, testDir, structural, projectDir) {
|
|
66
|
+
const scaffoldPlan = await buildScaffoldPlan(report, {
|
|
67
|
+
projectDir,
|
|
68
|
+
testDir,
|
|
69
|
+
runner,
|
|
70
|
+
structural,
|
|
71
|
+
categories: confirmedCategories,
|
|
72
|
+
});
|
|
73
|
+
const testingConfig = {
|
|
74
|
+
enforce: true,
|
|
75
|
+
runner,
|
|
76
|
+
testDir,
|
|
77
|
+
sourceDir: "src",
|
|
78
|
+
structural,
|
|
79
|
+
categories: confirmedCategories,
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
report,
|
|
83
|
+
confirmedCategories,
|
|
84
|
+
runner,
|
|
85
|
+
testDir,
|
|
86
|
+
structural,
|
|
87
|
+
testingConfig,
|
|
88
|
+
scaffoldPlan,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=test-planner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-planner.js","sourceRoot":"","sources":["../../src/setup/test-planner.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,iEAAiE;AACjE,uEAAuE;AACvE,kCAAkC;AAIlC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAyCvD,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,SAAS,oBAAoB,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY;YACf,OAAO,kCAAkC,CAAC;QAC5C,KAAK,YAAY;YACf,OAAO,2BAA2B,CAAC;QACrC,KAAK,OAAO;YACV,OAAO,+BAA+B,CAAC;QACzC,KAAK,YAAY;YACf,OAAO,6BAA6B,CAAC;QACvC,KAAK,QAAQ;YACX,OAAO,oCAAoC,CAAC;QAC9C,KAAK,OAAO;YACV,OAAO,oBAAoB,CAAC;QAC9B;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,UAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAErD,MAAM,iBAAiB,GAAsB,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC3E,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EAAE,GAAG,CAAC,WAAW,CAAC,MAAM;QACnC,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,MAAM;QAC/B,aAAa,EAAE,GAAG,CAAC,aAAa,CAAC,MAAM;QACvC,WAAW,EAAE,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;KAC5C,CAAC,CAAC,CAAC;IAEJ,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;IAC1D,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1D,KAAK,CAAC,IAAI,CACR,aAAa,MAAM,CAAC,QAAQ,CAAC,SAAS,iBAAiB,MAAM,CAAC,QAAQ,CAAC,WAAW,yBAAyB,MAAM,CAAC,QAAQ,CAAC,KAAK,GAAG,CACpI,CAAC;IAEF,IAAI,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CACR,OAAO,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,WAAW,YAAY,GAAG,CAAC,SAAS,YAAY,GAAG,CAAC,aAAa,WAAW,CACrG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM;QACN,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB,iBAAiB;KAClB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA0B,EAC1B,mBAA6B,EAC7B,MAAyB,EACzB,OAAe,EACf,UAAmB,EACnB,UAAkB;IAElB,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE;QACnD,UAAU;QACV,OAAO;QACP,MAAM;QACN,UAAU;QACV,UAAU,EAAE,mBAAmB;KAChC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAkB;QACnC,OAAO,EAAE,IAAI;QACb,MAAM;QACN,OAAO;QACP,SAAS,EAAE,KAAK;QAChB,UAAU;QACV,UAAU,EAAE,mBAAmB;KAChC,CAAC;IAEF,OAAO;QACL,MAAM;QACN,mBAAmB;QACnB,MAAM;QACN,OAAO;QACP,UAAU;QACV,aAAa;QACb,YAAY;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -59,7 +59,7 @@ function buildPackageJsonUpdates(runner, report) {
|
|
|
59
59
|
const devDependencies = {};
|
|
60
60
|
if (runner === "vitest") {
|
|
61
61
|
scripts["test"] = "vitest run";
|
|
62
|
-
devDependencies["vitest"] = "^
|
|
62
|
+
devDependencies["vitest"] = "^4.0.0";
|
|
63
63
|
}
|
|
64
64
|
else {
|
|
65
65
|
scripts["test"] = "jest";
|
package/dist/spec/templates.d.ts
CHANGED
|
@@ -89,6 +89,7 @@ export declare const MilestoneSchema: z.ZodObject<{
|
|
|
89
89
|
verificationCommands: z.ZodArray<z.ZodString, "many">;
|
|
90
90
|
maxContextWindowFit: z.ZodDefault<z.ZodBoolean>;
|
|
91
91
|
dependsOn: z.ZodDefault<z.ZodArray<z.ZodNumber, "many">>;
|
|
92
|
+
testCriteria: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
92
93
|
}, "strip", z.ZodTypeAny, {
|
|
93
94
|
number: number;
|
|
94
95
|
name: string;
|
|
@@ -105,6 +106,7 @@ export declare const MilestoneSchema: z.ZodObject<{
|
|
|
105
106
|
verificationCommands: string[];
|
|
106
107
|
maxContextWindowFit: boolean;
|
|
107
108
|
dependsOn: number[];
|
|
109
|
+
testCriteria: string[];
|
|
108
110
|
}, {
|
|
109
111
|
number: number;
|
|
110
112
|
name: string;
|
|
@@ -121,6 +123,7 @@ export declare const MilestoneSchema: z.ZodObject<{
|
|
|
121
123
|
verificationCommands: string[];
|
|
122
124
|
maxContextWindowFit?: boolean | undefined;
|
|
123
125
|
dependsOn?: number[] | undefined;
|
|
126
|
+
testCriteria?: string[] | undefined;
|
|
124
127
|
}>;
|
|
125
128
|
export declare const PRDSchema: z.ZodObject<{
|
|
126
129
|
project: z.ZodString;
|
|
@@ -166,14 +169,14 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
166
169
|
dependencies: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
167
170
|
existingCode: z.ZodOptional<z.ZodString>;
|
|
168
171
|
}, "strip", z.ZodTypeAny, {
|
|
172
|
+
dependencies?: string[] | undefined;
|
|
169
173
|
projectStructure?: string | undefined;
|
|
170
174
|
keyTypes?: string | undefined;
|
|
171
|
-
dependencies?: string[] | undefined;
|
|
172
175
|
existingCode?: string | undefined;
|
|
173
176
|
}, {
|
|
177
|
+
dependencies?: string[] | undefined;
|
|
174
178
|
projectStructure?: string | undefined;
|
|
175
179
|
keyTypes?: string | undefined;
|
|
176
|
-
dependencies?: string[] | undefined;
|
|
177
180
|
existingCode?: string | undefined;
|
|
178
181
|
}>;
|
|
179
182
|
milestones: z.ZodArray<z.ZodObject<{
|
|
@@ -214,6 +217,7 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
214
217
|
verificationCommands: z.ZodArray<z.ZodString, "many">;
|
|
215
218
|
maxContextWindowFit: z.ZodDefault<z.ZodBoolean>;
|
|
216
219
|
dependsOn: z.ZodDefault<z.ZodArray<z.ZodNumber, "many">>;
|
|
220
|
+
testCriteria: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
217
221
|
}, "strip", z.ZodTypeAny, {
|
|
218
222
|
number: number;
|
|
219
223
|
name: string;
|
|
@@ -230,6 +234,7 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
230
234
|
verificationCommands: string[];
|
|
231
235
|
maxContextWindowFit: boolean;
|
|
232
236
|
dependsOn: number[];
|
|
237
|
+
testCriteria: string[];
|
|
233
238
|
}, {
|
|
234
239
|
number: number;
|
|
235
240
|
name: string;
|
|
@@ -246,6 +251,7 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
246
251
|
verificationCommands: string[];
|
|
247
252
|
maxContextWindowFit?: boolean | undefined;
|
|
248
253
|
dependsOn?: number[] | undefined;
|
|
254
|
+
testCriteria?: string[] | undefined;
|
|
249
255
|
}>, "many">;
|
|
250
256
|
verification: z.ZodObject<{
|
|
251
257
|
perMilestone: z.ZodArray<z.ZodString, "many">;
|
|
@@ -277,6 +283,7 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
277
283
|
verificationCommands: string[];
|
|
278
284
|
maxContextWindowFit: boolean;
|
|
279
285
|
dependsOn: number[];
|
|
286
|
+
testCriteria: string[];
|
|
280
287
|
}[];
|
|
281
288
|
assignedTo: string;
|
|
282
289
|
created: string;
|
|
@@ -294,9 +301,9 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
294
301
|
acceptanceCriteria: string[];
|
|
295
302
|
}[];
|
|
296
303
|
technicalDesign: {
|
|
304
|
+
dependencies?: string[] | undefined;
|
|
297
305
|
projectStructure?: string | undefined;
|
|
298
306
|
keyTypes?: string | undefined;
|
|
299
|
-
dependencies?: string[] | undefined;
|
|
300
307
|
existingCode?: string | undefined;
|
|
301
308
|
};
|
|
302
309
|
verification: {
|
|
@@ -324,6 +331,7 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
324
331
|
verificationCommands: string[];
|
|
325
332
|
maxContextWindowFit?: boolean | undefined;
|
|
326
333
|
dependsOn?: number[] | undefined;
|
|
334
|
+
testCriteria?: string[] | undefined;
|
|
327
335
|
}[];
|
|
328
336
|
assignedTo: string;
|
|
329
337
|
created: string;
|
|
@@ -341,9 +349,9 @@ export declare const PRDSchema: z.ZodObject<{
|
|
|
341
349
|
acceptanceCriteria: string[];
|
|
342
350
|
}[];
|
|
343
351
|
technicalDesign: {
|
|
352
|
+
dependencies?: string[] | undefined;
|
|
344
353
|
projectStructure?: string | undefined;
|
|
345
354
|
keyTypes?: string | undefined;
|
|
346
|
-
dependencies?: string[] | undefined;
|
|
347
355
|
existingCode?: string | undefined;
|
|
348
356
|
};
|
|
349
357
|
verification: {
|
package/dist/spec/templates.js
CHANGED
|
@@ -29,6 +29,7 @@ export const MilestoneSchema = z.object({
|
|
|
29
29
|
verificationCommands: z.array(z.string()),
|
|
30
30
|
maxContextWindowFit: z.boolean().default(true),
|
|
31
31
|
dependsOn: z.array(z.number()).default([]),
|
|
32
|
+
testCriteria: z.array(z.string()).default([]),
|
|
32
33
|
});
|
|
33
34
|
export const PRDSchema = z.object({
|
|
34
35
|
project: z.string(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/spec/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wEAAwE;AAExE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CACxC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KAC3B,CAAC,CACH;CACF,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC;IACnC,oBAAoB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACzC,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC9C,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/spec/templates.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -55,6 +55,15 @@ export interface PipelineResult {
|
|
|
55
55
|
gates: GateResult[];
|
|
56
56
|
report: string;
|
|
57
57
|
}
|
|
58
|
+
/** Testing configuration from .forge.json */
|
|
59
|
+
export interface TestingConfig {
|
|
60
|
+
enforce: boolean;
|
|
61
|
+
runner: "vitest" | "jest" | "none";
|
|
62
|
+
testDir: string;
|
|
63
|
+
sourceDir: string;
|
|
64
|
+
structural: boolean;
|
|
65
|
+
categories: string[];
|
|
66
|
+
}
|
|
58
67
|
/** Configuration from .forge.json */
|
|
59
68
|
export interface ForgeConfig {
|
|
60
69
|
gates: string[];
|
|
@@ -70,6 +79,7 @@ export interface ForgeConfig {
|
|
|
70
79
|
review?: {
|
|
71
80
|
blocking: boolean;
|
|
72
81
|
};
|
|
82
|
+
testing?: TestingConfig;
|
|
73
83
|
}
|
|
74
84
|
/** Verification cache written to .forge/last-verify.json */
|
|
75
85
|
export interface VerifyCache {
|
package/package.json
CHANGED
package/skills/forge-go.md
CHANGED
|
@@ -86,6 +86,17 @@ Print the pre-flight summary:
|
|
|
86
86
|
Ready to execute Milestone 4.
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
**Visual baseline (if visual gate enabled):**
|
|
90
|
+
|
|
91
|
+
If `.forge.json` includes `visual` in gates or specifies a `devServerUrl`, capture before screenshots:
|
|
92
|
+
|
|
93
|
+
1. Start the dev server (from `.forge.json` devServerUrl or `npm run dev`)
|
|
94
|
+
2. Run `npx forge verify --gate visual --before-only` (or manually call the screenshot capture)
|
|
95
|
+
3. Screenshots save to `.forge/screenshots/before/`
|
|
96
|
+
4. Stop the dev server
|
|
97
|
+
|
|
98
|
+
This establishes the visual baseline for regression detection. Skip if no visual gate is configured.
|
|
99
|
+
|
|
89
100
|
### Step 2.5 — Session Isolation (Automatic)
|
|
90
101
|
|
|
91
102
|
The execution engine automatically creates a git worktree for isolated execution. This happens transparently — you don't need to manage it manually.
|
|
@@ -207,20 +218,31 @@ If verification **passes**: proceed to reviewer.
|
|
|
207
218
|
|
|
208
219
|
#### 3f. Reviewer Consensus Protocol
|
|
209
220
|
|
|
221
|
+
**MANDATORY: Do NOT proceed to the next wave or Step 5 until the reviewer has completed its review. This is the real quality gate — not tests.**
|
|
222
|
+
|
|
210
223
|
After mechanical gates pass, engage the reviewer agent:
|
|
211
224
|
|
|
212
225
|
1. **Send diff to reviewer:** Use SendMessage to send the wave's git diff to the reviewer agent, along with:
|
|
213
|
-
- The PRD milestone section
|
|
226
|
+
- The PRD milestone section including:
|
|
227
|
+
- The **Goal** line (what this milestone delivers)
|
|
228
|
+
- All `- [ ]` checklist items from the milestone (these are the acceptance criteria)
|
|
229
|
+
- The **User Stories** section from the PRD (for context on what users expect)
|
|
230
|
+
- The full git diff for the wave
|
|
214
231
|
- CLAUDE.md rules and architecture decisions
|
|
215
232
|
- The list of files changed and their ownership
|
|
233
|
+
- If the visual gate ran, include the screenshot file paths:
|
|
234
|
+
- Before: `.forge/screenshots/before/*.png` (if captured in Step 2)
|
|
235
|
+
- After: `.forge/screenshots/after/*.png`
|
|
236
|
+
- Tell the reviewer: "Use the Read tool to view these screenshots. Compare the rendered UI against the PRD's acceptance criteria and user stories. Check for: layout correctness, missing UI elements, visual regressions from the before state, responsive behavior across viewports (desktop 1280x800, tablet 768x1024, mobile 390x844)."
|
|
216
237
|
|
|
217
238
|
2. **Reviewer analyzes diff:** The reviewer examines the changes for:
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
239
|
+
- Does the code achieve the milestone's **Goal** line?
|
|
240
|
+
- Does it handle the acceptance criteria from the PRD? (Check each `- [ ]` item)
|
|
241
|
+
- Are there logic errors, missing error handling, or dead code paths?
|
|
242
|
+
- Would `npx forge verify` catch this, or is this a semantic issue only a reviewer would find?
|
|
243
|
+
- **If screenshots provided:** Does the rendered UI match the PRD's described behavior? Are there visual issues (broken layouts, missing elements, incorrect spacing) across any viewport?
|
|
222
244
|
|
|
223
|
-
3. **Reviewer sends findings:** The reviewer sends structured findings
|
|
245
|
+
3. **Reviewer sends findings:** The reviewer sends structured findings to the orchestrator (you). **Print each finding to the terminal** so the user sees them:
|
|
224
246
|
```
|
|
225
247
|
Finding: {description}
|
|
226
248
|
Severity: error | warning
|
|
@@ -228,23 +250,12 @@ After mechanical gates pass, engage the reviewer agent:
|
|
|
228
250
|
Line: {number}
|
|
229
251
|
```
|
|
230
252
|
|
|
231
|
-
4. **
|
|
232
|
-
- **Agree** — finding is queued for fix agent
|
|
233
|
-
- **Disagree: {reason}** — builder explains why the finding is incorrect
|
|
234
|
-
- **Alternative: {proposal}** — builder proposes a different approach
|
|
235
|
-
|
|
236
|
-
5. **Round 2 (if disagreement):** The reviewer re-evaluates the builder's reasoning. Either:
|
|
237
|
-
- Accepts the builder's position (finding dropped)
|
|
238
|
-
- Maintains the finding (escalates to executive)
|
|
239
|
-
|
|
240
|
-
6. **Escalation (deadlock after 2 rounds):** The executive (you) reviews both positions and makes the final call.
|
|
241
|
-
|
|
242
|
-
7. **Fix agreed findings:** Spawn a fix agent to address all agreed-upon findings. The fix agent receives:
|
|
253
|
+
4. **Fix findings:** Spawn a fix agent to address all findings. The fix agent receives:
|
|
243
254
|
- The specific findings to fix
|
|
244
255
|
- The files to modify
|
|
245
|
-
- "Fix ONLY the
|
|
256
|
+
- "Fix ONLY the listed findings. Do not refactor or add features."
|
|
246
257
|
|
|
247
|
-
|
|
258
|
+
5. **Re-verify:** Restage files and re-run mechanical gates after fixes.
|
|
248
259
|
|
|
249
260
|
If no findings (or all findings resolved): print wave completion summary and proceed to the next wave.
|
|
250
261
|
|
|
@@ -254,6 +265,7 @@ If no findings (or all findings resolved): print wave completion summary and pro
|
|
|
254
265
|
- agent-1: OK (created file1.ts, file2.ts)
|
|
255
266
|
- agent-2: OK (modified file3.ts)
|
|
256
267
|
- Mechanical verification: PASSED
|
|
268
|
+
- Visual gate: PASSED (3 viewports captured) | SKIPPED (not configured)
|
|
257
269
|
- Reviewer: {N} findings, {M} resolved, 0 outstanding
|
|
258
270
|
|
|
259
271
|
Proceeding to Wave {N+1}...
|
package/skills/forge-setup.md
CHANGED
|
@@ -69,6 +69,66 @@ question: "Tech stack? (e.g., TypeScript, React, Node.js)"
|
|
|
69
69
|
question: "One-line project description?"
|
|
70
70
|
</AskUserQuestion>
|
|
71
71
|
|
|
72
|
+
### Step 3.5 — Interactive Test Planning
|
|
73
|
+
|
|
74
|
+
**Skip this step if "tests" was NOT selected in Step 3.**
|
|
75
|
+
|
|
76
|
+
Run the test analysis engine on the project:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { analyzeForTestPlanning } from 'forge-cc/src/test-scaffold/analyze';
|
|
80
|
+
const analysis = await analyzeForTestPlanning(projectDir);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Present findings to the user:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
## Test Analysis
|
|
87
|
+
|
|
88
|
+
**Framework detected:** {e.g., "Next.js App Router with Vitest"}
|
|
89
|
+
**Coverage summary:** Found {N} source files, {M} test files
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For each category with untested files, ask:
|
|
93
|
+
|
|
94
|
+
<AskUserQuestion>
|
|
95
|
+
question: "Scaffold tests for {category description}? ({N} untested files)"
|
|
96
|
+
options:
|
|
97
|
+
- "Yes — scaffold test stubs for these files"
|
|
98
|
+
- "No — skip this category"
|
|
99
|
+
</AskUserQuestion>
|
|
100
|
+
|
|
101
|
+
If the test runner was NOT already detected from `package.json`, ask:
|
|
102
|
+
|
|
103
|
+
<AskUserQuestion>
|
|
104
|
+
question: "Which test runner?"
|
|
105
|
+
options:
|
|
106
|
+
- "Vitest (Recommended)"
|
|
107
|
+
- "Jest"
|
|
108
|
+
</AskUserQuestion>
|
|
109
|
+
|
|
110
|
+
Ask about structural tests:
|
|
111
|
+
|
|
112
|
+
<AskUserQuestion>
|
|
113
|
+
question: "Include structural tests? (circular import detection, file naming conventions)"
|
|
114
|
+
options:
|
|
115
|
+
- "Yes (Recommended)"
|
|
116
|
+
- "No"
|
|
117
|
+
</AskUserQuestion>
|
|
118
|
+
|
|
119
|
+
Print a summary of what will be scaffolded:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
## Test Scaffold Plan
|
|
123
|
+
|
|
124
|
+
- Config file: {vitest.config.ts / jest.config.ts}
|
|
125
|
+
- Package.json updates: test script, devDependencies
|
|
126
|
+
- Test stubs: {count} across {categories}
|
|
127
|
+
- Structural tests: {Yes / No}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Store the scaffold plan for execution in Step 4. The testing config will be persisted to `.forge.json`.
|
|
131
|
+
|
|
72
132
|
### Step 4 — Create or Update Files
|
|
73
133
|
|
|
74
134
|
Use the template functions from `forge-cc/src/setup/templates.ts` to generate file contents. The templates are:
|
|
@@ -86,6 +146,13 @@ Use the template functions from `forge-cc/src/setup/templates.ts` to generate fi
|
|
|
86
146
|
|
|
87
147
|
Write the actual files using the Write tool. Do not just print them.
|
|
88
148
|
|
|
149
|
+
**If a test scaffold plan exists from Step 3.5**, also execute it now:
|
|
150
|
+
- Write the test runner config file (`vitest.config.ts` or `jest.config.ts`)
|
|
151
|
+
- Write test stub files for each selected category
|
|
152
|
+
- Write structural test files if selected
|
|
153
|
+
- Update `package.json` with test script and devDependencies
|
|
154
|
+
- Persist the `testing` section to `.forge.json`
|
|
155
|
+
|
|
89
156
|
### Step 5 — Patch Global Config
|
|
90
157
|
|
|
91
158
|
Check if `~/.claude/CLAUDE.md` exists:
|
|
@@ -200,6 +267,10 @@ Print a summary of everything that was created or updated:
|
|
|
200
267
|
- .gitignore (forge lines) ✓
|
|
201
268
|
- .claude/settings.local.json ✓ (version-check hook)
|
|
202
269
|
|
|
270
|
+
### Test Planning
|
|
271
|
+
{If tests gate enabled: "Test planning: {N} test stubs scaffolded, {runner} configured, structural tests {included/skipped}"}
|
|
272
|
+
{If tests gate not enabled: "Test planning: skipped (tests gate not enabled)"}
|
|
273
|
+
|
|
203
274
|
### Next Steps
|
|
204
275
|
1. Review the generated `CLAUDE.md` and customize the Code Map section
|
|
205
276
|
2. Run `npx forge verify` to test your gate configuration
|
package/skills/forge-spec.md
CHANGED
|
@@ -193,6 +193,10 @@ The PRD should follow this structure:
|
|
|
193
193
|
|
|
194
194
|
**Milestone sizing check:** Before finalizing, review each milestone against the sizing constraint. Every milestone MUST fit in one agent context window (~4 agents across 2-3 waves max). If any milestone exceeds this, split it into smaller milestones before writing the final PRD. Set `maxContextWindowFit: true` on all milestones — if you cannot make a milestone fit, flag it as `maxContextWindowFit: false` and warn the user.
|
|
195
195
|
|
|
196
|
+
**Test criteria guidance:** Test criteria are optional. Only include them when the milestone produces testable behavior. Verification commands (`npx tsc --noEmit`, `npx forge verify`) are always included automatically — test criteria are for additional functional checks beyond mechanical gates. When including test criteria, make them **functional**, not structural:
|
|
197
|
+
- Good: "CLI `npx forge verify` runs and exits 0", "tsc compiles with no errors", "the new gate produces structured JSON output"
|
|
198
|
+
- Bad: "All new source files must have corresponding test files", "Run npx forge verify --gate tests"
|
|
199
|
+
|
|
196
200
|
Write the final PRD to `.planning/prds/{project-slug}.md`.
|
|
197
201
|
|
|
198
202
|
After writing the PRD file, also:
|