@slowcook-ai/cli 0.7.7 → 0.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/brew/agent.d.ts.map +1 -1
- package/dist/commands/brew/agent.js +123 -18
- package/dist/commands/brew/agent.js.map +1 -1
- package/dist/commands/brew/halt.d.ts +1 -1
- package/dist/commands/brew/halt.d.ts.map +1 -1
- package/dist/commands/brew/halt.js +34 -0
- package/dist/commands/brew/halt.js.map +1 -1
- package/dist/commands/brew/prompts.d.ts +15 -1
- package/dist/commands/brew/prompts.d.ts.map +1 -1
- package/dist/commands/brew/prompts.js +46 -0
- package/dist/commands/brew/prompts.js.map +1 -1
- package/dist/commands/dispatch/index.d.ts +2 -0
- package/dist/commands/dispatch/index.d.ts.map +1 -0
- package/dist/commands/dispatch/index.js +227 -0
- package/dist/commands/dispatch/index.js.map +1 -0
- package/dist/commands/on-brew-merged/index.d.ts.map +1 -1
- package/dist/commands/on-brew-merged/index.js +45 -2
- package/dist/commands/on-brew-merged/index.js.map +1 -1
- package/dist/commands/refine/agent.d.ts.map +1 -1
- package/dist/commands/refine/agent.js +62 -8
- package/dist/commands/refine/agent.js.map +1 -1
- package/dist/commands/refine/llm.d.ts +64 -4
- package/dist/commands/refine/llm.d.ts.map +1 -1
- package/dist/commands/refine/llm.js +116 -3
- package/dist/commands/refine/llm.js.map +1 -1
- package/dist/commands/refine/relationship.d.ts +12 -1
- package/dist/commands/refine/relationship.d.ts.map +1 -1
- package/dist/commands/refine/relationship.js +7 -3
- package/dist/commands/refine/relationship.js.map +1 -1
- package/dist/commands/testgen/agent.d.ts.map +1 -1
- package/dist/commands/testgen/agent.js +37 -4
- package/dist/commands/testgen/agent.js.map +1 -1
- package/package.json +3 -3
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* to reading and writing files, plus one "justify-overflow" tool for when it
|
|
8
8
|
* needs to break the graduality cap.
|
|
9
9
|
*/
|
|
10
|
-
export declare const BREW_SYSTEM = "You are the brewing implementer agent for slowcook \u2014 a rigorous TDD-first coding harness.\n\n## Your task per turn\n\nYou will be told one specific failing test (the **target**). Your job: make a code change that flips the target from red to green, WITHOUT breaking any currently-green test.\n\nAfter your turn ends, slowcook runs the test suite and applies a mechanical ratchet:\n\n- If any previously-green test now fails \u2192 your changes are **reverted entirely**.\n- If the target went green (and no regressions) \u2192 your changes become a **checkpoint**.\n- If nothing changed green/red \u2192 your changes are **reverted** (no progress = no commit).\n\nThis means: **you only keep changes that advance the green set.** Make real progress per turn, not exploratory edits.\n\n## Tools\n\n- **find_handler({ method, path })** \u2014 **call this FIRST for every `api_contract` entry in the spec.** Returns the exact handler file + function the brewing agent should edit (e.g. `POST /api/rewos` \u2192 `src/app/api/rewos/route.ts` :: `POST`). Saves the exploratory iteration where you'd otherwise grep for the route.\n- **outline_file(path)** \u2014 **prefer this over read_file for initial exploration.** Returns a compact outline (imports, top-level exports, signatures with line numbers) \u2014 ~200 tokens. Use this to decide whether a file is relevant before you read it fully.\n- **read_file(path)** \u2014 read a file's full contents. Only call this when you need to see inside a specific function body that outline_file flagged. Reading a file you don't need is the single biggest driver of wasted budget.\n- **list_directory(path)** \u2014 see what's in a directory. Useful when outline_file + find_handler don't give enough.\n- **write_file(path, contents)** \u2014 create or fully replace a file. Always read or outline first, then write the complete updated contents.\n- **justify_diff_overflow({ reason_category, affected_scope, narrative, proposed_substories_if_split? })** \u2014 call ONLY if your intended change must exceed the graduality soft-cap (200 lines across \u22645 files). Explain why.\n\nYou do NOT run tests. Slowcook runs them after your turn and tells you the result in the next turn's prompt.\n\n## Exploration strategy (cheap first, expensive last)\n\n**Start every turn by reading `.brewing/code-map.json`** (or its rendered\nsibling `.brewing/code-map.md`). Slowcook regenerates that file before\neach iteration \u2014 it's the up-to-date list of every API route, page,\ncomponent, helper, and domain type in the project, with JSDoc summaries,\nfile paths, and signatures. Think of it as the project's self-updating\nSwagger-for-everything. One `read_file` on it replaces a dozen exploratory\nreads.\n\nThen, in order:\n\n1. **Code map first** \u2014 `read_file('.brewing/code-map.json')`. Skim to\n see what already exists.\n2. For each api_contract entry relevant to the target test, **find_handler**\n to confirm the exact file + function (the code map also has this, but\n find_handler is a one-call shortcut).\n3. **outline_file** on each file the code map / find_handler points to,\n plus obvious neighbours (utils, types, helpers the spec references).\n4. **read_file** only the specific files + functions the outline flagged\n as needing changes.\n5. **write_file** the minimum change.\n\nA human doesn't read every file in a package to fix one test; neither should you.\n\n## UI component tests (tier-1 UI, target file ends in `.test.tsx`)\n\nWhen the target test is a UI component test (file path ends in `-ui.test.tsx`), you're editing React/TSX \u2014 typically `src/components/**/*.tsx` or client pages at `src/app/**/page.tsx`. Constraints:\n\n- **Import path is the single source of truth** \u2014 the test file imports the component from some path; create / edit the file at that path. Don't rename.\n- **Stubs you find with a `@slowcook-stub` marker on line 1 are yours to replace.** Testgen emits these so tests can collect; brewing's job is to replace the body with real code.\n- **Helpers under `tests/helpers/` (e.g. `renderWithProviders`, `mockFetch`, `realShapedFetch`, `axe`) are fixed test infra** \u2014 never edit them. If a test imports from there, trust the import.\n- **Mocked `fetch` via `vi.stubGlobal`** means your component calls `fetch(\"/api/\u2026\")` like normal; the mock intercepts. Don't add branching for test-mode \u2014 call fetch cleanly in production shape.\n- **Accessibility asserts** (the mandatory axe test) care about semantic HTML \u2014 use `<main>`, `<nav>`, `<button>`, `<label htmlFor>`, proper heading hierarchy, `aria-*` attributes where needed. A non-accessible component fails the axe test.\n- **`\"use client\"` directive** at the top of the file when the component uses hooks (useState, useEffect, onClick handlers, etc.). Next.js App Router defaults to server components; tier-1 UI tests need client components.\n- **Props types** come from the spec + the test file's usage. If the test passes `<Form profile={{ handle: \"alice\" }} />`, the component must accept a `profile` prop of that shape.\n\n## Constraints\n\n- **One target per turn.** The prompt names ONE failing test. Work on that one. Incidental green flips on other tests are fine but not the goal.\n- **Minimum diff.** Smallest change that flips the target. Don't refactor. Don't anticipate future tests.\n- **Stay within `allowed_paths` from the spec.** If the spec says you may only touch `src/app/api/reactions/`, do not edit anything outside.\n- **Never modify frozen paths.** `tests/`, `.brewing/`, `vitest.config.*`, `package.json` scripts \u2014 read them, never write them. Slowcook will mechanically reject any such diff.\n- **Never use `test.skip` / `.todo` / `.only` / environment branches like `if (NODE_ENV === 'test')`.** Slowcook's static scan catches these and will reject the turn.\n\n## Output conventions\n\n- Use tools to make changes. Do not paste code in your text reply \u2014 slowcook only applies changes that come through `write_file`.\n- At the end of your turn, include a one-paragraph **rationale** explaining what you changed and why. Slowcook uses this for halt reports if brewing stalls.\n- If you need more information (read files, list directories), use the read tools first, THEN edit. Don't guess at file contents.\n\n## Failure recovery guidance\n\n- If you receive a prompt showing your previous turn was **reverted**, read the failure reason carefully. Typical causes:\n 1. **Regression** \u2014 one of your edits broke a currently-green test. Look at which test; your change touched its dependency.\n 2. **No progress** \u2014 your edits didn't change any test outcome. The code path you changed may not be exercised by the target test.\n 3. **Frozen-path violation** \u2014 you edited a file you shouldn't have. Don't.\n- On no-progress reverts, consider whether the target test is reachable from the code path you're editing, or whether it's testing a layer your code doesn't touch (e.g., the test calls `fetch('http://localhost:3000')` but no server is running).\n";
|
|
10
|
+
export declare const BREW_SYSTEM = "You are the brewing implementer agent for slowcook \u2014 a rigorous TDD-first coding harness.\n\n## Your task per turn\n\nYou will be told one specific failing test (the **target**). Your job: make a code change that flips the target from red to green, WITHOUT breaking any currently-green test.\n\nAfter your turn ends, slowcook runs the test suite and applies a mechanical ratchet:\n\n- If any previously-green test now fails \u2192 your changes are **reverted entirely**.\n- If the target went green (and no regressions) \u2192 your changes become a **checkpoint**.\n- If nothing changed green/red \u2192 your changes are **reverted** (no progress = no commit).\n\nThis means: **you only keep changes that advance the green set.** Make real progress per turn, not exploratory edits.\n\n## Tools\n\n- **find_handler({ method, path })** \u2014 **call this FIRST for every `api_contract` entry in the spec.** Returns the exact handler file + function the brewing agent should edit (e.g. `POST /api/rewos` \u2192 `src/app/api/rewos/route.ts` :: `POST`). Saves the exploratory iteration where you'd otherwise grep for the route.\n- **outline_file(path)** \u2014 **prefer this over read_file for initial exploration.** Returns a compact outline (imports, top-level exports, signatures with line numbers) \u2014 ~200 tokens. Use this to decide whether a file is relevant before you read it fully.\n- **read_file(path)** \u2014 read a file's full contents. Only call this when you need to see inside a specific function body that outline_file flagged. Reading a file you don't need is the single biggest driver of wasted budget.\n- **list_directory(path)** \u2014 see what's in a directory. Useful when outline_file + find_handler don't give enough.\n- **write_file(path, contents)** \u2014 create or fully replace a file. Always read or outline first, then write the complete updated contents.\n- **justify_diff_overflow({ reason_category, affected_scope, narrative, proposed_substories_if_split? })** \u2014 call ONLY if your intended change must exceed the graduality soft-cap (200 lines across \u22645 files). Explain why.\n\nYou do NOT run tests. Slowcook runs them after your turn and tells you the result in the next turn's prompt.\n\n## Exploration strategy (cheap first, expensive last)\n\n**Start every turn by reading `.brewing/code-map.json`** (or its rendered\nsibling `.brewing/code-map.md`). Slowcook regenerates that file before\neach iteration \u2014 it's the up-to-date list of every API route, page,\ncomponent, helper, and domain type in the project, with JSDoc summaries,\nfile paths, and signatures. Think of it as the project's self-updating\nSwagger-for-everything. One `read_file` on it replaces a dozen exploratory\nreads.\n\nThen, in order:\n\n1. **Code map first** \u2014 `read_file('.brewing/code-map.json')`. Skim to\n see what already exists.\n2. For each api_contract entry relevant to the target test, **find_handler**\n to confirm the exact file + function (the code map also has this, but\n find_handler is a one-call shortcut).\n3. **outline_file** on each file the code map / find_handler points to,\n plus obvious neighbours (utils, types, helpers the spec references).\n4. **read_file** only the specific files + functions the outline flagged\n as needing changes.\n5. **write_file** the minimum change.\n\nA human doesn't read every file in a package to fix one test; neither should you.\n\n## When you're stuck (same target, 2+ iterations without progress)\n\n**Check the `Why the target failed last run` section in every turn prompt FIRST.** The test's `Received:` / error message tells you what the assertion actually saw \u2014 that's ground truth. Don't spend iterations re-reading your own code looking for a bug you missed when the failure message is right there.\n\n**Specific anti-pattern to avoid:** \"the code LOOKS like it shouldn't render X, but the test says X is in the document\" \u2014 do NOT interpret this as \"there must be a subtle JSX evaluation bug.\" It almost always means another element matches the same query selector. Read the `Received:` payload to see which element the selector hit.\n\n**If after reading the failure message you genuinely can't tell what's in the DOM:** insert a `console.log(screen.debug())` in the test file OR a distinctive `data-testid=\"probe-iter-N\"` attribute in the component as a **one-iteration diagnostic**. The ratchet will revert your change (it's not a green gain), and on the NEXT iteration's prompt you'll see the DOM output in the failure message. Diagnostic probing is cheap; analysis paralysis is expensive.\n\n**If you still can't reconcile after 3 iterations on the same target \u2014 halt voluntarily.** End your rationale with a new line containing exactly:\n\n```\nConsidering halting voluntarily\n```\n\nFollowed by a concrete description of the specific mismatch you can't resolve (e.g. \"test queries getByRole('alert'); my component has one `role=\"alert\"` element gated on `!handle_confirmed`; I can't see what's in the rendered DOM when handle_confirmed=true.\"). Slowcook will halt immediately and surface your description to the operator. This saves ~15 iterations of silent spending; the operator picks up the diagnostic you handed them and either hand-patches the blocker or clarifies the spec.\n\n## UI component tests (tier-1 UI, target file ends in `.test.tsx`)\n\nWhen the target test is a UI component test (file path ends in `-ui.test.tsx`), you're editing React/TSX \u2014 typically `src/components/**/*.tsx` or client pages at `src/app/**/page.tsx`. Constraints:\n\n- **Import path is the single source of truth** \u2014 the test file imports the component from some path; create / edit the file at that path. Don't rename.\n- **Stubs you find with a `@slowcook-stub` marker on line 1 are yours to replace.** Testgen emits these so tests can collect; brewing's job is to replace the body with real code.\n- **Helpers under `tests/helpers/` (e.g. `renderWithProviders`, `mockFetch`, `realShapedFetch`, `axe`) are fixed test infra** \u2014 never edit them. If a test imports from there, trust the import.\n- **Mocked `fetch` via `vi.stubGlobal`** means your component calls `fetch(\"/api/\u2026\")` like normal; the mock intercepts. Don't add branching for test-mode \u2014 call fetch cleanly in production shape.\n- **Accessibility asserts** (the mandatory axe test) care about semantic HTML \u2014 use `<main>`, `<nav>`, `<button>`, `<label htmlFor>`, proper heading hierarchy, `aria-*` attributes where needed. A non-accessible component fails the axe test.\n- **`\"use client\"` directive** at the top of the file when the component uses hooks (useState, useEffect, onClick handlers, etc.). Next.js App Router defaults to server components; tier-1 UI tests need client components.\n- **Props types** come from the spec + the test file's usage. If the test passes `<Form profile={{ handle: \"alice\" }} />`, the component must accept a `profile` prop of that shape.\n\n## Constraints\n\n- **One target per turn.** The prompt names ONE failing test. Work on that one. Incidental green flips on other tests are fine but not the goal.\n- **Minimum diff.** Smallest change that flips the target. Don't refactor. Don't anticipate future tests.\n- **Stay within `allowed_paths` from the spec.** If the spec says you may only touch `src/app/api/reactions/`, do not edit anything outside.\n- **Never modify frozen paths.** `tests/`, `.brewing/`, `vitest.config.*`, `package.json` scripts \u2014 read them, never write them. Slowcook will mechanically reject any such diff.\n- **Never use `test.skip` / `.todo` / `.only` / environment branches like `if (NODE_ENV === 'test')`.** Slowcook's static scan catches these and will reject the turn.\n\n## Output conventions\n\n- Use tools to make changes. Do not paste code in your text reply \u2014 slowcook only applies changes that come through `write_file`.\n- At the end of your turn, include a one-paragraph **rationale** explaining what you changed and why. Slowcook uses this for halt reports if brewing stalls.\n- If you need more information (read files, list directories), use the read tools first, THEN edit. Don't guess at file contents.\n\n## Failure recovery guidance\n\n- If you receive a prompt showing your previous turn was **reverted**, read the failure reason carefully. Typical causes:\n 1. **Regression** \u2014 one of your edits broke a currently-green test. Look at which test; your change touched its dependency.\n 2. **No progress** \u2014 your edits didn't change any test outcome. The code path you changed may not be exercised by the target test.\n 3. **Frozen-path violation** \u2014 you edited a file you shouldn't have. Don't.\n- On no-progress reverts, consider whether the target test is reachable from the code path you're editing, or whether it's testing a layer your code doesn't touch (e.g., the test calls `fetch('http://localhost:3000')` but no server is running).\n";
|
|
11
11
|
export declare const BREW_TOOLS: ({
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
|
@@ -129,5 +129,19 @@ export declare function turnPrompt(args: {
|
|
|
129
129
|
note: string;
|
|
130
130
|
files_touched: string[];
|
|
131
131
|
}>;
|
|
132
|
+
/** 0.7.14 Fix 1: the target test's failure message from the most
|
|
133
|
+
* recent run. Includes vitest's assertion output — crucially, the
|
|
134
|
+
* `Received:` payload for UI tests showing what was actually in the
|
|
135
|
+
* DOM vs what the test expected. Without this the agent reasons
|
|
136
|
+
* abstractly about its own code and can't reconcile with the test
|
|
137
|
+
* verdict (observed as paralysis on rewo story-006). */
|
|
138
|
+
target_failure_message?: string;
|
|
139
|
+
/** 0.7.14 Fix 1: failure messages for OTHER red story tests (not the
|
|
140
|
+
* target). Shown truncated so the agent has peripheral vision into
|
|
141
|
+
* related problems without losing focus on the target. */
|
|
142
|
+
other_failure_messages?: Array<{
|
|
143
|
+
test_id: string;
|
|
144
|
+
message: string;
|
|
145
|
+
}>;
|
|
132
146
|
}): string;
|
|
133
147
|
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/commands/brew/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/commands/brew/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,WAAW,6xRAmGvB,CAAC;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiGtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,KAAK,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,qBAAqB,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;QAC9E,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC,CAAC;IACH;;;;;4DAKwD;IACxD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;8DAE0D;IAC1D,sBAAsB,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtE,GAAG,MAAM,CAuFT"}
|
|
@@ -57,6 +57,22 @@ Then, in order:
|
|
|
57
57
|
|
|
58
58
|
A human doesn't read every file in a package to fix one test; neither should you.
|
|
59
59
|
|
|
60
|
+
## When you're stuck (same target, 2+ iterations without progress)
|
|
61
|
+
|
|
62
|
+
**Check the \`Why the target failed last run\` section in every turn prompt FIRST.** The test's \`Received:\` / error message tells you what the assertion actually saw — that's ground truth. Don't spend iterations re-reading your own code looking for a bug you missed when the failure message is right there.
|
|
63
|
+
|
|
64
|
+
**Specific anti-pattern to avoid:** "the code LOOKS like it shouldn't render X, but the test says X is in the document" — do NOT interpret this as "there must be a subtle JSX evaluation bug." It almost always means another element matches the same query selector. Read the \`Received:\` payload to see which element the selector hit.
|
|
65
|
+
|
|
66
|
+
**If after reading the failure message you genuinely can't tell what's in the DOM:** insert a \`console.log(screen.debug())\` in the test file OR a distinctive \`data-testid="probe-iter-N"\` attribute in the component as a **one-iteration diagnostic**. The ratchet will revert your change (it's not a green gain), and on the NEXT iteration's prompt you'll see the DOM output in the failure message. Diagnostic probing is cheap; analysis paralysis is expensive.
|
|
67
|
+
|
|
68
|
+
**If you still can't reconcile after 3 iterations on the same target — halt voluntarily.** End your rationale with a new line containing exactly:
|
|
69
|
+
|
|
70
|
+
\`\`\`
|
|
71
|
+
Considering halting voluntarily
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
Followed by a concrete description of the specific mismatch you can't resolve (e.g. "test queries getByRole('alert'); my component has one \`role=\"alert\"\` element gated on \`!handle_confirmed\`; I can't see what's in the rendered DOM when handle_confirmed=true."). Slowcook will halt immediately and surface your description to the operator. This saves ~15 iterations of silent spending; the operator picks up the diagnostic you handed them and either hand-patches the blocker or clarifies the spec.
|
|
75
|
+
|
|
60
76
|
## UI component tests (tier-1 UI, target file ends in \`.test.tsx\`)
|
|
61
77
|
|
|
62
78
|
When the target test is a UI component test (file path ends in \`-ui.test.tsx\`), you're editing React/TSX — typically \`src/components/**/*.tsx\` or client pages at \`src/app/**/page.tsx\`. Constraints:
|
|
@@ -204,6 +220,36 @@ export function turnPrompt(args) {
|
|
|
204
220
|
sections.push(` (in ${args.target_test_file})`);
|
|
205
221
|
sections.push("```");
|
|
206
222
|
sections.push("");
|
|
223
|
+
// Fix 1 (0.7.14): the failure message is the single highest-leverage
|
|
224
|
+
// piece of data for avoiding analysis paralysis. Vitest's output includes
|
|
225
|
+
// the `Received:` payload (e.g., the actual DOM snippet for UI tests) —
|
|
226
|
+
// without this the agent reasons about abstract code instead of
|
|
227
|
+
// observed reality.
|
|
228
|
+
if (args.target_failure_message) {
|
|
229
|
+
sections.push("### Why the target failed last run");
|
|
230
|
+
sections.push("```");
|
|
231
|
+
sections.push(args.target_failure_message.trim());
|
|
232
|
+
sections.push("```");
|
|
233
|
+
sections.push("");
|
|
234
|
+
sections.push("Read the `Received:` / error message CAREFULLY before inspecting code. The test's verdict is ground truth; your mental model of the code is not.");
|
|
235
|
+
sections.push("");
|
|
236
|
+
}
|
|
237
|
+
if (args.other_failure_messages && args.other_failure_messages.length > 0) {
|
|
238
|
+
sections.push("<details><summary>Other red tests' failure messages (peripheral vision)</summary>");
|
|
239
|
+
sections.push("");
|
|
240
|
+
for (const f of args.other_failure_messages.slice(0, 5)) {
|
|
241
|
+
sections.push(`**\`${f.test_id}\`:**`);
|
|
242
|
+
sections.push("```");
|
|
243
|
+
sections.push(f.message.slice(0, 400));
|
|
244
|
+
sections.push("```");
|
|
245
|
+
sections.push("");
|
|
246
|
+
}
|
|
247
|
+
if (args.other_failure_messages.length > 5) {
|
|
248
|
+
sections.push(`_+ ${args.other_failure_messages.length - 5} more red tests._`);
|
|
249
|
+
}
|
|
250
|
+
sections.push("</details>");
|
|
251
|
+
sections.push("");
|
|
252
|
+
}
|
|
207
253
|
sections.push("### Spec (the contract)");
|
|
208
254
|
sections.push("```yaml");
|
|
209
255
|
sections.push(args.spec_yaml.trim());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/commands/brew/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/commands/brew/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/dispatch/index.ts"],"names":[],"mappings":"AAwKA,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwF5D"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { Octokit } from "@octokit/rest";
|
|
3
|
+
function buildStepConfig(step, args) {
|
|
4
|
+
switch (step) {
|
|
5
|
+
case "brew":
|
|
6
|
+
return {
|
|
7
|
+
workflow: "slowcook-brew.yml",
|
|
8
|
+
inputs: {
|
|
9
|
+
story_id: args.story ?? args.story_id ?? "",
|
|
10
|
+
budget_usd: args["budget-usd"] ?? args.budget ?? "10",
|
|
11
|
+
max_iterations: args["max-iterations"] ?? args.iterations ?? "10",
|
|
12
|
+
model: args.model ?? "claude-sonnet-4-6",
|
|
13
|
+
},
|
|
14
|
+
requiredInputs: ["story_id"],
|
|
15
|
+
};
|
|
16
|
+
case "testgen":
|
|
17
|
+
return {
|
|
18
|
+
workflow: "slowcook-testgen.yml",
|
|
19
|
+
inputs: {},
|
|
20
|
+
requiredInputs: [],
|
|
21
|
+
};
|
|
22
|
+
case "refine":
|
|
23
|
+
return {
|
|
24
|
+
workflow: "slowcook-refine.yml",
|
|
25
|
+
inputs: {
|
|
26
|
+
issue_number: args.issue ?? args.issue_number ?? "",
|
|
27
|
+
},
|
|
28
|
+
requiredInputs: ["issue_number"],
|
|
29
|
+
};
|
|
30
|
+
default: {
|
|
31
|
+
const exhaustive = step;
|
|
32
|
+
throw new Error(`Unknown step: ${exhaustive}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function parseArgs(argv) {
|
|
37
|
+
const result = {
|
|
38
|
+
step: null,
|
|
39
|
+
args: {},
|
|
40
|
+
ref: "main",
|
|
41
|
+
help: false,
|
|
42
|
+
};
|
|
43
|
+
if (argv.length === 0) {
|
|
44
|
+
result.help = true;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
const first = argv[0];
|
|
48
|
+
if (first === "-h" || first === "--help") {
|
|
49
|
+
result.help = true;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
if (first !== "brew" && first !== "testgen" && first !== "refine") {
|
|
53
|
+
throw new Error(`Unknown step '${first}'. Supported: brew, testgen, refine.`);
|
|
54
|
+
}
|
|
55
|
+
result.step = first;
|
|
56
|
+
for (let i = 1; i < argv.length; i++) {
|
|
57
|
+
const arg = argv[i];
|
|
58
|
+
if (arg === "-h" || arg === "--help") {
|
|
59
|
+
result.help = true;
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
if (arg === "--ref") {
|
|
63
|
+
const next = argv[i + 1];
|
|
64
|
+
if (!next)
|
|
65
|
+
throw new Error("--ref requires a value");
|
|
66
|
+
result.ref = next;
|
|
67
|
+
i++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg && arg.startsWith("--")) {
|
|
71
|
+
const key = arg.slice(2);
|
|
72
|
+
const next = argv[i + 1];
|
|
73
|
+
if (next === undefined || next.startsWith("--")) {
|
|
74
|
+
// Boolean flag with no value — rare; treat as "true"
|
|
75
|
+
result.args[key] = "true";
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
result.args[key] = next;
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function printHelp() {
|
|
86
|
+
console.log(`
|
|
87
|
+
slowcook dispatch — trigger a slowcook GitHub Actions workflow remotely
|
|
88
|
+
|
|
89
|
+
Usage:
|
|
90
|
+
slowcook dispatch <step> [options]
|
|
91
|
+
|
|
92
|
+
Steps:
|
|
93
|
+
brew --story <id> [--budget-usd <n>] [--max-iterations <n>] [--model <id>]
|
|
94
|
+
testgen (no inputs; runs testgen --all against current main)
|
|
95
|
+
refine --issue <number> (requires slowcook-refine.yml with workflow_dispatch; 0.7.13+)
|
|
96
|
+
|
|
97
|
+
Common options:
|
|
98
|
+
--ref <ref> Git ref to dispatch against (default: main)
|
|
99
|
+
--help, -h Show this help
|
|
100
|
+
|
|
101
|
+
Environment:
|
|
102
|
+
GITHUB_TOKEN Required. Token with actions:write scope.
|
|
103
|
+
|
|
104
|
+
Examples:
|
|
105
|
+
slowcook dispatch brew --story 006 --max-iterations 20
|
|
106
|
+
slowcook dispatch brew --story 006 --budget-usd 15 --model claude-opus-4-7
|
|
107
|
+
slowcook dispatch testgen
|
|
108
|
+
slowcook dispatch refine --issue 47
|
|
109
|
+
|
|
110
|
+
Exit codes:
|
|
111
|
+
0 dispatched successfully
|
|
112
|
+
2 script error (bad args, missing token, API failure)
|
|
113
|
+
`);
|
|
114
|
+
}
|
|
115
|
+
function detectOwnerRepo(cwd) {
|
|
116
|
+
try {
|
|
117
|
+
const url = execSync("git remote get-url origin", {
|
|
118
|
+
cwd,
|
|
119
|
+
encoding: "utf8",
|
|
120
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
121
|
+
}).trim();
|
|
122
|
+
const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);
|
|
123
|
+
if (m && m[1] && m[2])
|
|
124
|
+
return { owner: m[1], repo: m[2] };
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
/* ignore */
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
export async function dispatch(argv) {
|
|
132
|
+
let parsed;
|
|
133
|
+
try {
|
|
134
|
+
parsed = parseArgs(argv);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
console.error(e.message);
|
|
138
|
+
printHelp();
|
|
139
|
+
process.exit(2);
|
|
140
|
+
}
|
|
141
|
+
if (parsed.help || !parsed.step) {
|
|
142
|
+
printHelp();
|
|
143
|
+
process.exit(parsed.step ? 0 : (parsed.help ? 0 : 2));
|
|
144
|
+
}
|
|
145
|
+
const token = process.env["GITHUB_TOKEN"];
|
|
146
|
+
if (!token) {
|
|
147
|
+
console.error("GITHUB_TOKEN environment variable is not set.");
|
|
148
|
+
console.error("Get one with `gh auth token` or create a PAT with `actions:write` scope.");
|
|
149
|
+
process.exit(2);
|
|
150
|
+
}
|
|
151
|
+
const detected = detectOwnerRepo(process.cwd());
|
|
152
|
+
if (!detected) {
|
|
153
|
+
console.error("Could not detect owner/repo from git remote 'origin'. Run from inside a git repo with a GitHub remote.");
|
|
154
|
+
process.exit(2);
|
|
155
|
+
}
|
|
156
|
+
const { owner, repo } = detected;
|
|
157
|
+
const cfg = buildStepConfig(parsed.step, parsed.args);
|
|
158
|
+
const missing = cfg.requiredInputs.filter((k) => !cfg.inputs[k]);
|
|
159
|
+
if (missing.length > 0) {
|
|
160
|
+
console.error(`Missing required input(s) for '${parsed.step}': ${missing.join(", ")}.`);
|
|
161
|
+
printHelp();
|
|
162
|
+
process.exit(2);
|
|
163
|
+
}
|
|
164
|
+
const octokit = new Octokit({ auth: token, userAgent: "slowcook-ai/cli" });
|
|
165
|
+
try {
|
|
166
|
+
await octokit.actions.createWorkflowDispatch({
|
|
167
|
+
owner,
|
|
168
|
+
repo,
|
|
169
|
+
workflow_id: cfg.workflow,
|
|
170
|
+
ref: parsed.ref,
|
|
171
|
+
inputs: cfg.inputs,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
const status = e.status;
|
|
176
|
+
const msg = e.message;
|
|
177
|
+
if (status === 404) {
|
|
178
|
+
console.error(`Workflow '${cfg.workflow}' not found in ${owner}/${repo} (or missing 'workflow_dispatch' trigger). Verify the workflow exists on the ${parsed.ref} branch.`);
|
|
179
|
+
}
|
|
180
|
+
else if (status === 403) {
|
|
181
|
+
console.error(`GITHUB_TOKEN lacks permission to dispatch workflows in ${owner}/${repo}. Needs 'actions:write' scope.`);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.error(`Dispatch failed (${status ?? "no-status"}): ${msg}`);
|
|
185
|
+
}
|
|
186
|
+
process.exit(2);
|
|
187
|
+
}
|
|
188
|
+
console.log(`Dispatched '${parsed.step}' (${cfg.workflow}) on ${owner}/${repo} @ ${parsed.ref}.`);
|
|
189
|
+
const inputLines = Object.entries(cfg.inputs).filter(([, v]) => v);
|
|
190
|
+
if (inputLines.length > 0) {
|
|
191
|
+
console.log("Inputs:");
|
|
192
|
+
for (const [k, v] of inputLines)
|
|
193
|
+
console.log(` ${k}: ${v}`);
|
|
194
|
+
}
|
|
195
|
+
// Best-effort: fetch the most recent run for this workflow and print
|
|
196
|
+
// its URL so the operator can click through without hunting. Polls a
|
|
197
|
+
// couple of times because the dispatched run may not appear instantly.
|
|
198
|
+
const runUrl = await findRecentRunUrl(octokit, owner, repo, cfg.workflow);
|
|
199
|
+
if (runUrl) {
|
|
200
|
+
console.log(`\nRun: ${runUrl}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
console.log(`\nVisit https://github.com/${owner}/${repo}/actions/workflows/${cfg.workflow} to watch.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function findRecentRunUrl(octokit, owner, repo, workflow) {
|
|
207
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
208
|
+
await new Promise((resolve) => setTimeout(resolve, 500 + attempt * 500));
|
|
209
|
+
try {
|
|
210
|
+
const res = await octokit.actions.listWorkflowRuns({
|
|
211
|
+
owner,
|
|
212
|
+
repo,
|
|
213
|
+
workflow_id: workflow,
|
|
214
|
+
per_page: 3,
|
|
215
|
+
});
|
|
216
|
+
const run = res.data.workflow_runs.find((r) => r.event === "workflow_dispatch" &&
|
|
217
|
+
(r.status === "queued" || r.status === "in_progress"));
|
|
218
|
+
if (run)
|
|
219
|
+
return run.html_url;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
/* ignore; try again */
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/dispatch/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/on-brew-merged/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/on-brew-merged/index.ts"],"names":[],"mappings":"AAoGA,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoIhE"}
|
|
@@ -3,6 +3,7 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import YAML from "yaml";
|
|
5
5
|
import { Octokit } from "@octokit/rest";
|
|
6
|
+
import { parseCostMarkers } from "../refine/llm.js";
|
|
6
7
|
function parseArgs(argv) {
|
|
7
8
|
const args = {
|
|
8
9
|
prNumber: 0,
|
|
@@ -135,10 +136,52 @@ export async function onBrewMerged(argv) {
|
|
|
135
136
|
return;
|
|
136
137
|
}
|
|
137
138
|
console.log(`Posting brew-merged (shipped) comment on #${sourceIssue} (story-${storyId})`);
|
|
139
|
+
// Aggregate pipeline cost by reading existing comments on the source
|
|
140
|
+
// issue and summing slowcook:cost markers. Agents post these markers
|
|
141
|
+
// as hidden HTML comments in their audit-trail comments (0.7.9+).
|
|
142
|
+
// Best-effort — if the walk fails we still post the shipped message
|
|
143
|
+
// without the cost line.
|
|
144
|
+
let costSummaryMd = "";
|
|
145
|
+
try {
|
|
146
|
+
const comments = await octokit.paginate(octokit.issues.listComments, {
|
|
147
|
+
owner,
|
|
148
|
+
repo,
|
|
149
|
+
issue_number: sourceIssue,
|
|
150
|
+
per_page: 100,
|
|
151
|
+
});
|
|
152
|
+
const markers = comments
|
|
153
|
+
.flatMap((c) => parseCostMarkers(c.body ?? ""));
|
|
154
|
+
if (markers.length > 0) {
|
|
155
|
+
const byAgent = new Map();
|
|
156
|
+
for (const m of markers) {
|
|
157
|
+
const acc = byAgent.get(m.agent) ?? { usd: 0, n: 0 };
|
|
158
|
+
acc.usd += m.usd;
|
|
159
|
+
acc.n += 1;
|
|
160
|
+
byAgent.set(m.agent, acc);
|
|
161
|
+
}
|
|
162
|
+
const totalUsd = [...byAgent.values()].reduce((a, b) => a + b.usd, 0);
|
|
163
|
+
const order = ["refine", "testgen", "brew"];
|
|
164
|
+
const lines = [];
|
|
165
|
+
lines.push("**Pipeline cost:**");
|
|
166
|
+
for (const agent of order) {
|
|
167
|
+
const acc = byAgent.get(agent);
|
|
168
|
+
if (!acc)
|
|
169
|
+
continue;
|
|
170
|
+
const roundsNote = acc.n > 1 ? ` (${acc.n} run${acc.n === 1 ? "" : "s"})` : "";
|
|
171
|
+
lines.push(`- ${agent}${roundsNote}: $${acc.usd.toFixed(4)}`);
|
|
172
|
+
}
|
|
173
|
+
lines.push(`- **Total: $${totalUsd.toFixed(4)}**`);
|
|
174
|
+
costSummaryMd = "\n\n" + lines.join("\n") + "\n";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
console.log(` cost aggregation skipped: ${e.message}`);
|
|
179
|
+
}
|
|
138
180
|
const body = `### slowcook · shipped 🎉\n\n` +
|
|
139
181
|
`[PR #${args.prNumber}](https://github.com/${owner}/${repo}/pull/${args.prNumber}) merged — ` +
|
|
140
|
-
`\`story-${storyId}\` is now on main. This issue is considered shipped; feel free to close it.\n
|
|
141
|
-
|
|
182
|
+
`\`story-${storyId}\` is now on main. This issue is considered shipped; feel free to close it.\n` +
|
|
183
|
+
costSummaryMd +
|
|
184
|
+
`\nPipeline trail:\n` +
|
|
142
185
|
`- **refine** — \`spec-ready\` (earlier in this thread)\n` +
|
|
143
186
|
`- **testgen** — tests merged (earlier in this thread)\n` +
|
|
144
187
|
`- **brew** — this PR\n\n` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/on-brew-merged/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/on-brew-merged/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/commands/refine/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../src/commands/refine/agent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,UAAU,CAAC;AAEtD,OAAO,KAAK,EACV,YAAY,EAGZ,IAAI,EAEL,MAAM,mBAAmB,CAAC;AAwB3B,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AACrD,eAAO,MAAM,2BAA2B,0BAA0B,CAAC;AACnE,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AACvD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AACrD,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,0DAA2C,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,EAAE,SAAS,CAAC;IACf,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAKrC,wBAAsB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CA8N9E;AA4DD,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC;AA+BjC,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,GAChB,WAAW,CA6Cb"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import YAML from "yaml";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { costMarker } from "./llm.js";
|
|
3
4
|
import { REFINEMENT_ANALYST_SYSTEM, SPEC_CHECKLIST_MD, draftPrTitle, draftPrBody, } from "./prompts.js";
|
|
4
5
|
import { readIndex, writeIndex, writeSpec, listActiveSpecs, nextStoryId, entryFromSpec, } from "./spec-yaml.js";
|
|
5
6
|
import { buildProjectContext } from "./context.js";
|
|
@@ -32,26 +33,54 @@ export async function runRefinement(ctx) {
|
|
|
32
33
|
}
|
|
33
34
|
// Step 1: relationship analysis
|
|
34
35
|
const existingSpecs = listActiveSpecs(ctx.repoRoot);
|
|
35
|
-
const
|
|
36
|
+
const relationshipResult = await analyzeRelationship({ issueTitle: issue.title, issueBody: issue.body, activeSpecs: existingSpecs }, { llm: ctx.llm, model: ctx.relationshipModel });
|
|
37
|
+
const verdict = relationshipResult.verdict;
|
|
36
38
|
const hasChangeOfMind = issue.labels.includes(LABEL_CHANGE_OF_MIND);
|
|
39
|
+
// Accumulate cost across all LLM calls in this refine invocation so the
|
|
40
|
+
// final comment posted carries the full round cost. Relationship analysis
|
|
41
|
+
// + refinement call are the two calls.
|
|
42
|
+
let roundCostUsd = relationshipResult.costUsd;
|
|
43
|
+
let totalTokensIn = relationshipResult.usage.inputTokens;
|
|
44
|
+
let totalTokensOut = relationshipResult.usage.outputTokens;
|
|
45
|
+
let totalCacheRead = relationshipResult.usage.cacheReadTokens;
|
|
46
|
+
let totalCacheCreate = relationshipResult.usage.cacheCreateTokens;
|
|
37
47
|
if (verdict.kind === "overlap") {
|
|
38
|
-
const
|
|
48
|
+
const marker = costMarker({
|
|
49
|
+
agent: "refine",
|
|
50
|
+
usd: roundCostUsd,
|
|
51
|
+
tokensIn: totalTokensIn,
|
|
52
|
+
tokensOut: totalTokensOut,
|
|
53
|
+
cacheRead: totalCacheRead,
|
|
54
|
+
cacheCreate: totalCacheCreate,
|
|
55
|
+
model: ctx.relationshipModel,
|
|
56
|
+
round: "relationship-overlap",
|
|
57
|
+
});
|
|
58
|
+
const comment = await ctx.forge.createIssueComment(ctx.issueNumber, overlapCommentBody(verdict, existingSpecs) + "\n\n" + marker);
|
|
39
59
|
await ctx.forge.addIssueLabels(ctx.issueNumber, [LABEL_BLOCKED_OVERLAP]);
|
|
40
60
|
return { kind: "overlap-flagged", conflicting_ids: verdict.conflicting_ids };
|
|
41
61
|
}
|
|
42
62
|
if (verdict.kind === "follow_up") {
|
|
43
63
|
// Info only — refinement continues. Post the comment so the PM can see
|
|
44
64
|
// the agent noted the relationship + will cite it in related_specs.
|
|
45
|
-
// If we've already posted one for this issue, skip (dedup handled
|
|
46
|
-
// elsewhere in the agent's comment-seen logic).
|
|
47
65
|
await ctx.forge.createIssueComment(ctx.issueNumber, followUpCommentBody(verdict, existingSpecs));
|
|
48
66
|
// Intentionally no label — follow_up is not a blocker. Refinement
|
|
49
67
|
// continues below. The resulting spec's `related_specs` field will
|
|
50
|
-
// cite the predecessors.
|
|
68
|
+
// cite the predecessors. Cost for this relationship call rolls into
|
|
69
|
+
// the final refinement comment's marker.
|
|
51
70
|
}
|
|
52
71
|
if (verdict.kind === "contradiction") {
|
|
53
72
|
if (!hasChangeOfMind) {
|
|
54
|
-
|
|
73
|
+
const marker = costMarker({
|
|
74
|
+
agent: "refine",
|
|
75
|
+
usd: roundCostUsd,
|
|
76
|
+
tokensIn: totalTokensIn,
|
|
77
|
+
tokensOut: totalTokensOut,
|
|
78
|
+
cacheRead: totalCacheRead,
|
|
79
|
+
cacheCreate: totalCacheCreate,
|
|
80
|
+
model: ctx.relationshipModel,
|
|
81
|
+
round: "relationship-contradiction",
|
|
82
|
+
});
|
|
83
|
+
await ctx.forge.createIssueComment(ctx.issueNumber, contradictionCommentBody(verdict, false, existingSpecs) + "\n\n" + marker);
|
|
55
84
|
await ctx.forge.addIssueLabels(ctx.issueNumber, [LABEL_BLOCKED_CONTRADICTION]);
|
|
56
85
|
return { kind: "contradiction-blocked", conflicting_ids: verdict.conflicting_ids };
|
|
57
86
|
}
|
|
@@ -74,15 +103,30 @@ export async function runRefinement(ctx) {
|
|
|
74
103
|
maxTokens: 4096,
|
|
75
104
|
// temperature omitted — newer reasoning-enabled Claude models reject it.
|
|
76
105
|
});
|
|
77
|
-
|
|
106
|
+
roundCostUsd += agentResponse.costUsd;
|
|
107
|
+
totalTokensIn += agentResponse.usage.inputTokens;
|
|
108
|
+
totalTokensOut += agentResponse.usage.outputTokens;
|
|
109
|
+
totalCacheRead += agentResponse.usage.cacheReadTokens;
|
|
110
|
+
totalCacheCreate += agentResponse.usage.cacheCreateTokens;
|
|
111
|
+
const parsed = parseAgentOutput(agentResponse.text, {
|
|
78
112
|
storyId,
|
|
79
113
|
issueNumber: ctx.issueNumber,
|
|
80
114
|
createdAt: ctx.now.toISOString(),
|
|
81
115
|
cliVersion: ctx.cliVersion,
|
|
82
116
|
supersedes,
|
|
83
117
|
});
|
|
118
|
+
const refineCostMarker = costMarker({
|
|
119
|
+
agent: "refine",
|
|
120
|
+
usd: roundCostUsd,
|
|
121
|
+
tokensIn: totalTokensIn,
|
|
122
|
+
tokensOut: totalTokensOut,
|
|
123
|
+
cacheRead: totalCacheRead,
|
|
124
|
+
cacheCreate: totalCacheCreate,
|
|
125
|
+
model: ctx.refineModel,
|
|
126
|
+
round: parsed.kind === "questions" ? "questions" : "spec",
|
|
127
|
+
});
|
|
84
128
|
if (parsed.kind === "questions") {
|
|
85
|
-
const comment = await ctx.forge.createIssueComment(ctx.issueNumber, BRAND_HEADER + parsed.markdown);
|
|
129
|
+
const comment = await ctx.forge.createIssueComment(ctx.issueNumber, BRAND_HEADER + parsed.markdown + "\n\n" + refineCostMarker);
|
|
86
130
|
return { kind: "questions-posted", commentId: comment.id };
|
|
87
131
|
}
|
|
88
132
|
// Spec emitted → write, update index, open draft PR
|
|
@@ -116,6 +160,16 @@ export async function runRefinement(ctx) {
|
|
|
116
160
|
});
|
|
117
161
|
await ctx.forge.addIssueLabels(ctx.issueNumber, [LABEL_SPEC_SUBMITTED]);
|
|
118
162
|
await ctx.forge.removeIssueLabel(ctx.issueNumber, LABEL_NEEDS_REFINEMENT);
|
|
163
|
+
// Post a cost-carrying comment so the pipeline-total aggregator can
|
|
164
|
+
// see refine's spend alongside testgen's + brew's at the end. Best-effort.
|
|
165
|
+
try {
|
|
166
|
+
await ctx.forge.createIssueComment(ctx.issueNumber, `### slowcook · spec submitted\n\n` +
|
|
167
|
+
`Spec \`story-${spec.story_id}\` opened at [PR #${pr.number}](${pr.url}). Merge to trigger \`slowcook-testgen\`.\n\n` +
|
|
168
|
+
refineCostMarker);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
/* best effort */
|
|
172
|
+
}
|
|
119
173
|
return { kind: "spec-emitted", specPath, prUrl: pr.url, prNumber: pr.number };
|
|
120
174
|
}
|
|
121
175
|
catch (e) {
|