@quinteroac/agents-coding-toolkit 0.1.0-preview → 0.1.1-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/package.json +13 -4
  3. package/scaffold/.agents/skills/execute-refactor-item/tmpl_SKILL.md +59 -0
  4. package/scaffold/.agents/skills/plan-refactor/tmpl_SKILL.md +89 -9
  5. package/scaffold/.agents/skills/refine-refactor-plan/tmpl_SKILL.md +30 -0
  6. package/scaffold/.agents/tmpl_state_rules.md +0 -1
  7. package/scaffold/schemas/tmpl_refactor-execution-progress.ts +16 -0
  8. package/scaffold/schemas/tmpl_refactor-prd.ts +14 -0
  9. package/scaffold/schemas/tmpl_state.ts +1 -0
  10. package/schemas/refactor-execution-progress.ts +16 -0
  11. package/schemas/refactor-prd.ts +14 -0
  12. package/schemas/state.test.ts +58 -0
  13. package/schemas/state.ts +1 -0
  14. package/schemas/test-plan.test.ts +1 -1
  15. package/src/cli.test.ts +57 -0
  16. package/src/cli.ts +180 -56
  17. package/src/commands/approve-project-context.ts +13 -6
  18. package/src/commands/approve-refactor-plan.test.ts +254 -0
  19. package/src/commands/approve-refactor-plan.ts +200 -0
  20. package/src/commands/approve-requirement.test.ts +224 -0
  21. package/src/commands/approve-requirement.ts +75 -16
  22. package/src/commands/approve-test-plan.test.ts +2 -2
  23. package/src/commands/approve-test-plan.ts +21 -7
  24. package/src/commands/create-issue.test.ts +2 -2
  25. package/src/commands/create-project-context.ts +31 -25
  26. package/src/commands/create-prototype.test.ts +31 -13
  27. package/src/commands/create-prototype.ts +17 -7
  28. package/src/commands/create-test-plan.ts +8 -6
  29. package/src/commands/define-refactor-plan.test.ts +208 -0
  30. package/src/commands/define-refactor-plan.ts +96 -0
  31. package/src/commands/define-requirement.ts +15 -9
  32. package/src/commands/execute-refactor.test.ts +954 -0
  33. package/src/commands/execute-refactor.ts +336 -0
  34. package/src/commands/execute-test-plan.test.ts +9 -2
  35. package/src/commands/execute-test-plan.ts +13 -6
  36. package/src/commands/refine-project-context.ts +9 -7
  37. package/src/commands/refine-refactor-plan.test.ts +210 -0
  38. package/src/commands/refine-refactor-plan.ts +95 -0
  39. package/src/commands/refine-requirement.ts +9 -6
  40. package/src/commands/refine-test-plan.test.ts +2 -2
  41. package/src/commands/refine-test-plan.ts +9 -6
  42. package/src/commands/write-json.ts +102 -97
  43. package/src/force-flag.test.ts +144 -0
  44. package/src/guardrail.test.ts +411 -0
  45. package/src/guardrail.ts +104 -0
  46. package/src/install.test.ts +7 -5
  47. package/src/pack.test.ts +2 -1
  48. package/scaffold/.agents/flow/tmpl_README.md +0 -7
  49. package/scaffold/.agents/flow/tmpl_iteration_close_checklist.example.md +0 -11
  50. package/schemas/test-plan.ts +0 -20
package/src/cli.ts CHANGED
@@ -2,20 +2,25 @@
2
2
 
3
3
  import { join } from "node:path";
4
4
  import { parseAgentArg } from "./agent";
5
+ import { GuardrailAbortError } from "./guardrail";
5
6
  import { runApproveProjectContext } from "./commands/approve-project-context";
7
+ import { runApproveRefactorPlan } from "./commands/approve-refactor-plan";
6
8
  import { runApproveRequirement } from "./commands/approve-requirement";
7
9
  import { runApproveTestPlan } from "./commands/approve-test-plan";
8
10
  import { runCreateIssue, runCreateIssueFromTestReport } from "./commands/create-issue";
9
11
  import { runCreateProjectContext } from "./commands/create-project-context";
10
12
  import { runCreatePrototype } from "./commands/create-prototype";
11
13
  import { runCreateTestPlan } from "./commands/create-test-plan";
14
+ import { runDefineRefactorPlan } from "./commands/define-refactor-plan";
12
15
  import { runDefineRequirement } from "./commands/define-requirement";
13
16
  import { runDestroy } from "./commands/destroy";
14
17
  import { runExecuteAutomatedFix } from "./commands/execute-automated-fix";
15
18
  import { runExecuteManualFix } from "./commands/execute-manual-fix";
19
+ import { runExecuteRefactor } from "./commands/execute-refactor";
16
20
  import { runExecuteTestPlan } from "./commands/execute-test-plan";
17
21
  import { runInit } from "./commands/init";
18
22
  import { runRefineProjectContext } from "./commands/refine-project-context";
23
+ import { runRefineRefactorPlan } from "./commands/refine-refactor-plan";
19
24
  import { runRefineRequirement } from "./commands/refine-requirement";
20
25
  import { runRefineTestPlan } from "./commands/refine-test-plan";
21
26
  import { runStartIteration } from "./commands/start-iteration";
@@ -76,13 +81,21 @@ function parseOptionalIntegerFlag(
76
81
  return { value: parsed, remainingArgs };
77
82
  }
78
83
 
84
+ function parseForce(args: string[]): { force: boolean; remainingArgs: string[] } {
85
+ const force = args.includes("--force");
86
+ return {
87
+ force,
88
+ remainingArgs: args.filter((arg) => arg !== "--force"),
89
+ };
90
+ }
91
+
79
92
  function printUsage() {
80
93
  console.log(`Usage: nvst <command> [options]
81
94
 
82
95
  Commands:
83
96
  init Initialize toolkit files in the current directory
84
97
  start iteration Start a new iteration (archives previous if exists)
85
- create project-context --agent <provider> [--mode strict|yolo]
98
+ create project-context --agent <provider> [--mode strict|yolo] [--force]
86
99
  Generate/update .agents/PROJECT_CONTEXT.md via agent
87
100
  create test-plan --agent <provider> [--force]
88
101
  Generate test plan document for current iteration
@@ -96,20 +109,28 @@ Commands:
96
109
  Mark project context as approved
97
110
  approve test-plan
98
111
  Mark test plan as approved and generate structured TP JSON
112
+ approve refactor-plan
113
+ Mark refactor plan as approved and generate structured refactor PRD JSON
99
114
  refine project-context --agent <provider> [--challenge]
100
115
  Refine project context via agent (editor or challenge mode)
101
- define requirement --agent <provider>
116
+ define requirement --agent <provider> [--force]
102
117
  Create requirement document via agent
103
- refine requirement --agent <provider> [--challenge]
118
+ define refactor-plan --agent <provider> [--force]
119
+ Create refactor plan document via agent
120
+ refine requirement --agent <provider> [--challenge] [--force]
104
121
  Refine requirement document via agent
105
- refine test-plan --agent <provider> [--challenge]
122
+ refine test-plan --agent <provider> [--challenge] [--force]
106
123
  Refine test plan document via agent
107
- execute test-plan --agent <provider>
124
+ refine refactor-plan --agent <provider> [--challenge] [--force]
125
+ Refine refactor plan document via agent
126
+ execute test-plan --agent <provider> [--force]
108
127
  Execute approved structured test-plan JSON via agent
109
128
  execute automated-fix --agent <provider> [--iterations <N>] [--retry-on-fail <N>]
110
129
  Attempt automated fixes for open issues in current iteration
111
130
  execute manual-fix --agent <provider>
112
131
  Find manual-fix issues for current iteration and confirm execution
132
+ execute refactor --agent <provider> [--force]
133
+ Execute approved refactor items via agent in order
113
134
  approve requirement
114
135
  Mark requirement definition as approved
115
136
  write-json --schema <name> --out <path> [--data '<json>']
@@ -122,7 +143,7 @@ Options:
122
143
  --iterations Maximum prototype passes (integer >= 1)
123
144
  --retry-on-fail Retry attempts per failed story (integer >= 0)
124
145
  --stop-on-critical Stop execution after critical failures
125
- --force Overwrite output file without confirmation
146
+ --force Bypass flow guardrail confirmation (and overwrite test-plan output)
126
147
  --challenge Run refine in challenger mode
127
148
  --clean When used with destroy, also removes .agents/flow/archived
128
149
  -h, --help Show this help message
@@ -153,8 +174,9 @@ async function main() {
153
174
  }
154
175
 
155
176
  if (command === "init") {
156
- if (args.length > 0) {
157
- console.error(`Unknown option(s) for init: ${args.join(" ")}`);
177
+ const { remainingArgs: unknownArgs } = parseForce(args);
178
+ if (unknownArgs.length > 0) {
179
+ console.error(`Unknown option(s) for init: ${unknownArgs.join(" ")}`);
158
180
  printUsage();
159
181
  process.exitCode = 1;
160
182
  return;
@@ -164,8 +186,9 @@ async function main() {
164
186
  }
165
187
 
166
188
  if (command === "destroy") {
167
- const clean = args.includes("--clean");
168
- const unknownArgs = args.filter((arg) => arg !== "--clean");
189
+ const { remainingArgs: argsWithoutForce } = parseForce(args);
190
+ const clean = argsWithoutForce.includes("--clean");
191
+ const unknownArgs = argsWithoutForce.filter((arg) => arg !== "--clean");
169
192
  if (unknownArgs.length > 0) {
170
193
  console.error(`Unknown option(s) for destroy: ${unknownArgs.join(" ")}`);
171
194
  printUsage();
@@ -177,7 +200,8 @@ async function main() {
177
200
  }
178
201
 
179
202
  if (command === "start") {
180
- if (args[0] !== "iteration" || args.length !== 1) {
203
+ const { remainingArgs: argsWithoutForce } = parseForce(args);
204
+ if (argsWithoutForce[0] !== "iteration" || argsWithoutForce.length !== 1) {
181
205
  console.error(`Usage for start: nvst start iteration`);
182
206
  printUsage();
183
207
  process.exitCode = 1;
@@ -203,15 +227,16 @@ async function main() {
203
227
  try {
204
228
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
205
229
  const { mode, remainingArgs: postModeArgs } = parseMode(postAgentArgs);
230
+ const { force, remainingArgs: postForceArgs } = parseForce(postModeArgs);
206
231
 
207
- if (postModeArgs.length > 0) {
208
- console.error(`Unknown option(s) for create project-context: ${postModeArgs.join(" ")}`);
232
+ if (postForceArgs.length > 0) {
233
+ console.error(`Unknown option(s) for create project-context: ${postForceArgs.join(" ")}`);
209
234
  printUsage();
210
235
  process.exitCode = 1;
211
236
  return;
212
237
  }
213
238
 
214
- await runCreateProjectContext({ provider, mode });
239
+ await runCreateProjectContext({ provider, mode, force });
215
240
  return;
216
241
  } catch (error) {
217
242
  console.error(error instanceof Error ? error.message : String(error));
@@ -234,8 +259,9 @@ async function main() {
234
259
  remainingArgs: postRetryArgs,
235
260
  } = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
236
261
 
237
- const stopOnCritical = postRetryArgs.includes("--stop-on-critical");
238
- const unknownArgs = postRetryArgs.filter((arg) => arg !== "--stop-on-critical");
262
+ const { force, remainingArgs: postForceArgs } = parseForce(postRetryArgs);
263
+ const stopOnCritical = postForceArgs.includes("--stop-on-critical");
264
+ const unknownArgs = postForceArgs.filter((arg) => arg !== "--stop-on-critical");
239
265
  if (unknownArgs.length > 0) {
240
266
  console.error(`Unknown option(s) for create prototype: ${unknownArgs.join(" ")}`);
241
267
  printUsage();
@@ -243,7 +269,7 @@ async function main() {
243
269
  return;
244
270
  }
245
271
 
246
- await runCreatePrototype({ provider, iterations, retryOnFail, stopOnCritical });
272
+ await runCreatePrototype({ provider, iterations, retryOnFail, stopOnCritical, force });
247
273
  return;
248
274
  } catch (error) {
249
275
  console.error(error instanceof Error ? error.message : String(error));
@@ -278,9 +304,10 @@ async function main() {
278
304
 
279
305
  if (subcommand === "issue") {
280
306
  const subArgs = args.slice(1);
307
+ const { remainingArgs: subArgsWithoutForce } = parseForce(subArgs);
281
308
 
282
309
  // Check for --help before parsing
283
- if (subArgs.includes("--help") || subArgs.includes("-h")) {
310
+ if (subArgsWithoutForce.includes("--help") || subArgsWithoutForce.includes("-h")) {
284
311
  console.log(`Usage for create issue:
285
312
  nvst create issue --agent <provider> Create issues interactively via agent
286
313
  nvst create issue --test-execution-report Derive issues from test execution results
@@ -292,8 +319,8 @@ Providers: claude, codex, gemini, cursor`);
292
319
 
293
320
  try {
294
321
  // Check for --test-execution-report flag
295
- if (subArgs.includes("--test-execution-report")) {
296
- const remaining = subArgs.filter((a) => a !== "--test-execution-report");
322
+ if (subArgsWithoutForce.includes("--test-execution-report")) {
323
+ const remaining = subArgsWithoutForce.filter((a) => a !== "--test-execution-report");
297
324
  if (remaining.length > 0) {
298
325
  console.error(`Unknown option(s) for create issue --test-execution-report: ${remaining.join(" ")}`);
299
326
  printUsage();
@@ -304,7 +331,7 @@ Providers: claude, codex, gemini, cursor`);
304
331
  return;
305
332
  }
306
333
 
307
- const { provider, remainingArgs: postAgentArgs } = parseAgentArg(subArgs);
334
+ const { provider, remainingArgs: postAgentArgs } = parseAgentArg(subArgsWithoutForce);
308
335
 
309
336
  if (postAgentArgs.length > 0) {
310
337
  console.error(`Unknown option(s) for create issue: ${postAgentArgs.join(" ")}`);
@@ -330,37 +357,67 @@ Providers: claude, codex, gemini, cursor`);
330
357
  }
331
358
 
332
359
  if (command === "define") {
333
- if (args.length === 0 || args[0] !== "requirement") {
334
- console.error(`Usage for define: nvst define requirement --agent <provider>`);
360
+ if (args.length === 0) {
361
+ console.error(`Usage for define: nvst define <requirement|refactor-plan> --agent <provider>`);
335
362
  printUsage();
336
363
  process.exitCode = 1;
337
364
  return;
338
365
  }
339
366
 
340
- try {
341
- const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
367
+ const subcommand = args[0];
368
+
369
+ if (subcommand === "requirement") {
370
+ try {
371
+ const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
372
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
373
+ if (postForceArgs.length > 0) {
374
+ console.error(`Unknown option(s) for define requirement: ${postForceArgs.join(" ")}`);
375
+ printUsage();
376
+ process.exitCode = 1;
377
+ return;
378
+ }
342
379
 
343
- if (postAgentArgs.length > 0) {
344
- console.error(`Unknown option(s) for define requirement: ${postAgentArgs.join(" ")}`);
380
+ await runDefineRequirement({ provider, force });
381
+ return;
382
+ } catch (error) {
383
+ console.error(error instanceof Error ? error.message : String(error));
345
384
  printUsage();
346
385
  process.exitCode = 1;
347
386
  return;
348
387
  }
388
+ }
349
389
 
350
- await runDefineRequirement({ provider });
351
- return;
352
- } catch (error) {
353
- console.error(error instanceof Error ? error.message : String(error));
354
- printUsage();
355
- process.exitCode = 1;
356
- return;
390
+ if (subcommand === "refactor-plan") {
391
+ try {
392
+ const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
393
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
394
+ if (postForceArgs.length > 0) {
395
+ console.error(`Unknown option(s) for define refactor-plan: ${postForceArgs.join(" ")}`);
396
+ printUsage();
397
+ process.exitCode = 1;
398
+ return;
399
+ }
400
+
401
+ await runDefineRefactorPlan({ provider, force });
402
+ return;
403
+ } catch (error) {
404
+ console.error(error instanceof Error ? error.message : String(error));
405
+ printUsage();
406
+ process.exitCode = 1;
407
+ return;
408
+ }
357
409
  }
410
+
411
+ console.error(`Unknown define subcommand: ${subcommand}`);
412
+ printUsage();
413
+ process.exitCode = 1;
414
+ return;
358
415
  }
359
416
 
360
417
  if (command === "refine") {
361
418
  if (args.length === 0) {
362
419
  console.error(
363
- `Usage for refine: nvst refine <requirement|project-context|test-plan> --agent <provider> [--challenge]`,
420
+ `Usage for refine: nvst refine <requirement|project-context|test-plan|refactor-plan> --agent <provider> [--challenge]`,
364
421
  );
365
422
  printUsage();
366
423
  process.exitCode = 1;
@@ -372,8 +429,9 @@ Providers: claude, codex, gemini, cursor`);
372
429
  if (subcommand === "requirement") {
373
430
  try {
374
431
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
375
- const challenge = postAgentArgs.includes("--challenge");
376
- const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
432
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
433
+ const challenge = postForceArgs.includes("--challenge");
434
+ const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
377
435
 
378
436
  if (unknownArgs.length > 0) {
379
437
  console.error(`Unknown option(s) for refine requirement: ${unknownArgs.join(" ")}`);
@@ -382,7 +440,7 @@ Providers: claude, codex, gemini, cursor`);
382
440
  return;
383
441
  }
384
442
 
385
- await runRefineRequirement({ provider, challenge });
443
+ await runRefineRequirement({ provider, challenge, force });
386
444
  return;
387
445
  } catch (error) {
388
446
  console.error(error instanceof Error ? error.message : String(error));
@@ -395,8 +453,9 @@ Providers: claude, codex, gemini, cursor`);
395
453
  if (subcommand === "project-context") {
396
454
  try {
397
455
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
398
- const challenge = postAgentArgs.includes("--challenge");
399
- const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
456
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
457
+ const challenge = postForceArgs.includes("--challenge");
458
+ const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
400
459
 
401
460
  if (unknownArgs.length > 0) {
402
461
  console.error(
@@ -407,7 +466,7 @@ Providers: claude, codex, gemini, cursor`);
407
466
  return;
408
467
  }
409
468
 
410
- await runRefineProjectContext({ provider, challenge });
469
+ await runRefineProjectContext({ provider, challenge, force });
411
470
  return;
412
471
  } catch (error) {
413
472
  console.error(error instanceof Error ? error.message : String(error));
@@ -420,8 +479,9 @@ Providers: claude, codex, gemini, cursor`);
420
479
  if (subcommand === "test-plan") {
421
480
  try {
422
481
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
423
- const challenge = postAgentArgs.includes("--challenge");
424
- const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
482
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
483
+ const challenge = postForceArgs.includes("--challenge");
484
+ const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
425
485
 
426
486
  if (unknownArgs.length > 0) {
427
487
  console.error(`Unknown option(s) for refine test-plan: ${unknownArgs.join(" ")}`);
@@ -430,7 +490,31 @@ Providers: claude, codex, gemini, cursor`);
430
490
  return;
431
491
  }
432
492
 
433
- await runRefineTestPlan({ provider, challenge });
493
+ await runRefineTestPlan({ provider, challenge, force });
494
+ return;
495
+ } catch (error) {
496
+ console.error(error instanceof Error ? error.message : String(error));
497
+ printUsage();
498
+ process.exitCode = 1;
499
+ return;
500
+ }
501
+ }
502
+
503
+ if (subcommand === "refactor-plan") {
504
+ try {
505
+ const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
506
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
507
+ const challenge = postForceArgs.includes("--challenge");
508
+ const unknownArgs = postForceArgs.filter((arg) => arg !== "--challenge");
509
+
510
+ if (unknownArgs.length > 0) {
511
+ console.error(`Unknown option(s) for refine refactor-plan: ${unknownArgs.join(" ")}`);
512
+ printUsage();
513
+ process.exitCode = 1;
514
+ return;
515
+ }
516
+
517
+ await runRefineRefactorPlan({ provider, challenge, force });
434
518
  return;
435
519
  } catch (error) {
436
520
  console.error(error instanceof Error ? error.message : String(error));
@@ -447,27 +531,39 @@ Providers: claude, codex, gemini, cursor`);
447
531
  }
448
532
 
449
533
  if (command === "approve") {
450
- if (args.length !== 1) {
451
- console.error(`Usage for approve: nvst approve <requirement|project-context|test-plan>`);
534
+ if (args.length === 0) {
535
+ console.error(`Usage for approve: nvst approve <requirement|project-context|test-plan|refactor-plan>`);
452
536
  printUsage();
453
537
  process.exitCode = 1;
454
538
  return;
455
539
  }
456
540
 
457
541
  const subcommand = args[0];
542
+ const { force, remainingArgs: unknownArgs } = parseForce(args.slice(1));
543
+ if (unknownArgs.length > 0) {
544
+ console.error(`Unknown option(s) for approve ${subcommand}: ${unknownArgs.join(" ")}`);
545
+ printUsage();
546
+ process.exitCode = 1;
547
+ return;
548
+ }
458
549
 
459
550
  if (subcommand === "requirement") {
460
- await runApproveRequirement();
551
+ await runApproveRequirement({ force });
461
552
  return;
462
553
  }
463
554
 
464
555
  if (subcommand === "project-context") {
465
- await runApproveProjectContext();
556
+ await runApproveProjectContext({ force });
466
557
  return;
467
558
  }
468
559
 
469
560
  if (subcommand === "test-plan") {
470
- await runApproveTestPlan();
561
+ await runApproveTestPlan({ force });
562
+ return;
563
+ }
564
+
565
+ if (subcommand === "refactor-plan") {
566
+ await runApproveRefactorPlan({ force });
471
567
  return;
472
568
  }
473
569
 
@@ -490,14 +586,15 @@ Providers: claude, codex, gemini, cursor`);
490
586
  if (subcommand === "test-plan") {
491
587
  try {
492
588
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
493
- if (postAgentArgs.length > 0) {
494
- console.error(`Unknown option(s) for execute test-plan: ${postAgentArgs.join(" ")}`);
589
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
590
+ if (postForceArgs.length > 0) {
591
+ console.error(`Unknown option(s) for execute test-plan: ${postForceArgs.join(" ")}`);
495
592
  printUsage();
496
593
  process.exitCode = 1;
497
594
  return;
498
595
  }
499
596
 
500
- await runExecuteTestPlan({ provider });
597
+ await runExecuteTestPlan({ provider, force });
501
598
  return;
502
599
  } catch (error) {
503
600
  console.error(error instanceof Error ? error.message : String(error));
@@ -519,8 +616,9 @@ Providers: claude, codex, gemini, cursor`);
519
616
  remainingArgs: postRetryArgs,
520
617
  } = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
521
618
 
522
- if (postRetryArgs.length > 0) {
523
- console.error(`Unknown option(s) for execute automated-fix: ${postRetryArgs.join(" ")}`);
619
+ const { remainingArgs: postForceArgs } = parseForce(postRetryArgs);
620
+ if (postForceArgs.length > 0) {
621
+ console.error(`Unknown option(s) for execute automated-fix: ${postForceArgs.join(" ")}`);
524
622
  printUsage();
525
623
  process.exitCode = 1;
526
624
  return;
@@ -539,8 +637,9 @@ Providers: claude, codex, gemini, cursor`);
539
637
  if (subcommand === "manual-fix") {
540
638
  try {
541
639
  const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
542
- if (postAgentArgs.length > 0) {
543
- console.error(`Unknown option(s) for execute manual-fix: ${postAgentArgs.join(" ")}`);
640
+ const { remainingArgs: postForceArgs } = parseForce(postAgentArgs);
641
+ if (postForceArgs.length > 0) {
642
+ console.error(`Unknown option(s) for execute manual-fix: ${postForceArgs.join(" ")}`);
544
643
  printUsage();
545
644
  process.exitCode = 1;
546
645
  return;
@@ -556,6 +655,27 @@ Providers: claude, codex, gemini, cursor`);
556
655
  }
557
656
  }
558
657
 
658
+ if (subcommand === "refactor") {
659
+ try {
660
+ const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
661
+ const { force, remainingArgs: postForceArgs } = parseForce(postAgentArgs);
662
+ if (postForceArgs.length > 0) {
663
+ console.error(`Unknown option(s) for execute refactor: ${postForceArgs.join(" ")}`);
664
+ printUsage();
665
+ process.exitCode = 1;
666
+ return;
667
+ }
668
+
669
+ await runExecuteRefactor({ provider, force });
670
+ return;
671
+ } catch (error) {
672
+ console.error(error instanceof Error ? error.message : String(error));
673
+ printUsage();
674
+ process.exitCode = 1;
675
+ return;
676
+ }
677
+ }
678
+
559
679
  console.error(`Unknown execute subcommand: ${subcommand}`);
560
680
  printUsage();
561
681
  process.exitCode = 1;
@@ -573,6 +693,10 @@ Providers: claude, codex, gemini, cursor`);
573
693
  }
574
694
 
575
695
  main().catch((error) => {
696
+ if (error instanceof GuardrailAbortError) {
697
+ // exitCode already set and "Aborted." already written by assertGuardrail
698
+ return;
699
+ }
576
700
  console.error("nvst failed:", error);
577
701
  process.exitCode = 1;
578
702
  });
@@ -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 async function runApproveProjectContext(): Promise<void> {
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
- if (projectContext.status !== "pending_approval") {
12
- throw new Error(
13
- `Cannot approve project context from status '${projectContext.status}'. Expected pending_approval.`,
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;