@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
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { $ } from "bun";
|
|
4
4
|
|
|
5
5
|
import { CLI_PATH } from "../cli-path";
|
|
6
|
+
import { assertGuardrail } from "../guardrail";
|
|
6
7
|
import type { Prd } from "../../scaffold/schemas/tmpl_prd";
|
|
7
8
|
import { exists, readState, writeState, FLOW_REL_DIR } from "../state";
|
|
8
9
|
|
|
@@ -154,17 +155,75 @@ function parsePrd(markdown: string): Prd {
|
|
|
154
155
|
// Main command
|
|
155
156
|
// ---------------------------------------------------------------------------
|
|
156
157
|
|
|
157
|
-
|
|
158
|
+
interface WriteJsonResult {
|
|
159
|
+
exitCode: number;
|
|
160
|
+
stderr: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface ApproveRequirementDeps {
|
|
164
|
+
existsFn: (path: string) => Promise<boolean>;
|
|
165
|
+
invokeWriteJsonFn: (
|
|
166
|
+
projectRoot: string,
|
|
167
|
+
schemaName: string,
|
|
168
|
+
outPath: string,
|
|
169
|
+
data: string,
|
|
170
|
+
) => Promise<WriteJsonResult>;
|
|
171
|
+
nowFn: () => Date;
|
|
172
|
+
readFileFn: typeof readFile;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function runWriteJsonCommand(
|
|
176
|
+
projectRoot: string,
|
|
177
|
+
schemaName: string,
|
|
178
|
+
outPath: string,
|
|
179
|
+
data: string,
|
|
180
|
+
): Promise<WriteJsonResult> {
|
|
181
|
+
const result =
|
|
182
|
+
await $`bun ${CLI_PATH} write-json --schema ${schemaName} --out ${outPath} --data ${data}`
|
|
183
|
+
.cwd(projectRoot)
|
|
184
|
+
.nothrow()
|
|
185
|
+
.quiet();
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
exitCode: result.exitCode,
|
|
189
|
+
stderr: result.stderr.toString().trim(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const defaultDeps: ApproveRequirementDeps = {
|
|
194
|
+
existsFn: exists,
|
|
195
|
+
invokeWriteJsonFn: runWriteJsonCommand,
|
|
196
|
+
nowFn: () => new Date(),
|
|
197
|
+
readFileFn: readFile,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export async function runApproveRequirement(
|
|
201
|
+
optsOrDeps: { force?: boolean } | Partial<ApproveRequirementDeps> = {},
|
|
202
|
+
maybeDeps: Partial<ApproveRequirementDeps> = {},
|
|
203
|
+
): Promise<void> {
|
|
204
|
+
const isDepsArg =
|
|
205
|
+
typeof optsOrDeps === "object"
|
|
206
|
+
&& optsOrDeps !== null
|
|
207
|
+
&& (
|
|
208
|
+
"existsFn" in optsOrDeps
|
|
209
|
+
|| "invokeWriteJsonFn" in optsOrDeps
|
|
210
|
+
|| "nowFn" in optsOrDeps
|
|
211
|
+
|| "readFileFn" in optsOrDeps
|
|
212
|
+
);
|
|
213
|
+
const force = isDepsArg ? false : ((optsOrDeps as { force?: boolean }).force ?? false);
|
|
158
214
|
const projectRoot = process.cwd();
|
|
159
215
|
const state = await readState(projectRoot);
|
|
216
|
+
const deps = isDepsArg ? optsOrDeps : maybeDeps;
|
|
217
|
+
const mergedDeps: ApproveRequirementDeps = { ...defaultDeps, ...deps };
|
|
160
218
|
|
|
161
219
|
// --- US-001: Validate status ---
|
|
162
220
|
const requirementDefinition = state.phases.define.requirement_definition;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
221
|
+
await assertGuardrail(
|
|
222
|
+
state,
|
|
223
|
+
requirementDefinition.status !== "in_progress",
|
|
224
|
+
`Cannot approve requirement from status '${requirementDefinition.status}'. Expected in_progress.`,
|
|
225
|
+
{ force },
|
|
226
|
+
);
|
|
168
227
|
|
|
169
228
|
const requirementFile = requirementDefinition.file;
|
|
170
229
|
if (!requirementFile) {
|
|
@@ -172,26 +231,26 @@ export async function runApproveRequirement(): Promise<void> {
|
|
|
172
231
|
}
|
|
173
232
|
|
|
174
233
|
const requirementPath = join(projectRoot, FLOW_REL_DIR, requirementFile);
|
|
175
|
-
if (!(await
|
|
234
|
+
if (!(await mergedDeps.existsFn(requirementPath))) {
|
|
176
235
|
throw new Error(`Cannot approve requirement: file not found at ${requirementPath}`);
|
|
177
236
|
}
|
|
178
237
|
|
|
179
238
|
// --- US-002: Parse PRD markdown and generate JSON ---
|
|
180
|
-
const markdown = await
|
|
239
|
+
const markdown = await mergedDeps.readFileFn(requirementPath, "utf-8");
|
|
181
240
|
const prdData = parsePrd(markdown);
|
|
182
241
|
const prdJsonFileName = `it_${state.current_iteration}_PRD.json`;
|
|
183
242
|
const prdJsonRelPath = join(FLOW_REL_DIR, prdJsonFileName);
|
|
184
243
|
|
|
185
244
|
// Invoke write-json CLI to validate and write the PRD JSON
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
245
|
+
const result = await mergedDeps.invokeWriteJsonFn(
|
|
246
|
+
projectRoot,
|
|
247
|
+
"prd",
|
|
248
|
+
prdJsonRelPath,
|
|
249
|
+
JSON.stringify(prdData),
|
|
250
|
+
);
|
|
192
251
|
|
|
193
252
|
if (result.exitCode !== 0) {
|
|
194
|
-
const stderr = result.stderr.
|
|
253
|
+
const stderr = result.stderr.trim();
|
|
195
254
|
console.error("PRD JSON generation failed. Requirement remains in_progress.");
|
|
196
255
|
if (stderr) {
|
|
197
256
|
console.error(stderr);
|
|
@@ -207,7 +266,7 @@ export async function runApproveRequirement(): Promise<void> {
|
|
|
207
266
|
state.phases.define.prd_generation.status = "completed";
|
|
208
267
|
state.phases.define.prd_generation.file = prdJsonFileName;
|
|
209
268
|
|
|
210
|
-
state.last_updated =
|
|
269
|
+
state.last_updated = mergedDeps.nowFn().toISOString();
|
|
211
270
|
state.updated_by = "nvst:approve-requirement";
|
|
212
271
|
|
|
213
272
|
await writeState(projectRoot, state);
|
|
@@ -3,7 +3,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
|
|
6
|
-
import { TestPlanSchema } from "../../schemas/
|
|
6
|
+
import { TestPlanSchema } from "../../scaffold/schemas/tmpl_test-plan";
|
|
7
7
|
import { readState, writeState } from "../state";
|
|
8
8
|
import { parseTestPlan, runApproveTestPlan } from "./approve-test-plan";
|
|
9
9
|
|
|
@@ -68,7 +68,7 @@ describe("approve test-plan command", () => {
|
|
|
68
68
|
|
|
69
69
|
expect(source).toContain('import { runApproveTestPlan } from "./commands/approve-test-plan";');
|
|
70
70
|
expect(source).toContain('if (subcommand === "test-plan") {');
|
|
71
|
-
expect(source).toContain("await runApproveTestPlan();");
|
|
71
|
+
expect(source).toContain("await runApproveTestPlan({ force });");
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test("requires test_plan.status to be pending_approval", async () => {
|
|
@@ -3,7 +3,8 @@ import { join } from "node:path";
|
|
|
3
3
|
import { $ } from "bun";
|
|
4
4
|
|
|
5
5
|
import { CLI_PATH } from "../cli-path";
|
|
6
|
-
import
|
|
6
|
+
import { assertGuardrail } from "../guardrail";
|
|
7
|
+
import type { TestPlan } from "../../scaffold/schemas/tmpl_test-plan";
|
|
7
8
|
import { exists, FLOW_REL_DIR, readState, writeState } from "../state";
|
|
8
9
|
|
|
9
10
|
interface WriteJsonResult {
|
|
@@ -145,18 +146,31 @@ async function runWriteJsonCommand(
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
export async function runApproveTestPlan(
|
|
148
|
-
|
|
149
|
+
optsOrDeps: { force?: boolean } | Partial<ApproveTestPlanDeps> = {},
|
|
150
|
+
maybeDeps: Partial<ApproveTestPlanDeps> = {},
|
|
149
151
|
): Promise<void> {
|
|
152
|
+
const isDepsArg =
|
|
153
|
+
typeof optsOrDeps === "object"
|
|
154
|
+
&& optsOrDeps !== null
|
|
155
|
+
&& (
|
|
156
|
+
"existsFn" in optsOrDeps
|
|
157
|
+
|| "invokeWriteJsonFn" in optsOrDeps
|
|
158
|
+
|| "nowFn" in optsOrDeps
|
|
159
|
+
|| "readFileFn" in optsOrDeps
|
|
160
|
+
);
|
|
161
|
+
const force = isDepsArg ? false : ((optsOrDeps as { force?: boolean }).force ?? false);
|
|
150
162
|
const projectRoot = process.cwd();
|
|
151
163
|
const state = await readState(projectRoot);
|
|
164
|
+
const deps = isDepsArg ? optsOrDeps : maybeDeps;
|
|
152
165
|
const mergedDeps: ApproveTestPlanDeps = { ...defaultDeps, ...deps };
|
|
153
166
|
|
|
154
167
|
const testPlan = state.phases.prototype.test_plan;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
168
|
+
await assertGuardrail(
|
|
169
|
+
state,
|
|
170
|
+
testPlan.status !== "pending_approval",
|
|
171
|
+
`Cannot approve test plan from status '${testPlan.status}'. Expected pending_approval.`,
|
|
172
|
+
{ force },
|
|
173
|
+
);
|
|
160
174
|
|
|
161
175
|
const testPlanFile = testPlan.file;
|
|
162
176
|
if (!testPlanFile) {
|
|
@@ -420,8 +420,8 @@ describe("create issue --test-execution-report CLI integration", () => {
|
|
|
420
420
|
const exitCode = await proc.exited;
|
|
421
421
|
const stderr = await new Response(proc.stderr).text();
|
|
422
422
|
expect(stderr).not.toContain("Missing --agent");
|
|
423
|
-
if (exitCode !== 0) {
|
|
424
|
-
expect(stderr).
|
|
423
|
+
if (exitCode !== 0 && stderr) {
|
|
424
|
+
expect(stderr).toMatch(/test-execution-results|not found|Test execution results/);
|
|
425
425
|
}
|
|
426
426
|
});
|
|
427
427
|
|
|
@@ -7,44 +7,50 @@ import {
|
|
|
7
7
|
loadSkill,
|
|
8
8
|
type AgentProvider,
|
|
9
9
|
} from "../agent";
|
|
10
|
+
import { assertGuardrail } from "../guardrail";
|
|
10
11
|
import { exists, readState, writeState } from "../state";
|
|
11
12
|
|
|
12
13
|
export interface CreateProjectContextOptions {
|
|
13
14
|
provider: AgentProvider;
|
|
14
15
|
mode: "strict" | "yolo";
|
|
16
|
+
force?: boolean;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export async function runCreateProjectContext(opts: CreateProjectContextOptions): Promise<void> {
|
|
18
|
-
const { provider, mode } = opts;
|
|
20
|
+
const { provider, mode, force = false } = opts;
|
|
19
21
|
const projectRoot = process.cwd();
|
|
20
22
|
const state = await readState(projectRoot);
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
await assertGuardrail(
|
|
25
|
+
state,
|
|
26
|
+
state.phases.define.prd_generation.status !== "completed",
|
|
27
|
+
"Cannot create project context: define.prd_generation must be completed first.",
|
|
28
|
+
{ force },
|
|
29
|
+
);
|
|
27
30
|
|
|
28
31
|
const projectContext = state.phases.prototype.project_context;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
await assertGuardrail(
|
|
33
|
+
state,
|
|
34
|
+
projectContext.status === "pending_approval",
|
|
35
|
+
"Cannot create project context: project context is pending approval. " +
|
|
36
|
+
"Run `bun nvst approve project-context` or `bun nvst refine project-context` first.",
|
|
37
|
+
{ force },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
await assertGuardrail(
|
|
41
|
+
state,
|
|
42
|
+
projectContext.status === "created",
|
|
43
|
+
"Cannot create project context: project context already exists. " +
|
|
44
|
+
"Use `bun nvst refine project-context` to iterate on it.",
|
|
45
|
+
{ force },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
await assertGuardrail(
|
|
49
|
+
state,
|
|
50
|
+
projectContext.status !== "pending",
|
|
51
|
+
`Cannot create project context from status '${projectContext.status}'. Expected pending.`,
|
|
52
|
+
{ force },
|
|
53
|
+
);
|
|
48
54
|
|
|
49
55
|
const prdFile = state.phases.define.prd_generation.file;
|
|
50
56
|
if (!prdFile) {
|