@quinteroac/agents-coding-toolkit 0.1.0-preview
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/AGENTS.md +7 -0
- package/README.md +127 -0
- package/package.json +34 -0
- package/scaffold/.agents/flow/archived/tmpl_.gitkeep +0 -0
- package/scaffold/.agents/flow/tmpl_README.md +7 -0
- package/scaffold/.agents/flow/tmpl_iteration_close_checklist.example.md +11 -0
- package/scaffold/.agents/skills/automated-fix/tmpl_SKILL.md +67 -0
- package/scaffold/.agents/skills/create-issue/tmpl_SKILL.md +68 -0
- package/scaffold/.agents/skills/create-pr-document/tmpl_SKILL.md +125 -0
- package/scaffold/.agents/skills/create-project-context/tmpl_SKILL.md +168 -0
- package/scaffold/.agents/skills/create-test-plan/tmpl_SKILL.md +86 -0
- package/scaffold/.agents/skills/debug/tmpl_SKILL.md +19 -0
- package/scaffold/.agents/skills/evaluate/tmpl_SKILL.md +19 -0
- package/scaffold/.agents/skills/execute-test-batch/tmpl_SKILL.md +49 -0
- package/scaffold/.agents/skills/execute-test-case/tmpl_SKILL.md +47 -0
- package/scaffold/.agents/skills/implement-user-story/tmpl_SKILL.md +68 -0
- package/scaffold/.agents/skills/plan-refactor/tmpl_SKILL.md +19 -0
- package/scaffold/.agents/skills/refactor-prd/tmpl_SKILL.md +19 -0
- package/scaffold/.agents/skills/refine-pr-document/tmpl_SKILL.md +108 -0
- package/scaffold/.agents/skills/refine-project-context/tmpl_SKILL.md +157 -0
- package/scaffold/.agents/skills/refine-test-plan/tmpl_SKILL.md +76 -0
- package/scaffold/.agents/tmpl_PROJECT_CONTEXT.md +3 -0
- package/scaffold/.agents/tmpl_state.example.json +26 -0
- package/scaffold/.agents/tmpl_state_rules.md +29 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_CHANGELOG.md +18 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_TECHNICAL_DEBT.md +11 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_it_000001_evaluation-report.md +19 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_it_000001_product-requirement-document.md +19 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_it_000001_refactor_plan.md +19 -0
- package/scaffold/docs/nvst-flow/templates/tmpl_it_000001_test-plan.md +19 -0
- package/scaffold/docs/nvst-flow/tmpl_COMMANDS.md +0 -0
- package/scaffold/docs/nvst-flow/tmpl_QUICK_USE.md +0 -0
- package/scaffold/docs/tmpl_PLACEHOLDER.md +0 -0
- package/scaffold/schemas/node-shims.d.ts +15 -0
- package/scaffold/schemas/tmpl_issues.ts +19 -0
- package/scaffold/schemas/tmpl_prd.ts +26 -0
- package/scaffold/schemas/tmpl_progress.ts +39 -0
- package/scaffold/schemas/tmpl_state.ts +81 -0
- package/scaffold/schemas/tmpl_test-plan.ts +20 -0
- package/scaffold/schemas/tmpl_validate-progress.ts +13 -0
- package/scaffold/schemas/tmpl_validate-state.ts +13 -0
- package/scaffold/tmpl_AGENTS.md +7 -0
- package/schemas/prd.ts +26 -0
- package/schemas/progress.ts +39 -0
- package/schemas/state.ts +81 -0
- package/schemas/test-plan.test.ts +53 -0
- package/schemas/test-plan.ts +20 -0
- package/schemas/validate-progress.ts +13 -0
- package/schemas/validate-state.ts +13 -0
- package/src/agent.test.ts +37 -0
- package/src/agent.ts +225 -0
- package/src/cli-path.ts +4 -0
- package/src/cli.ts +578 -0
- package/src/commands/approve-project-context.ts +37 -0
- package/src/commands/approve-requirement.ts +217 -0
- package/src/commands/approve-test-plan.test.ts +193 -0
- package/src/commands/approve-test-plan.ts +202 -0
- package/src/commands/create-issue.test.ts +484 -0
- package/src/commands/create-issue.ts +371 -0
- package/src/commands/create-project-context.ts +96 -0
- package/src/commands/create-prototype.test.ts +153 -0
- package/src/commands/create-prototype.ts +425 -0
- package/src/commands/create-test-plan.test.ts +381 -0
- package/src/commands/create-test-plan.ts +248 -0
- package/src/commands/define-requirement.ts +47 -0
- package/src/commands/destroy.ts +113 -0
- package/src/commands/execute-automated-fix.test.ts +580 -0
- package/src/commands/execute-automated-fix.ts +363 -0
- package/src/commands/execute-manual-fix.test.ts +343 -0
- package/src/commands/execute-manual-fix.ts +203 -0
- package/src/commands/execute-test-plan.test.ts +1891 -0
- package/src/commands/execute-test-plan.ts +722 -0
- package/src/commands/init.ts +85 -0
- package/src/commands/refine-project-context.ts +74 -0
- package/src/commands/refine-requirement.ts +60 -0
- package/src/commands/refine-test-plan.test.ts +200 -0
- package/src/commands/refine-test-plan.ts +93 -0
- package/src/commands/start-iteration.test.ts +144 -0
- package/src/commands/start-iteration.ts +101 -0
- package/src/commands/write-json.ts +136 -0
- package/src/install.test.ts +124 -0
- package/src/pack.test.ts +103 -0
- package/src/state.test.ts +66 -0
- package/src/state.ts +52 -0
- package/tsconfig.json +15 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parseAgentArg } from "./agent";
|
|
5
|
+
import { runApproveProjectContext } from "./commands/approve-project-context";
|
|
6
|
+
import { runApproveRequirement } from "./commands/approve-requirement";
|
|
7
|
+
import { runApproveTestPlan } from "./commands/approve-test-plan";
|
|
8
|
+
import { runCreateIssue, runCreateIssueFromTestReport } from "./commands/create-issue";
|
|
9
|
+
import { runCreateProjectContext } from "./commands/create-project-context";
|
|
10
|
+
import { runCreatePrototype } from "./commands/create-prototype";
|
|
11
|
+
import { runCreateTestPlan } from "./commands/create-test-plan";
|
|
12
|
+
import { runDefineRequirement } from "./commands/define-requirement";
|
|
13
|
+
import { runDestroy } from "./commands/destroy";
|
|
14
|
+
import { runExecuteAutomatedFix } from "./commands/execute-automated-fix";
|
|
15
|
+
import { runExecuteManualFix } from "./commands/execute-manual-fix";
|
|
16
|
+
import { runExecuteTestPlan } from "./commands/execute-test-plan";
|
|
17
|
+
import { runInit } from "./commands/init";
|
|
18
|
+
import { runRefineProjectContext } from "./commands/refine-project-context";
|
|
19
|
+
import { runRefineRequirement } from "./commands/refine-requirement";
|
|
20
|
+
import { runRefineTestPlan } from "./commands/refine-test-plan";
|
|
21
|
+
import { runStartIteration } from "./commands/start-iteration";
|
|
22
|
+
import { runWriteJson } from "./commands/write-json";
|
|
23
|
+
|
|
24
|
+
function parseMode(args: string[]): { mode: "strict" | "yolo"; remainingArgs: string[] } {
|
|
25
|
+
const idx = args.indexOf("--mode");
|
|
26
|
+
if (idx === -1) {
|
|
27
|
+
return { mode: "strict", remainingArgs: args };
|
|
28
|
+
}
|
|
29
|
+
if (idx + 1 >= args.length) {
|
|
30
|
+
throw new Error("Missing value for --mode. Expected: strict or yolo.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const mode = args[idx + 1];
|
|
34
|
+
if (mode !== "strict" && mode !== "yolo") {
|
|
35
|
+
throw new Error(`Invalid --mode '${mode}'. Expected: strict or yolo.`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const remainingArgs = [...args.slice(0, idx), ...args.slice(idx + 2)];
|
|
39
|
+
return { mode, remainingArgs };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function extractFlagValue(args: string[], flag: string): { value: string | null; remainingArgs: string[] } {
|
|
43
|
+
const idx = args.indexOf(flag);
|
|
44
|
+
if (idx === -1) {
|
|
45
|
+
return { value: null, remainingArgs: args };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (idx + 1 >= args.length) {
|
|
49
|
+
throw new Error(`Missing value for ${flag}.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const value = args[idx + 1];
|
|
53
|
+
return {
|
|
54
|
+
value,
|
|
55
|
+
remainingArgs: [...args.slice(0, idx), ...args.slice(idx + 2)],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseOptionalIntegerFlag(
|
|
60
|
+
args: string[],
|
|
61
|
+
flag: "--iterations" | "--retry-on-fail",
|
|
62
|
+
min: number,
|
|
63
|
+
): { value: number | undefined; remainingArgs: string[] } {
|
|
64
|
+
const { value, remainingArgs } = extractFlagValue(args, flag);
|
|
65
|
+
if (value === null) {
|
|
66
|
+
return { value: undefined, remainingArgs };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const parsed = Number(value);
|
|
70
|
+
if (!Number.isInteger(parsed) || parsed < min) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Invalid ${flag} value '${value}'. Expected an integer >= ${min}.`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { value: parsed, remainingArgs };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function printUsage() {
|
|
80
|
+
console.log(`Usage: nvst <command> [options]
|
|
81
|
+
|
|
82
|
+
Commands:
|
|
83
|
+
init Initialize toolkit files in the current directory
|
|
84
|
+
start iteration Start a new iteration (archives previous if exists)
|
|
85
|
+
create project-context --agent <provider> [--mode strict|yolo]
|
|
86
|
+
Generate/update .agents/PROJECT_CONTEXT.md via agent
|
|
87
|
+
create test-plan --agent <provider> [--force]
|
|
88
|
+
Generate test plan document for current iteration
|
|
89
|
+
create prototype --agent <provider> [--iterations <N>] [--retry-on-fail <N>] [--stop-on-critical]
|
|
90
|
+
Initialize prototype build for current iteration
|
|
91
|
+
create issue --agent <provider>
|
|
92
|
+
Create issues interactively via agent
|
|
93
|
+
create issue --test-execution-report
|
|
94
|
+
Derive issues from test execution results
|
|
95
|
+
approve project-context
|
|
96
|
+
Mark project context as approved
|
|
97
|
+
approve test-plan
|
|
98
|
+
Mark test plan as approved and generate structured TP JSON
|
|
99
|
+
refine project-context --agent <provider> [--challenge]
|
|
100
|
+
Refine project context via agent (editor or challenge mode)
|
|
101
|
+
define requirement --agent <provider>
|
|
102
|
+
Create requirement document via agent
|
|
103
|
+
refine requirement --agent <provider> [--challenge]
|
|
104
|
+
Refine requirement document via agent
|
|
105
|
+
refine test-plan --agent <provider> [--challenge]
|
|
106
|
+
Refine test plan document via agent
|
|
107
|
+
execute test-plan --agent <provider>
|
|
108
|
+
Execute approved structured test-plan JSON via agent
|
|
109
|
+
execute automated-fix --agent <provider> [--iterations <N>] [--retry-on-fail <N>]
|
|
110
|
+
Attempt automated fixes for open issues in current iteration
|
|
111
|
+
execute manual-fix --agent <provider>
|
|
112
|
+
Find manual-fix issues for current iteration and confirm execution
|
|
113
|
+
approve requirement
|
|
114
|
+
Mark requirement definition as approved
|
|
115
|
+
write-json --schema <name> --out <path> [--data '<json>']
|
|
116
|
+
Write a schema-validated JSON file (payload via --data or stdin)
|
|
117
|
+
destroy [--clean] Remove files generated by nvst
|
|
118
|
+
|
|
119
|
+
Options:
|
|
120
|
+
--agent Agent provider (claude, codex, gemini, cursor) for agent-backed commands
|
|
121
|
+
--mode Create mode for project-context (strict or yolo)
|
|
122
|
+
--iterations Maximum prototype passes (integer >= 1)
|
|
123
|
+
--retry-on-fail Retry attempts per failed story (integer >= 0)
|
|
124
|
+
--stop-on-critical Stop execution after critical failures
|
|
125
|
+
--force Overwrite output file without confirmation
|
|
126
|
+
--challenge Run refine in challenger mode
|
|
127
|
+
--clean When used with destroy, also removes .agents/flow/archived
|
|
128
|
+
-h, --help Show this help message
|
|
129
|
+
-v, --version Print version and exit`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function printVersion(): Promise<void> {
|
|
133
|
+
const pkgPath = join(import.meta.dir, "..", "package.json");
|
|
134
|
+
try {
|
|
135
|
+
const pkg = (await Bun.file(pkgPath).json()) as { version?: string };
|
|
136
|
+
console.log(pkg?.version ?? "unknown");
|
|
137
|
+
} catch {
|
|
138
|
+
console.log("unknown");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function main() {
|
|
143
|
+
const [, , command, ...args] = process.argv;
|
|
144
|
+
|
|
145
|
+
if (command === "-v" || command === "--version") {
|
|
146
|
+
await printVersion();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!command || command === "-h" || command === "--help" || command === "help") {
|
|
151
|
+
printUsage();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (command === "init") {
|
|
156
|
+
if (args.length > 0) {
|
|
157
|
+
console.error(`Unknown option(s) for init: ${args.join(" ")}`);
|
|
158
|
+
printUsage();
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await runInit();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (command === "destroy") {
|
|
167
|
+
const clean = args.includes("--clean");
|
|
168
|
+
const unknownArgs = args.filter((arg) => arg !== "--clean");
|
|
169
|
+
if (unknownArgs.length > 0) {
|
|
170
|
+
console.error(`Unknown option(s) for destroy: ${unknownArgs.join(" ")}`);
|
|
171
|
+
printUsage();
|
|
172
|
+
process.exitCode = 1;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
await runDestroy({ clean });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (command === "start") {
|
|
180
|
+
if (args[0] !== "iteration" || args.length !== 1) {
|
|
181
|
+
console.error(`Usage for start: nvst start iteration`);
|
|
182
|
+
printUsage();
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
await runStartIteration();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (command === "create") {
|
|
191
|
+
if (args.length === 0) {
|
|
192
|
+
console.error(
|
|
193
|
+
`Usage for create: nvst create <project-context|test-plan|prototype|issue> --agent <provider> [options]`,
|
|
194
|
+
);
|
|
195
|
+
printUsage();
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const subcommand = args[0];
|
|
201
|
+
|
|
202
|
+
if (subcommand === "project-context") {
|
|
203
|
+
try {
|
|
204
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
205
|
+
const { mode, remainingArgs: postModeArgs } = parseMode(postAgentArgs);
|
|
206
|
+
|
|
207
|
+
if (postModeArgs.length > 0) {
|
|
208
|
+
console.error(`Unknown option(s) for create project-context: ${postModeArgs.join(" ")}`);
|
|
209
|
+
printUsage();
|
|
210
|
+
process.exitCode = 1;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
await runCreateProjectContext({ provider, mode });
|
|
215
|
+
return;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
218
|
+
printUsage();
|
|
219
|
+
process.exitCode = 1;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (subcommand === "prototype") {
|
|
225
|
+
try {
|
|
226
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
227
|
+
|
|
228
|
+
const {
|
|
229
|
+
value: iterations,
|
|
230
|
+
remainingArgs: postIterationsArgs,
|
|
231
|
+
} = parseOptionalIntegerFlag(postAgentArgs, "--iterations", 1);
|
|
232
|
+
const {
|
|
233
|
+
value: retryOnFail,
|
|
234
|
+
remainingArgs: postRetryArgs,
|
|
235
|
+
} = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
|
|
236
|
+
|
|
237
|
+
const stopOnCritical = postRetryArgs.includes("--stop-on-critical");
|
|
238
|
+
const unknownArgs = postRetryArgs.filter((arg) => arg !== "--stop-on-critical");
|
|
239
|
+
if (unknownArgs.length > 0) {
|
|
240
|
+
console.error(`Unknown option(s) for create prototype: ${unknownArgs.join(" ")}`);
|
|
241
|
+
printUsage();
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await runCreatePrototype({ provider, iterations, retryOnFail, stopOnCritical });
|
|
247
|
+
return;
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
250
|
+
printUsage();
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (subcommand === "test-plan") {
|
|
257
|
+
try {
|
|
258
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
259
|
+
const force = postAgentArgs.includes("--force");
|
|
260
|
+
const unknownArgs = postAgentArgs.filter((arg) => arg !== "--force");
|
|
261
|
+
|
|
262
|
+
if (unknownArgs.length > 0) {
|
|
263
|
+
console.error(`Unknown option(s) for create test-plan: ${unknownArgs.join(" ")}`);
|
|
264
|
+
printUsage();
|
|
265
|
+
process.exitCode = 1;
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
await runCreateTestPlan({ provider, force });
|
|
270
|
+
return;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
273
|
+
printUsage();
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (subcommand === "issue") {
|
|
280
|
+
const subArgs = args.slice(1);
|
|
281
|
+
|
|
282
|
+
// Check for --help before parsing
|
|
283
|
+
if (subArgs.includes("--help") || subArgs.includes("-h")) {
|
|
284
|
+
console.log(`Usage for create issue:
|
|
285
|
+
nvst create issue --agent <provider> Create issues interactively via agent
|
|
286
|
+
nvst create issue --test-execution-report Derive issues from test execution results
|
|
287
|
+
|
|
288
|
+
Providers: claude, codex, gemini, cursor`);
|
|
289
|
+
printUsage();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
// Check for --test-execution-report flag
|
|
295
|
+
if (subArgs.includes("--test-execution-report")) {
|
|
296
|
+
const remaining = subArgs.filter((a) => a !== "--test-execution-report");
|
|
297
|
+
if (remaining.length > 0) {
|
|
298
|
+
console.error(`Unknown option(s) for create issue --test-execution-report: ${remaining.join(" ")}`);
|
|
299
|
+
printUsage();
|
|
300
|
+
process.exitCode = 1;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
await runCreateIssueFromTestReport();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(subArgs);
|
|
308
|
+
|
|
309
|
+
if (postAgentArgs.length > 0) {
|
|
310
|
+
console.error(`Unknown option(s) for create issue: ${postAgentArgs.join(" ")}`);
|
|
311
|
+
printUsage();
|
|
312
|
+
process.exitCode = 1;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
await runCreateIssue({ provider });
|
|
317
|
+
return;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
320
|
+
printUsage();
|
|
321
|
+
process.exitCode = 1;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.error(`Unknown create subcommand: ${subcommand}`);
|
|
327
|
+
printUsage();
|
|
328
|
+
process.exitCode = 1;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (command === "define") {
|
|
333
|
+
if (args.length === 0 || args[0] !== "requirement") {
|
|
334
|
+
console.error(`Usage for define: nvst define requirement --agent <provider>`);
|
|
335
|
+
printUsage();
|
|
336
|
+
process.exitCode = 1;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
342
|
+
|
|
343
|
+
if (postAgentArgs.length > 0) {
|
|
344
|
+
console.error(`Unknown option(s) for define requirement: ${postAgentArgs.join(" ")}`);
|
|
345
|
+
printUsage();
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
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;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (command === "refine") {
|
|
361
|
+
if (args.length === 0) {
|
|
362
|
+
console.error(
|
|
363
|
+
`Usage for refine: nvst refine <requirement|project-context|test-plan> --agent <provider> [--challenge]`,
|
|
364
|
+
);
|
|
365
|
+
printUsage();
|
|
366
|
+
process.exitCode = 1;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const subcommand = args[0];
|
|
371
|
+
|
|
372
|
+
if (subcommand === "requirement") {
|
|
373
|
+
try {
|
|
374
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
375
|
+
const challenge = postAgentArgs.includes("--challenge");
|
|
376
|
+
const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
|
|
377
|
+
|
|
378
|
+
if (unknownArgs.length > 0) {
|
|
379
|
+
console.error(`Unknown option(s) for refine requirement: ${unknownArgs.join(" ")}`);
|
|
380
|
+
printUsage();
|
|
381
|
+
process.exitCode = 1;
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
await runRefineRequirement({ provider, challenge });
|
|
386
|
+
return;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
389
|
+
printUsage();
|
|
390
|
+
process.exitCode = 1;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (subcommand === "project-context") {
|
|
396
|
+
try {
|
|
397
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
398
|
+
const challenge = postAgentArgs.includes("--challenge");
|
|
399
|
+
const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
|
|
400
|
+
|
|
401
|
+
if (unknownArgs.length > 0) {
|
|
402
|
+
console.error(
|
|
403
|
+
`Unknown option(s) for refine project-context: ${unknownArgs.join(" ")}`,
|
|
404
|
+
);
|
|
405
|
+
printUsage();
|
|
406
|
+
process.exitCode = 1;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
await runRefineProjectContext({ provider, challenge });
|
|
411
|
+
return;
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
414
|
+
printUsage();
|
|
415
|
+
process.exitCode = 1;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (subcommand === "test-plan") {
|
|
421
|
+
try {
|
|
422
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
423
|
+
const challenge = postAgentArgs.includes("--challenge");
|
|
424
|
+
const unknownArgs = postAgentArgs.filter((arg) => arg !== "--challenge");
|
|
425
|
+
|
|
426
|
+
if (unknownArgs.length > 0) {
|
|
427
|
+
console.error(`Unknown option(s) for refine test-plan: ${unknownArgs.join(" ")}`);
|
|
428
|
+
printUsage();
|
|
429
|
+
process.exitCode = 1;
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
await runRefineTestPlan({ provider, challenge });
|
|
434
|
+
return;
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
437
|
+
printUsage();
|
|
438
|
+
process.exitCode = 1;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
console.error(`Unknown refine subcommand: ${subcommand}`);
|
|
444
|
+
printUsage();
|
|
445
|
+
process.exitCode = 1;
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (command === "approve") {
|
|
450
|
+
if (args.length !== 1) {
|
|
451
|
+
console.error(`Usage for approve: nvst approve <requirement|project-context|test-plan>`);
|
|
452
|
+
printUsage();
|
|
453
|
+
process.exitCode = 1;
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const subcommand = args[0];
|
|
458
|
+
|
|
459
|
+
if (subcommand === "requirement") {
|
|
460
|
+
await runApproveRequirement();
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (subcommand === "project-context") {
|
|
465
|
+
await runApproveProjectContext();
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (subcommand === "test-plan") {
|
|
470
|
+
await runApproveTestPlan();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.error(`Unknown approve subcommand: ${subcommand}`);
|
|
475
|
+
printUsage();
|
|
476
|
+
process.exitCode = 1;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (command === "execute") {
|
|
481
|
+
if (args.length === 0) {
|
|
482
|
+
console.error(`Usage for execute: nvst execute <test-plan|automated-fix|manual-fix> --agent <provider>`);
|
|
483
|
+
printUsage();
|
|
484
|
+
process.exitCode = 1;
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const subcommand = args[0];
|
|
489
|
+
|
|
490
|
+
if (subcommand === "test-plan") {
|
|
491
|
+
try {
|
|
492
|
+
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(" ")}`);
|
|
495
|
+
printUsage();
|
|
496
|
+
process.exitCode = 1;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
await runExecuteTestPlan({ provider });
|
|
501
|
+
return;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
504
|
+
printUsage();
|
|
505
|
+
process.exitCode = 1;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (subcommand === "automated-fix") {
|
|
511
|
+
try {
|
|
512
|
+
const { provider, remainingArgs: postAgentArgs } = parseAgentArg(args.slice(1));
|
|
513
|
+
const {
|
|
514
|
+
value: iterations,
|
|
515
|
+
remainingArgs: postIterationsArgs,
|
|
516
|
+
} = parseOptionalIntegerFlag(postAgentArgs, "--iterations", 1);
|
|
517
|
+
const {
|
|
518
|
+
value: retryOnFail,
|
|
519
|
+
remainingArgs: postRetryArgs,
|
|
520
|
+
} = parseOptionalIntegerFlag(postIterationsArgs, "--retry-on-fail", 0);
|
|
521
|
+
|
|
522
|
+
if (postRetryArgs.length > 0) {
|
|
523
|
+
console.error(`Unknown option(s) for execute automated-fix: ${postRetryArgs.join(" ")}`);
|
|
524
|
+
printUsage();
|
|
525
|
+
process.exitCode = 1;
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
await runExecuteAutomatedFix({ provider, iterations, retryOnFail });
|
|
530
|
+
return;
|
|
531
|
+
} catch (error) {
|
|
532
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
533
|
+
printUsage();
|
|
534
|
+
process.exitCode = 1;
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (subcommand === "manual-fix") {
|
|
540
|
+
try {
|
|
541
|
+
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(" ")}`);
|
|
544
|
+
printUsage();
|
|
545
|
+
process.exitCode = 1;
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
await runExecuteManualFix({ provider });
|
|
550
|
+
return;
|
|
551
|
+
} catch (error) {
|
|
552
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
553
|
+
printUsage();
|
|
554
|
+
process.exitCode = 1;
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
console.error(`Unknown execute subcommand: ${subcommand}`);
|
|
560
|
+
printUsage();
|
|
561
|
+
process.exitCode = 1;
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (command === "write-json") {
|
|
566
|
+
await runWriteJson({ args });
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
console.error(`Unknown command: ${command}`);
|
|
571
|
+
printUsage();
|
|
572
|
+
process.exitCode = 1;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
main().catch((error) => {
|
|
576
|
+
console.error("nvst failed:", error);
|
|
577
|
+
process.exitCode = 1;
|
|
578
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { exists, readState, writeState } from "../state";
|
|
4
|
+
|
|
5
|
+
export async function runApproveProjectContext(): Promise<void> {
|
|
6
|
+
const projectRoot = process.cwd();
|
|
7
|
+
const state = await readState(projectRoot);
|
|
8
|
+
|
|
9
|
+
// US-002-AC01: Validate status is pending_approval
|
|
10
|
+
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
|
+
}
|
|
16
|
+
|
|
17
|
+
// US-002-AC01: Validate project context file exists
|
|
18
|
+
const contextFile = projectContext.file;
|
|
19
|
+
if (!contextFile) {
|
|
20
|
+
throw new Error("Cannot approve project context: project_context.file is missing.");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const contextPath = join(projectRoot, contextFile);
|
|
24
|
+
if (!(await exists(contextPath))) {
|
|
25
|
+
throw new Error(`Cannot approve project context: file not found at ${contextPath}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// US-002-AC02: Transition status to created
|
|
29
|
+
projectContext.status = "created";
|
|
30
|
+
state.last_updated = new Date().toISOString();
|
|
31
|
+
state.updated_by = "nvst:approve-project-context";
|
|
32
|
+
|
|
33
|
+
await writeState(projectRoot, state);
|
|
34
|
+
|
|
35
|
+
// US-002-AC03: Clear feedback
|
|
36
|
+
console.log("Project context approved.");
|
|
37
|
+
}
|