@quinteroac/agents-coding-toolkit 0.1.0-preview → 0.2.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/README.md +29 -15
- package/package.json +14 -4
- package/scaffold/.agents/flow/tmpl_it_000001_progress.example.json +20 -0
- package/scaffold/.agents/skills/execute-refactor-item/tmpl_SKILL.md +59 -0
- package/scaffold/.agents/skills/plan-refactor/tmpl_SKILL.md +89 -9
- package/scaffold/.agents/skills/refine-refactor-plan/tmpl_SKILL.md +30 -0
- package/scaffold/.agents/tmpl_state_rules.md +0 -1
- package/scaffold/schemas/tmpl_prototype-progress.ts +22 -0
- package/scaffold/schemas/tmpl_refactor-execution-progress.ts +16 -0
- package/scaffold/schemas/tmpl_refactor-prd.ts +14 -0
- package/scaffold/schemas/tmpl_state.ts +1 -0
- package/scaffold/schemas/tmpl_test-execution-progress.ts +17 -0
- package/schemas/issues.ts +19 -0
- package/schemas/prototype-progress.ts +22 -0
- package/schemas/refactor-execution-progress.ts +16 -0
- package/schemas/refactor-prd.ts +14 -0
- package/schemas/state.test.ts +58 -0
- package/schemas/state.ts +1 -0
- package/schemas/test-execution-progress.ts +17 -0
- package/schemas/test-plan.test.ts +1 -1
- package/schemas/validate-progress.ts +1 -1
- package/schemas/validate-state.ts +1 -1
- package/src/cli.test.ts +57 -0
- package/src/cli.ts +227 -58
- package/src/commands/approve-project-context.ts +13 -6
- package/src/commands/approve-prototype.test.ts +427 -0
- package/src/commands/approve-prototype.ts +185 -0
- package/src/commands/approve-refactor-plan.test.ts +254 -0
- package/src/commands/approve-refactor-plan.ts +200 -0
- package/src/commands/approve-requirement.test.ts +224 -0
- package/src/commands/approve-requirement.ts +75 -16
- package/src/commands/approve-test-plan.test.ts +2 -2
- package/src/commands/approve-test-plan.ts +21 -7
- package/src/commands/create-issue.test.ts +2 -2
- package/src/commands/create-project-context.ts +31 -25
- package/src/commands/create-prototype.test.ts +488 -18
- package/src/commands/create-prototype.ts +185 -63
- package/src/commands/create-test-plan.ts +8 -6
- package/src/commands/define-refactor-plan.test.ts +208 -0
- package/src/commands/define-refactor-plan.ts +96 -0
- package/src/commands/define-requirement.ts +15 -9
- package/src/commands/execute-automated-fix.test.ts +78 -33
- package/src/commands/execute-automated-fix.ts +34 -101
- package/src/commands/execute-refactor.test.ts +954 -0
- package/src/commands/execute-refactor.ts +332 -0
- package/src/commands/execute-test-plan.test.ts +24 -16
- package/src/commands/execute-test-plan.ts +29 -55
- package/src/commands/flow-config.ts +79 -0
- package/src/commands/flow.test.ts +755 -0
- package/src/commands/flow.ts +405 -0
- package/src/commands/refine-project-context.ts +9 -7
- package/src/commands/refine-refactor-plan.test.ts +210 -0
- package/src/commands/refine-refactor-plan.ts +95 -0
- package/src/commands/refine-requirement.ts +9 -6
- package/src/commands/refine-test-plan.test.ts +2 -2
- package/src/commands/refine-test-plan.ts +9 -6
- package/src/commands/start-iteration.test.ts +52 -0
- package/src/commands/start-iteration.ts +5 -0
- package/src/commands/write-json.ts +102 -97
- package/src/flow-cli.test.ts +18 -0
- package/src/force-flag.test.ts +144 -0
- package/src/guardrail.test.ts +411 -0
- package/src/guardrail.ts +82 -0
- package/src/install.test.ts +7 -5
- package/src/pack.test.ts +2 -1
- package/src/progress-utils.ts +34 -0
- package/src/readline.ts +23 -0
- package/src/write-json-artifact.ts +33 -0
- package/scaffold/.agents/flow/tmpl_README.md +0 -7
- package/scaffold/.agents/flow/tmpl_iteration_close_checklist.example.md +0 -11
- package/schemas/test-plan.ts +0 -20
package/src/cli.test.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
describe("US-005: execute refactor CLI routing and help text", () => {
|
|
6
|
+
// AC01: cli.ts routes `execute refactor` to runExecuteRefactor handler
|
|
7
|
+
test("cli.ts routes execute refactor subcommand to runExecuteRefactor", async () => {
|
|
8
|
+
const source = await readFile(join(import.meta.dir, "cli.ts"), "utf8");
|
|
9
|
+
expect(source).toContain('import { runExecuteRefactor } from "./commands/execute-refactor"');
|
|
10
|
+
expect(source).toContain('if (subcommand === "refactor")');
|
|
11
|
+
expect(source).toContain("await runExecuteRefactor({ provider, force })");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// AC02: printUsage includes `execute refactor --agent <provider>` with a one-line description
|
|
15
|
+
test("printUsage includes execute refactor --agent <provider> with one-line description", async () => {
|
|
16
|
+
const source = await readFile(join(import.meta.dir, "cli.ts"), "utf8");
|
|
17
|
+
expect(source).toContain("execute refactor --agent <provider>");
|
|
18
|
+
expect(source).toContain("Execute approved refactor items via agent in order");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// AC03: Unknown options after --agent <provider> cause clear error and exit code 1
|
|
22
|
+
test("unknown options after --agent <provider> cause clear error message and exit code 1", async () => {
|
|
23
|
+
const cliPath = join(import.meta.dir, "cli.ts");
|
|
24
|
+
const proc = Bun.spawn(
|
|
25
|
+
["bun", cliPath, "execute", "refactor", "--agent", "claude", "--unknown-flag"],
|
|
26
|
+
{
|
|
27
|
+
stdout: "pipe",
|
|
28
|
+
stderr: "pipe",
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
const exitCode = await proc.exited;
|
|
32
|
+
const stderrText = await new Response(proc.stderr).text();
|
|
33
|
+
|
|
34
|
+
expect(exitCode).toBe(1);
|
|
35
|
+
expect(stderrText).toContain("Unknown option(s) for execute refactor");
|
|
36
|
+
expect(stderrText).toContain("--unknown-flag");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// AC03: Multiple unknown options after --agent <provider> are all reported
|
|
40
|
+
test("multiple unknown options after --agent <provider> are all listed in error message", async () => {
|
|
41
|
+
const cliPath = join(import.meta.dir, "cli.ts");
|
|
42
|
+
const proc = Bun.spawn(
|
|
43
|
+
["bun", cliPath, "execute", "refactor", "--agent", "claude", "--foo", "--bar"],
|
|
44
|
+
{
|
|
45
|
+
stdout: "pipe",
|
|
46
|
+
stderr: "pipe",
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
const exitCode = await proc.exited;
|
|
50
|
+
const stderrText = await new Response(proc.stderr).text();
|
|
51
|
+
|
|
52
|
+
expect(exitCode).toBe(1);
|
|
53
|
+
expect(stderrText).toContain("Unknown option(s) for execute refactor");
|
|
54
|
+
expect(stderrText).toContain("--foo");
|
|
55
|
+
expect(stderrText).toContain("--bar");
|
|
56
|
+
});
|
|
57
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { parseAgentArg } from "./agent";
|
|
4
|
+
import { parseAgentArg, parseProvider, type AgentProvider } from "./agent";
|
|
5
|
+
import { GuardrailAbortError } from "./guardrail";
|
|
5
6
|
import { runApproveProjectContext } from "./commands/approve-project-context";
|
|
7
|
+
import { runApprovePrototype } from "./commands/approve-prototype";
|
|
8
|
+
import { runApproveRefactorPlan } from "./commands/approve-refactor-plan";
|
|
6
9
|
import { runApproveRequirement } from "./commands/approve-requirement";
|
|
7
10
|
import { runApproveTestPlan } from "./commands/approve-test-plan";
|
|
8
11
|
import { runCreateIssue, runCreateIssueFromTestReport } from "./commands/create-issue";
|
|
9
12
|
import { runCreateProjectContext } from "./commands/create-project-context";
|
|
10
13
|
import { runCreatePrototype } from "./commands/create-prototype";
|
|
11
14
|
import { runCreateTestPlan } from "./commands/create-test-plan";
|
|
15
|
+
import { runDefineRefactorPlan } from "./commands/define-refactor-plan";
|
|
12
16
|
import { runDefineRequirement } from "./commands/define-requirement";
|
|
13
17
|
import { runDestroy } from "./commands/destroy";
|
|
14
18
|
import { runExecuteAutomatedFix } from "./commands/execute-automated-fix";
|
|
15
19
|
import { runExecuteManualFix } from "./commands/execute-manual-fix";
|
|
20
|
+
import { runExecuteRefactor } from "./commands/execute-refactor";
|
|
16
21
|
import { runExecuteTestPlan } from "./commands/execute-test-plan";
|
|
22
|
+
import { runFlow } from "./commands/flow";
|
|
17
23
|
import { runInit } from "./commands/init";
|
|
18
24
|
import { runRefineProjectContext } from "./commands/refine-project-context";
|
|
25
|
+
import { runRefineRefactorPlan } from "./commands/refine-refactor-plan";
|
|
19
26
|
import { runRefineRequirement } from "./commands/refine-requirement";
|
|
20
27
|
import { runRefineTestPlan } from "./commands/refine-test-plan";
|
|
21
28
|
import { runStartIteration } from "./commands/start-iteration";
|
|
@@ -76,18 +83,43 @@ function parseOptionalIntegerFlag(
|
|
|
76
83
|
return { value: parsed, remainingArgs };
|
|
77
84
|
}
|
|
78
85
|
|
|
86
|
+
function parseForce(args: string[]): { force: boolean; remainingArgs: string[] } {
|
|
87
|
+
const force = args.includes("--force");
|
|
88
|
+
return {
|
|
89
|
+
force,
|
|
90
|
+
remainingArgs: args.filter((arg) => arg !== "--force"),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseOptionalAgentArg(args: string[]): {
|
|
95
|
+
provider: AgentProvider | undefined;
|
|
96
|
+
remainingArgs: string[];
|
|
97
|
+
} {
|
|
98
|
+
const { value, remainingArgs } = extractFlagValue(args, "--agent");
|
|
99
|
+
if (value === null) {
|
|
100
|
+
return { provider: undefined, remainingArgs };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
provider: parseProvider(value),
|
|
105
|
+
remainingArgs,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
79
109
|
function printUsage() {
|
|
80
110
|
console.log(`Usage: nvst <command> [options]
|
|
81
111
|
|
|
82
112
|
Commands:
|
|
83
113
|
init Initialize toolkit files in the current directory
|
|
84
114
|
start iteration Start a new iteration (archives previous if exists)
|
|
85
|
-
create project-context --agent <provider> [--mode strict|yolo]
|
|
115
|
+
create project-context --agent <provider> [--mode strict|yolo] [--force]
|
|
86
116
|
Generate/update .agents/PROJECT_CONTEXT.md via agent
|
|
87
117
|
create test-plan --agent <provider> [--force]
|
|
88
118
|
Generate test plan document for current iteration
|
|
89
119
|
create prototype --agent <provider> [--iterations <N>] [--retry-on-fail <N>] [--stop-on-critical]
|
|
90
120
|
Initialize prototype build for current iteration
|
|
121
|
+
flow [--agent <provider>] [--force]
|
|
122
|
+
Run the next pending flow step(s) until an approval gate or completion
|
|
91
123
|
create issue --agent <provider>
|
|
92
124
|
Create issues interactively via agent
|
|
93
125
|
create issue --test-execution-report
|
|
@@ -96,20 +128,30 @@ Commands:
|
|
|
96
128
|
Mark project context as approved
|
|
97
129
|
approve test-plan
|
|
98
130
|
Mark test plan as approved and generate structured TP JSON
|
|
131
|
+
approve prototype
|
|
132
|
+
Stage and commit all pending changes for current iteration
|
|
133
|
+
approve refactor-plan
|
|
134
|
+
Mark refactor plan as approved and generate structured refactor PRD JSON
|
|
99
135
|
refine project-context --agent <provider> [--challenge]
|
|
100
136
|
Refine project context via agent (editor or challenge mode)
|
|
101
|
-
define requirement --agent <provider>
|
|
137
|
+
define requirement --agent <provider> [--force]
|
|
102
138
|
Create requirement document via agent
|
|
103
|
-
|
|
139
|
+
define refactor-plan --agent <provider> [--force]
|
|
140
|
+
Create refactor plan document via agent
|
|
141
|
+
refine requirement --agent <provider> [--challenge] [--force]
|
|
104
142
|
Refine requirement document via agent
|
|
105
|
-
refine test-plan --agent <provider> [--challenge]
|
|
143
|
+
refine test-plan --agent <provider> [--challenge] [--force]
|
|
106
144
|
Refine test plan document via agent
|
|
107
|
-
|
|
145
|
+
refine refactor-plan --agent <provider> [--challenge] [--force]
|
|
146
|
+
Refine refactor plan document via agent
|
|
147
|
+
execute test-plan --agent <provider> [--force]
|
|
108
148
|
Execute approved structured test-plan JSON via agent
|
|
109
149
|
execute automated-fix --agent <provider> [--iterations <N>] [--retry-on-fail <N>]
|
|
110
150
|
Attempt automated fixes for open issues in current iteration
|
|
111
151
|
execute manual-fix --agent <provider>
|
|
112
152
|
Find manual-fix issues for current iteration and confirm execution
|
|
153
|
+
execute refactor --agent <provider> [--force]
|
|
154
|
+
Execute approved refactor items via agent in order
|
|
113
155
|
approve requirement
|
|
114
156
|
Mark requirement definition as approved
|
|
115
157
|
write-json --schema <name> --out <path> [--data '<json>']
|
|
@@ -122,7 +164,7 @@ Options:
|
|
|
122
164
|
--iterations Maximum prototype passes (integer >= 1)
|
|
123
165
|
--retry-on-fail Retry attempts per failed story (integer >= 0)
|
|
124
166
|
--stop-on-critical Stop execution after critical failures
|
|
125
|
-
--force
|
|
167
|
+
--force Bypass flow guardrail confirmation (and overwrite test-plan output)
|
|
126
168
|
--challenge Run refine in challenger mode
|
|
127
169
|
--clean When used with destroy, also removes .agents/flow/archived
|
|
128
170
|
-h, --help Show this help message
|
|
@@ -153,8 +195,9 @@ async function main() {
|
|
|
153
195
|
}
|
|
154
196
|
|
|
155
197
|
if (command === "init") {
|
|
156
|
-
|
|
157
|
-
|
|
198
|
+
const { remainingArgs: unknownArgs } = parseForce(args);
|
|
199
|
+
if (unknownArgs.length > 0) {
|
|
200
|
+
console.error(`Unknown option(s) for init: ${unknownArgs.join(" ")}`);
|
|
158
201
|
printUsage();
|
|
159
202
|
process.exitCode = 1;
|
|
160
203
|
return;
|
|
@@ -164,8 +207,9 @@ async function main() {
|
|
|
164
207
|
}
|
|
165
208
|
|
|
166
209
|
if (command === "destroy") {
|
|
167
|
-
const
|
|
168
|
-
const
|
|
210
|
+
const { remainingArgs: argsWithoutForce } = parseForce(args);
|
|
211
|
+
const clean = argsWithoutForce.includes("--clean");
|
|
212
|
+
const unknownArgs = argsWithoutForce.filter((arg) => arg !== "--clean");
|
|
169
213
|
if (unknownArgs.length > 0) {
|
|
170
214
|
console.error(`Unknown option(s) for destroy: ${unknownArgs.join(" ")}`);
|
|
171
215
|
printUsage();
|
|
@@ -177,7 +221,8 @@ async function main() {
|
|
|
177
221
|
}
|
|
178
222
|
|
|
179
223
|
if (command === "start") {
|
|
180
|
-
|
|
224
|
+
const { remainingArgs: argsWithoutForce } = parseForce(args);
|
|
225
|
+
if (argsWithoutForce[0] !== "iteration" || argsWithoutForce.length !== 1) {
|
|
181
226
|
console.error(`Usage for start: nvst start iteration`);
|
|
182
227
|
printUsage();
|
|
183
228
|
process.exitCode = 1;
|
|
@@ -187,6 +232,26 @@ async function main() {
|
|
|
187
232
|
return;
|
|
188
233
|
}
|
|
189
234
|
|
|
235
|
+
if (command === "flow") {
|
|
236
|
+
try {
|
|
237
|
+
const { provider, remainingArgs: postAgentArgs } = parseOptionalAgentArg(args);
|
|
238
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
239
|
+
if (postForceArgs.length > 0) {
|
|
240
|
+
console.error(`Unknown option(s) for flow: ${postForceArgs.join(" ")}`);
|
|
241
|
+
printUsage();
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
await runFlow({ provider, force });
|
|
246
|
+
return;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
249
|
+
printUsage();
|
|
250
|
+
process.exitCode = 1;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
190
255
|
if (command === "create") {
|
|
191
256
|
if (args.length === 0) {
|
|
192
257
|
console.error(
|
|
@@ -203,15 +268,16 @@ async function main() {
|
|
|
203
268
|
try {
|
|
204
269
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
205
270
|
const { mode, remainingArgs: postModeArgs } = parseMode(postAgentArgs);
|
|
271
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postModeArgs);
|
|
206
272
|
|
|
207
|
-
if (
|
|
208
|
-
console.error(`Unknown option(s) for create project-context: ${
|
|
273
|
+
if (postForceArgs.length > 0) {
|
|
274
|
+
console.error(`Unknown option(s) for create project-context: ${postForceArgs.join(" ")}`);
|
|
209
275
|
printUsage();
|
|
210
276
|
process.exitCode = 1;
|
|
211
277
|
return;
|
|
212
278
|
}
|
|
213
279
|
|
|
214
|
-
await runCreateProjectContext({ provider, mode });
|
|
280
|
+
await runCreateProjectContext({ provider, mode, force });
|
|
215
281
|
return;
|
|
216
282
|
} catch (error) {
|
|
217
283
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -234,8 +300,9 @@ async function main() {
|
|
|
234
300
|
remainingArgs: postRetryArgs,
|
|
235
301
|
} = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
|
|
236
302
|
|
|
237
|
-
const
|
|
238
|
-
const
|
|
303
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postRetryArgs);
|
|
304
|
+
const stopOnCritical = postForceArgs.includes("--stop-on-critical");
|
|
305
|
+
const unknownArgs = postForceArgs.filter((arg) => arg !== "--stop-on-critical");
|
|
239
306
|
if (unknownArgs.length > 0) {
|
|
240
307
|
console.error(`Unknown option(s) for create prototype: ${unknownArgs.join(" ")}`);
|
|
241
308
|
printUsage();
|
|
@@ -243,7 +310,7 @@ async function main() {
|
|
|
243
310
|
return;
|
|
244
311
|
}
|
|
245
312
|
|
|
246
|
-
await runCreatePrototype({ provider, iterations, retryOnFail, stopOnCritical });
|
|
313
|
+
await runCreatePrototype({ provider, iterations, retryOnFail, stopOnCritical, force });
|
|
247
314
|
return;
|
|
248
315
|
} catch (error) {
|
|
249
316
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -278,9 +345,10 @@ async function main() {
|
|
|
278
345
|
|
|
279
346
|
if (subcommand === "issue") {
|
|
280
347
|
const subArgs = args.slice(1);
|
|
348
|
+
const { remainingArgs: subArgsWithoutForce } = parseForce(subArgs);
|
|
281
349
|
|
|
282
350
|
// Check for --help before parsing
|
|
283
|
-
if (
|
|
351
|
+
if (subArgsWithoutForce.includes("--help") || subArgsWithoutForce.includes("-h")) {
|
|
284
352
|
console.log(`Usage for create issue:
|
|
285
353
|
nvst create issue --agent <provider> Create issues interactively via agent
|
|
286
354
|
nvst create issue --test-execution-report Derive issues from test execution results
|
|
@@ -292,8 +360,8 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
292
360
|
|
|
293
361
|
try {
|
|
294
362
|
// Check for --test-execution-report flag
|
|
295
|
-
if (
|
|
296
|
-
const remaining =
|
|
363
|
+
if (subArgsWithoutForce.includes("--test-execution-report")) {
|
|
364
|
+
const remaining = subArgsWithoutForce.filter((a) => a !== "--test-execution-report");
|
|
297
365
|
if (remaining.length > 0) {
|
|
298
366
|
console.error(`Unknown option(s) for create issue --test-execution-report: ${remaining.join(" ")}`);
|
|
299
367
|
printUsage();
|
|
@@ -304,7 +372,7 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
304
372
|
return;
|
|
305
373
|
}
|
|
306
374
|
|
|
307
|
-
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(
|
|
375
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(subArgsWithoutForce);
|
|
308
376
|
|
|
309
377
|
if (postAgentArgs.length > 0) {
|
|
310
378
|
console.error(`Unknown option(s) for create issue: ${postAgentArgs.join(" ")}`);
|
|
@@ -330,37 +398,67 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
330
398
|
}
|
|
331
399
|
|
|
332
400
|
if (command === "define") {
|
|
333
|
-
if (args.length === 0
|
|
334
|
-
console.error(`Usage for define: nvst define requirement --agent <provider>`);
|
|
401
|
+
if (args.length === 0) {
|
|
402
|
+
console.error(`Usage for define: nvst define <requirement|refactor-plan> --agent <provider>`);
|
|
335
403
|
printUsage();
|
|
336
404
|
process.exitCode = 1;
|
|
337
405
|
return;
|
|
338
406
|
}
|
|
339
407
|
|
|
340
|
-
|
|
341
|
-
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
408
|
+
const subcommand = args[0];
|
|
342
409
|
|
|
343
|
-
|
|
344
|
-
|
|
410
|
+
if (subcommand === "requirement") {
|
|
411
|
+
try {
|
|
412
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
413
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
414
|
+
if (postForceArgs.length > 0) {
|
|
415
|
+
console.error(`Unknown option(s) for define requirement: ${postForceArgs.join(" ")}`);
|
|
416
|
+
printUsage();
|
|
417
|
+
process.exitCode = 1;
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
await runDefineRequirement({ provider, force });
|
|
422
|
+
return;
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
345
425
|
printUsage();
|
|
346
426
|
process.exitCode = 1;
|
|
347
427
|
return;
|
|
348
428
|
}
|
|
429
|
+
}
|
|
349
430
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
431
|
+
if (subcommand === "refactor-plan") {
|
|
432
|
+
try {
|
|
433
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
434
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
435
|
+
if (postForceArgs.length > 0) {
|
|
436
|
+
console.error(`Unknown option(s) for define refactor-plan: ${postForceArgs.join(" ")}`);
|
|
437
|
+
printUsage();
|
|
438
|
+
process.exitCode = 1;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
await runDefineRefactorPlan({ provider, force });
|
|
443
|
+
return;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
446
|
+
printUsage();
|
|
447
|
+
process.exitCode = 1;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
357
450
|
}
|
|
451
|
+
|
|
452
|
+
console.error(`Unknown define subcommand: ${subcommand}`);
|
|
453
|
+
printUsage();
|
|
454
|
+
process.exitCode = 1;
|
|
455
|
+
return;
|
|
358
456
|
}
|
|
359
457
|
|
|
360
458
|
if (command === "refine") {
|
|
361
459
|
if (args.length === 0) {
|
|
362
460
|
console.error(
|
|
363
|
-
`Usage for refine: nvst refine <requirement|project-context|test-plan> --agent <provider> [--challenge]`,
|
|
461
|
+
`Usage for refine: nvst refine <requirement|project-context|test-plan|refactor-plan> --agent <provider> [--challenge]`,
|
|
364
462
|
);
|
|
365
463
|
printUsage();
|
|
366
464
|
process.exitCode = 1;
|
|
@@ -372,8 +470,9 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
372
470
|
if (subcommand === "requirement") {
|
|
373
471
|
try {
|
|
374
472
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
375
|
-
const
|
|
376
|
-
const
|
|
473
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
474
|
+
const challenge = postForceArgs.includes("--challenge");
|
|
475
|
+
const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
|
|
377
476
|
|
|
378
477
|
if (unknownArgs.length > 0) {
|
|
379
478
|
console.error(`Unknown option(s) for refine requirement: ${unknownArgs.join(" ")}`);
|
|
@@ -382,7 +481,7 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
382
481
|
return;
|
|
383
482
|
}
|
|
384
483
|
|
|
385
|
-
await runRefineRequirement({ provider, challenge });
|
|
484
|
+
await runRefineRequirement({ provider, challenge, force });
|
|
386
485
|
return;
|
|
387
486
|
} catch (error) {
|
|
388
487
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -395,8 +494,9 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
395
494
|
if (subcommand === "project-context") {
|
|
396
495
|
try {
|
|
397
496
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
398
|
-
const
|
|
399
|
-
const
|
|
497
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
498
|
+
const challenge = postForceArgs.includes("--challenge");
|
|
499
|
+
const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
|
|
400
500
|
|
|
401
501
|
if (unknownArgs.length > 0) {
|
|
402
502
|
console.error(
|
|
@@ -407,7 +507,7 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
407
507
|
return;
|
|
408
508
|
}
|
|
409
509
|
|
|
410
|
-
await runRefineProjectContext({ provider, challenge });
|
|
510
|
+
await runRefineProjectContext({ provider, challenge, force });
|
|
411
511
|
return;
|
|
412
512
|
} catch (error) {
|
|
413
513
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -420,8 +520,9 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
420
520
|
if (subcommand === "test-plan") {
|
|
421
521
|
try {
|
|
422
522
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
423
|
-
const
|
|
424
|
-
const
|
|
523
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
524
|
+
const challenge = postForceArgs.includes("--challenge");
|
|
525
|
+
const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
|
|
425
526
|
|
|
426
527
|
if (unknownArgs.length > 0) {
|
|
427
528
|
console.error(`Unknown option(s) for refine test-plan: ${unknownArgs.join(" ")}`);
|
|
@@ -430,7 +531,31 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
430
531
|
return;
|
|
431
532
|
}
|
|
432
533
|
|
|
433
|
-
await runRefineTestPlan({ provider, challenge });
|
|
534
|
+
await runRefineTestPlan({ provider, challenge, force });
|
|
535
|
+
return;
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
538
|
+
printUsage();
|
|
539
|
+
process.exitCode = 1;
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (subcommand === "refactor-plan") {
|
|
545
|
+
try {
|
|
546
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
547
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
548
|
+
const challenge = postForceArgs.includes("--challenge");
|
|
549
|
+
const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
|
|
550
|
+
|
|
551
|
+
if (unknownArgs.length > 0) {
|
|
552
|
+
console.error(`Unknown option(s) for refine refactor-plan: ${unknownArgs.join(" ")}`);
|
|
553
|
+
printUsage();
|
|
554
|
+
process.exitCode = 1;
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
await runRefineRefactorPlan({ provider, challenge, force });
|
|
434
559
|
return;
|
|
435
560
|
} catch (error) {
|
|
436
561
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -447,27 +572,44 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
447
572
|
}
|
|
448
573
|
|
|
449
574
|
if (command === "approve") {
|
|
450
|
-
if (args.length
|
|
451
|
-
console.error(`Usage for approve: nvst approve <requirement|project-context|test-plan>`);
|
|
575
|
+
if (args.length === 0) {
|
|
576
|
+
console.error(`Usage for approve: nvst approve <requirement|project-context|test-plan|prototype|refactor-plan>`);
|
|
452
577
|
printUsage();
|
|
453
578
|
process.exitCode = 1;
|
|
454
579
|
return;
|
|
455
580
|
}
|
|
456
581
|
|
|
457
582
|
const subcommand = args[0];
|
|
583
|
+
const { force, remainingArgs: unknownArgs } = parseForce(args.slice(1));
|
|
584
|
+
if (unknownArgs.length > 0) {
|
|
585
|
+
console.error(`Unknown option(s) for approve ${subcommand}: ${unknownArgs.join(" ")}`);
|
|
586
|
+
printUsage();
|
|
587
|
+
process.exitCode = 1;
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
458
590
|
|
|
459
591
|
if (subcommand === "requirement") {
|
|
460
|
-
await runApproveRequirement();
|
|
592
|
+
await runApproveRequirement({ force });
|
|
461
593
|
return;
|
|
462
594
|
}
|
|
463
595
|
|
|
464
596
|
if (subcommand === "project-context") {
|
|
465
|
-
await runApproveProjectContext();
|
|
597
|
+
await runApproveProjectContext({ force });
|
|
466
598
|
return;
|
|
467
599
|
}
|
|
468
600
|
|
|
469
601
|
if (subcommand === "test-plan") {
|
|
470
|
-
await runApproveTestPlan();
|
|
602
|
+
await runApproveTestPlan({ force });
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (subcommand === "prototype") {
|
|
607
|
+
await runApprovePrototype({ force });
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (subcommand === "refactor-plan") {
|
|
612
|
+
await runApproveRefactorPlan({ force });
|
|
471
613
|
return;
|
|
472
614
|
}
|
|
473
615
|
|
|
@@ -490,14 +632,15 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
490
632
|
if (subcommand === "test-plan") {
|
|
491
633
|
try {
|
|
492
634
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
493
|
-
|
|
494
|
-
|
|
635
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
636
|
+
if (postForceArgs.length > 0) {
|
|
637
|
+
console.error(`Unknown option(s) for execute test-plan: ${postForceArgs.join(" ")}`);
|
|
495
638
|
printUsage();
|
|
496
639
|
process.exitCode = 1;
|
|
497
640
|
return;
|
|
498
641
|
}
|
|
499
642
|
|
|
500
|
-
await runExecuteTestPlan({ provider });
|
|
643
|
+
await runExecuteTestPlan({ provider, force });
|
|
501
644
|
return;
|
|
502
645
|
} catch (error) {
|
|
503
646
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -516,11 +659,11 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
516
659
|
} = parseOptionalIntegerFlag(postAgentArgs, "--iterations", 1);
|
|
517
660
|
const {
|
|
518
661
|
value: retryOnFail,
|
|
519
|
-
remainingArgs:
|
|
662
|
+
remainingArgs: unknownArgs,
|
|
520
663
|
} = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
|
|
521
664
|
|
|
522
|
-
if (
|
|
523
|
-
console.error(`Unknown option(s) for execute automated-fix: ${
|
|
665
|
+
if (unknownArgs.length > 0) {
|
|
666
|
+
console.error(`Unknown option(s) for execute automated-fix: ${unknownArgs.join(" ")}`);
|
|
524
667
|
printUsage();
|
|
525
668
|
process.exitCode = 1;
|
|
526
669
|
return;
|
|
@@ -539,8 +682,9 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
539
682
|
if (subcommand === "manual-fix") {
|
|
540
683
|
try {
|
|
541
684
|
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
542
|
-
|
|
543
|
-
|
|
685
|
+
const { remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
686
|
+
if (postForceArgs.length > 0) {
|
|
687
|
+
console.error(`Unknown option(s) for execute manual-fix: ${postForceArgs.join(" ")}`);
|
|
544
688
|
printUsage();
|
|
545
689
|
process.exitCode = 1;
|
|
546
690
|
return;
|
|
@@ -556,6 +700,27 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
556
700
|
}
|
|
557
701
|
}
|
|
558
702
|
|
|
703
|
+
if (subcommand === "refactor") {
|
|
704
|
+
try {
|
|
705
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
706
|
+
const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
|
|
707
|
+
if (postForceArgs.length > 0) {
|
|
708
|
+
console.error(`Unknown option(s) for execute refactor: ${postForceArgs.join(" ")}`);
|
|
709
|
+
printUsage();
|
|
710
|
+
process.exitCode = 1;
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
await runExecuteRefactor({ provider, force });
|
|
715
|
+
return;
|
|
716
|
+
} catch (error) {
|
|
717
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
718
|
+
printUsage();
|
|
719
|
+
process.exitCode = 1;
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
559
724
|
console.error(`Unknown execute subcommand: ${subcommand}`);
|
|
560
725
|
printUsage();
|
|
561
726
|
process.exitCode = 1;
|
|
@@ -573,6 +738,10 @@ Providers: claude, codex, gemini, cursor`);
|
|
|
573
738
|
}
|
|
574
739
|
|
|
575
740
|
main().catch((error) => {
|
|
741
|
+
if (error instanceof GuardrailAbortError) {
|
|
742
|
+
// exitCode already set and "Aborted." already written by assertGuardrail
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
576
745
|
console.error("nvst failed:", error);
|
|
577
746
|
process.exitCode = 1;
|
|
578
747
|
});
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
|
|
3
|
+
import { assertGuardrail } from "../guardrail";
|
|
3
4
|
import { exists, readState, writeState } from "../state";
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
+
export interface ApproveProjectContextOptions {
|
|
7
|
+
force?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function runApproveProjectContext(opts: ApproveProjectContextOptions = {}): Promise<void> {
|
|
11
|
+
const { force = false } = opts;
|
|
6
12
|
const projectRoot = process.cwd();
|
|
7
13
|
const state = await readState(projectRoot);
|
|
8
14
|
|
|
9
15
|
// US-002-AC01: Validate status is pending_approval
|
|
10
16
|
const projectContext = state.phases.prototype.project_context;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
await assertGuardrail(
|
|
18
|
+
state,
|
|
19
|
+
projectContext.status !== "pending_approval",
|
|
20
|
+
`Cannot approve project context from status '${projectContext.status}'. Expected pending_approval.`,
|
|
21
|
+
{ force },
|
|
22
|
+
);
|
|
16
23
|
|
|
17
24
|
// US-002-AC01: Validate project context file exists
|
|
18
25
|
const contextFile = projectContext.file;
|