@slowcook-ai/cli 0.12.12 → 0.13.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/dist/cli.js +41 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/brew/agent.d.ts.map +1 -1
- package/dist/commands/brew/agent.js +19 -6
- package/dist/commands/brew/agent.js.map +1 -1
- package/dist/commands/chef/classify.d.ts +65 -0
- package/dist/commands/chef/classify.d.ts.map +1 -0
- package/dist/commands/chef/classify.js +102 -0
- package/dist/commands/chef/classify.js.map +1 -0
- package/dist/commands/chef/index.d.ts +22 -0
- package/dist/commands/chef/index.d.ts.map +1 -0
- package/dist/commands/chef/index.js +287 -0
- package/dist/commands/chef/index.js.map +1 -0
- package/dist/commands/dispatch/index.d.ts.map +1 -1
- package/dist/commands/dispatch/index.js +10 -3
- package/dist/commands/dispatch/index.js.map +1 -1
- package/dist/commands/investigate/agent.d.ts +68 -0
- package/dist/commands/investigate/agent.d.ts.map +1 -0
- package/dist/commands/investigate/agent.js +503 -0
- package/dist/commands/investigate/agent.js.map +1 -0
- package/dist/commands/investigate/index.d.ts +43 -0
- package/dist/commands/investigate/index.d.ts.map +1 -0
- package/dist/commands/investigate/index.js +413 -0
- package/dist/commands/investigate/index.js.map +1 -0
- package/dist/commands/investigate/prompts.d.ts +90 -0
- package/dist/commands/investigate/prompts.d.ts.map +1 -0
- package/dist/commands/investigate/prompts.js +237 -0
- package/dist/commands/investigate/prompts.js.map +1 -0
- package/dist/commands/investigate/schema.d.ts +91 -0
- package/dist/commands/investigate/schema.d.ts.map +1 -0
- package/dist/commands/investigate/schema.js +87 -0
- package/dist/commands/investigate/schema.js.map +1 -0
- package/dist/commands/on-brew-merged/index.d.ts.map +1 -1
- package/dist/commands/on-brew-merged/index.js +27 -6
- package/dist/commands/on-brew-merged/index.js.map +1 -1
- package/dist/commands/recipe-regression/agent.d.ts +94 -0
- package/dist/commands/recipe-regression/agent.d.ts.map +1 -0
- package/dist/commands/recipe-regression/agent.js +442 -0
- package/dist/commands/recipe-regression/agent.js.map +1 -0
- package/dist/commands/recipe-regression/index.d.ts +61 -0
- package/dist/commands/recipe-regression/index.d.ts.map +1 -0
- package/dist/commands/recipe-regression/index.js +187 -0
- package/dist/commands/recipe-regression/index.js.map +1 -0
- package/dist/commands/sift/agent.d.ts +52 -0
- package/dist/commands/sift/agent.d.ts.map +1 -0
- package/dist/commands/sift/agent.js +392 -0
- package/dist/commands/sift/agent.js.map +1 -0
- package/dist/commands/sift/index.d.ts +23 -0
- package/dist/commands/sift/index.d.ts.map +1 -0
- package/dist/commands/sift/index.js +314 -0
- package/dist/commands/sift/index.js.map +1 -0
- package/dist/commands/sift/prompts.d.ts +114 -0
- package/dist/commands/sift/prompts.d.ts.map +1 -0
- package/dist/commands/sift/prompts.js +193 -0
- package/dist/commands/sift/prompts.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.13.0-alpha.3b — LLM-backed regression test emitter.
|
|
3
|
+
*
|
|
4
|
+
* Replaces alpha.3a's `expect.fail()` stub with a real vitest file
|
|
5
|
+
* the agent writes by reading the bug profile + the failure-locus
|
|
6
|
+
* file. The emitted test must be RED against current code (the bug
|
|
7
|
+
* exists) and become GREEN once sift fixes it. That's the contract
|
|
8
|
+
* sift's red→green ratchet runs against.
|
|
9
|
+
*
|
|
10
|
+
* Different from sift: recipe-regression *only* writes the test
|
|
11
|
+
* file. It doesn't edit production code. Read tools available;
|
|
12
|
+
* write_file is restricted to the regression test path itself.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync, readdirSync, statSync, } from "node:fs";
|
|
15
|
+
import { resolve, isAbsolute, join } from "node:path";
|
|
16
|
+
import { execSync } from "node:child_process";
|
|
17
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
18
|
+
import { outlineFile } from "../brew/agent.js";
|
|
19
|
+
import { findReferences, findDefinition, renderReferences, } from "../brew/retrieval.js";
|
|
20
|
+
const MAX_ROUNDS = 8;
|
|
21
|
+
const MAX_FILE_READ_BYTES = 20000;
|
|
22
|
+
export const RECIPE_REGRESSION_SYSTEM = `You are the recipe agent (regression mode) for slowcook — a TDD-first bug-fix flow.
|
|
23
|
+
|
|
24
|
+
## Your role
|
|
25
|
+
|
|
26
|
+
You receive a bug profile from \`investigate\`. Your job: write a single vitest file that asserts the regression assertion(s) hold. The test you write is the contract \`sift\` will fix the code against.
|
|
27
|
+
|
|
28
|
+
You are NOT testgen. testgen writes acceptance tests for new features. You write a regression test for a known-broken behavior. Different posture:
|
|
29
|
+
|
|
30
|
+
- **The bug profile names the failure locus.** Read it (read_file). Understand what's broken. The diagnosis tells you why.
|
|
31
|
+
- **The regression test must be RED against current code.** If your test passes against the broken state, the bug profile is wrong OR your test isn't actually exercising the bug. Halt voluntarily — don't ship a passing regression test for a live bug.
|
|
32
|
+
- **The regression test must be GREEN once the bug is fixed.** It targets the corrected behavior, not the buggy state.
|
|
33
|
+
- **One test file. tests/regression/B-N-<slug>.test.ts.** Don't create helpers, don't edit fixtures, don't touch source code. The output is exactly one new file.
|
|
34
|
+
- **Test against the SMALLEST testable surface.** If the bug is in an API route, hit the route handler in isolation (mock the database). If it's in a component, render the component with stub props (mock fetch). Don't spin up the whole app.
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
- **read_file(path)** — read a file in full.
|
|
39
|
+
- **outline_file(path)** — compact outline (imports, top-level exports, signatures with line numbers).
|
|
40
|
+
- **list_directory(path)** — see what's in a dir.
|
|
41
|
+
- **find_references(symbol)** — find all use sites of a symbol.
|
|
42
|
+
- **find_definition(symbol)** — find where a symbol is declared.
|
|
43
|
+
- **grep(pattern, glob?)** — repo-wide ripgrep.
|
|
44
|
+
|
|
45
|
+
You do NOT have write_file — you produce the test file as a single \`<test_file>\` block in your final reply. Slowcook writes it to disk after parsing.
|
|
46
|
+
|
|
47
|
+
## Test conventions
|
|
48
|
+
|
|
49
|
+
The consumer project uses vitest. Match the patterns already in tests/integration/ — use \`vi.mock("@/utils/supabase/server")\`, \`renderWithProviders\`, \`mockFetch\` etc. when relevant. Don't invent new patterns.
|
|
50
|
+
|
|
51
|
+
For UI components, mock the data layer at the fetch boundary; for handlers, mock supabase via \`realShapedCreateClient\`. Read at least one existing tests/integration/ file before writing yours so you match the local conventions.
|
|
52
|
+
|
|
53
|
+
## Output format
|
|
54
|
+
|
|
55
|
+
A single \`<test_file>\` block whose content is the complete vitest source:
|
|
56
|
+
|
|
57
|
+
\`\`\`
|
|
58
|
+
<test_file>
|
|
59
|
+
// slowcook regression test — B-N
|
|
60
|
+
//
|
|
61
|
+
// (full test file contents)
|
|
62
|
+
import { describe, it, expect, vi } from "vitest";
|
|
63
|
+
// ...
|
|
64
|
+
|
|
65
|
+
describe("B-N regression — <bug title>", () => {
|
|
66
|
+
it("<regression assertion>", () => {
|
|
67
|
+
// ...
|
|
68
|
+
expect(...).toBe(...);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
</test_file>
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
If you can't write a test that's reliably red-against-current and green-against-fixed (e.g., the failure mode is non-deterministic, or you can't isolate the bug from network state), emit a \`<halt>\` block with a one-line description instead. **Don't ship a useless test.**
|
|
75
|
+
|
|
76
|
+
## Halt format
|
|
77
|
+
|
|
78
|
+
\`\`\`
|
|
79
|
+
<halt>
|
|
80
|
+
<reason>One-line description of what made writing the test impossible.</reason>
|
|
81
|
+
</halt>
|
|
82
|
+
\`\`\`
|
|
83
|
+
`;
|
|
84
|
+
export const RECIPE_REGRESSION_TOOLS = [
|
|
85
|
+
{
|
|
86
|
+
name: "read_file",
|
|
87
|
+
description: "Read a file's full contents.",
|
|
88
|
+
input_schema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
path: { type: "string", description: "Repo-relative path." },
|
|
92
|
+
},
|
|
93
|
+
required: ["path"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "outline_file",
|
|
98
|
+
description: "Compact outline (imports, exports, signatures with line numbers).",
|
|
99
|
+
input_schema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
path: { type: "string", description: "Repo-relative path." },
|
|
103
|
+
},
|
|
104
|
+
required: ["path"],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "list_directory",
|
|
109
|
+
description: "List entries in a directory.",
|
|
110
|
+
input_schema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
path: { type: "string", description: "Repo-relative path." },
|
|
114
|
+
},
|
|
115
|
+
required: ["path"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "find_references",
|
|
120
|
+
description: "All use sites of a symbol across the repo.",
|
|
121
|
+
input_schema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
symbol: { type: "string", description: "Identifier name." },
|
|
125
|
+
},
|
|
126
|
+
required: ["symbol"],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "find_definition",
|
|
131
|
+
description: "Where a symbol is declared.",
|
|
132
|
+
input_schema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
symbol: { type: "string", description: "Identifier name." },
|
|
136
|
+
},
|
|
137
|
+
required: ["symbol"],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "grep",
|
|
142
|
+
description: "Repo-wide ripgrep.",
|
|
143
|
+
input_schema: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
pattern: { type: "string", description: "Pattern." },
|
|
147
|
+
glob: { type: "string", description: "Optional glob restriction." },
|
|
148
|
+
},
|
|
149
|
+
required: ["pattern"],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
export async function runRegressionRecipe(ctx) {
|
|
154
|
+
const anthropic = new Anthropic({ apiKey: ctx.anthropicApiKey });
|
|
155
|
+
const userPrompt = buildUserPrompt(ctx.bugProfile);
|
|
156
|
+
const messages = [
|
|
157
|
+
{ role: "user", content: userPrompt },
|
|
158
|
+
];
|
|
159
|
+
let spendUsd = 0;
|
|
160
|
+
let finalText = "";
|
|
161
|
+
let rounds = 0;
|
|
162
|
+
for (let round = 0; round < MAX_ROUNDS; round++) {
|
|
163
|
+
rounds = round + 1;
|
|
164
|
+
const response = await anthropic.messages.create({
|
|
165
|
+
model: ctx.model,
|
|
166
|
+
max_tokens: 8192,
|
|
167
|
+
system: RECIPE_REGRESSION_SYSTEM,
|
|
168
|
+
tools: RECIPE_REGRESSION_TOOLS,
|
|
169
|
+
messages,
|
|
170
|
+
});
|
|
171
|
+
spendUsd += costUsd(response, ctx.model);
|
|
172
|
+
for (const block of response.content) {
|
|
173
|
+
if (block.type === "text")
|
|
174
|
+
finalText = block.text;
|
|
175
|
+
}
|
|
176
|
+
const toolUses = response.content.filter((b) => b.type === "tool_use");
|
|
177
|
+
if (response.stop_reason === "tool_use" && toolUses.length > 0) {
|
|
178
|
+
const toolResults = toolUses.map((t) => {
|
|
179
|
+
const r = executeTool(ctx.repoRoot, t);
|
|
180
|
+
return {
|
|
181
|
+
type: "tool_result",
|
|
182
|
+
tool_use_id: t.id,
|
|
183
|
+
content: r.content,
|
|
184
|
+
is_error: r.is_error,
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
messages.push({ role: "assistant", content: response.content });
|
|
188
|
+
messages.push({ role: "user", content: toolResults });
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
// Format-compliance retry — same pattern as investigate's
|
|
194
|
+
// alpha.3.1 fix. If the agent stopped without emitting a
|
|
195
|
+
// <test_file> or <halt> block, nudge it once.
|
|
196
|
+
if (!hasTestFileBlock(finalText) && !parseHalt(finalText)) {
|
|
197
|
+
rounds += 1;
|
|
198
|
+
messages.push({ role: "assistant", content: finalText });
|
|
199
|
+
messages.push({
|
|
200
|
+
role: "user",
|
|
201
|
+
content: "Your previous reply was free-form prose. Slowcook's parser greps for `<test_file>...</test_file>` (or `<halt>...</halt>`) literally. Re-emit your final answer wrapped in one of those two tags — nothing else will parse. The test file body goes between `<test_file>` and `</test_file>`, no fences, no commentary inside.",
|
|
202
|
+
});
|
|
203
|
+
const retry = await anthropic.messages.create({
|
|
204
|
+
model: ctx.model,
|
|
205
|
+
max_tokens: 8192,
|
|
206
|
+
system: RECIPE_REGRESSION_SYSTEM,
|
|
207
|
+
tools: RECIPE_REGRESSION_TOOLS,
|
|
208
|
+
messages,
|
|
209
|
+
});
|
|
210
|
+
spendUsd += costUsd(retry, ctx.model);
|
|
211
|
+
for (const block of retry.content) {
|
|
212
|
+
if (block.type === "text")
|
|
213
|
+
finalText = block.text;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const halt = parseHalt(finalText);
|
|
217
|
+
if (halt !== null) {
|
|
218
|
+
return { emitted: false, rounds, spendUsd, haltReason: halt, finalText };
|
|
219
|
+
}
|
|
220
|
+
const testContents = parseTestFileBlock(finalText);
|
|
221
|
+
if (!testContents) {
|
|
222
|
+
return {
|
|
223
|
+
emitted: false,
|
|
224
|
+
rounds,
|
|
225
|
+
spendUsd,
|
|
226
|
+
haltReason: "agent did not emit a <test_file> block (or <halt>) after the format-compliance retry",
|
|
227
|
+
finalText,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return { emitted: true, testContents, rounds, spendUsd, finalText };
|
|
231
|
+
}
|
|
232
|
+
function buildUserPrompt(profile) {
|
|
233
|
+
const lines = [];
|
|
234
|
+
lines.push(`# Write a regression test for ${profile.bug_id}`);
|
|
235
|
+
lines.push("");
|
|
236
|
+
lines.push("## Bug profile");
|
|
237
|
+
lines.push("```yaml");
|
|
238
|
+
lines.push(`bug_id: ${profile.bug_id}`);
|
|
239
|
+
lines.push(`title: ${JSON.stringify(profile.title)}`);
|
|
240
|
+
lines.push(`source_issue: "${profile.source_issue}"`);
|
|
241
|
+
lines.push(`symptom:`);
|
|
242
|
+
for (const s of profile.symptom)
|
|
243
|
+
lines.push(` - ${JSON.stringify(s)}`);
|
|
244
|
+
lines.push(`expected:`);
|
|
245
|
+
for (const s of profile.expected)
|
|
246
|
+
lines.push(` - ${JSON.stringify(s)}`);
|
|
247
|
+
lines.push(`reproduction:`);
|
|
248
|
+
for (const s of profile.reproduction)
|
|
249
|
+
lines.push(` - ${JSON.stringify(s)}`);
|
|
250
|
+
lines.push(`failure_locus:`);
|
|
251
|
+
lines.push(` file: ${JSON.stringify(profile.failure_locus.file)}`);
|
|
252
|
+
if (profile.failure_locus.line !== undefined) {
|
|
253
|
+
lines.push(` line: ${profile.failure_locus.line}`);
|
|
254
|
+
}
|
|
255
|
+
if (profile.failure_locus.function !== undefined) {
|
|
256
|
+
lines.push(` function: ${JSON.stringify(profile.failure_locus.function)}`);
|
|
257
|
+
}
|
|
258
|
+
lines.push(` diagnosis: |`);
|
|
259
|
+
for (const l of profile.failure_locus.diagnosis.split("\n")) {
|
|
260
|
+
lines.push(` ${l}`);
|
|
261
|
+
}
|
|
262
|
+
lines.push(`regression_assertion:`);
|
|
263
|
+
for (const a of profile.regression_assertion)
|
|
264
|
+
lines.push(` - ${JSON.stringify(a)}`);
|
|
265
|
+
lines.push(`fix_scope:`);
|
|
266
|
+
for (const s of profile.fix_scope)
|
|
267
|
+
lines.push(` - ${JSON.stringify(s)}`);
|
|
268
|
+
lines.push("```");
|
|
269
|
+
lines.push("");
|
|
270
|
+
lines.push("## Your task");
|
|
271
|
+
lines.push("1. Read the failure_locus file (and any other files you need to understand the contract).");
|
|
272
|
+
lines.push("2. Read at least one existing tests/integration/ file to match local conventions.");
|
|
273
|
+
lines.push("3. Write a vitest file that asserts the regression_assertion(s) hold.");
|
|
274
|
+
lines.push("4. Emit it inside a single `<test_file>...</test_file>` block.");
|
|
275
|
+
lines.push("");
|
|
276
|
+
lines.push(`The test will be saved at \`tests/regression/${profile.bug_id}-<slug>.test.ts\`. The slug is generated by slowcook from the bug title; you don't need to include the path in your output.`);
|
|
277
|
+
return lines.join("\n");
|
|
278
|
+
}
|
|
279
|
+
function executeTool(repoRoot, tool) {
|
|
280
|
+
const input = tool.input;
|
|
281
|
+
try {
|
|
282
|
+
switch (tool.name) {
|
|
283
|
+
case "read_file": {
|
|
284
|
+
const p = String(input["path"] ?? "");
|
|
285
|
+
if (!isPathSafe(repoRoot, p)) {
|
|
286
|
+
return { content: `Path escape forbidden: ${p}`, is_error: true };
|
|
287
|
+
}
|
|
288
|
+
const full = resolve(repoRoot, p);
|
|
289
|
+
if (!existsSync(full))
|
|
290
|
+
return { content: `File not found: ${p}`, is_error: true };
|
|
291
|
+
if (!statSync(full).isFile())
|
|
292
|
+
return { content: `Not a file: ${p}`, is_error: true };
|
|
293
|
+
const txt = readFileSync(full, "utf8");
|
|
294
|
+
return {
|
|
295
|
+
content: txt.length > MAX_FILE_READ_BYTES
|
|
296
|
+
? txt.slice(0, MAX_FILE_READ_BYTES) + "\n…(truncated)"
|
|
297
|
+
: txt,
|
|
298
|
+
is_error: false,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
case "outline_file": {
|
|
302
|
+
const p = String(input["path"] ?? "");
|
|
303
|
+
if (!isPathSafe(repoRoot, p)) {
|
|
304
|
+
return { content: `Path escape forbidden: ${p}`, is_error: true };
|
|
305
|
+
}
|
|
306
|
+
const full = resolve(repoRoot, p);
|
|
307
|
+
if (!existsSync(full))
|
|
308
|
+
return { content: `File not found: ${p}`, is_error: true };
|
|
309
|
+
const txt = readFileSync(full, "utf8");
|
|
310
|
+
return { content: outlineFile(p, txt), is_error: false };
|
|
311
|
+
}
|
|
312
|
+
case "list_directory": {
|
|
313
|
+
const p = String(input["path"] ?? "");
|
|
314
|
+
if (!isPathSafe(repoRoot, p)) {
|
|
315
|
+
return { content: `Path escape forbidden: ${p}`, is_error: true };
|
|
316
|
+
}
|
|
317
|
+
const full = resolve(repoRoot, p);
|
|
318
|
+
if (!existsSync(full))
|
|
319
|
+
return { content: `Not found: ${p}`, is_error: true };
|
|
320
|
+
if (!statSync(full).isDirectory()) {
|
|
321
|
+
return { content: `Not a directory: ${p}`, is_error: true };
|
|
322
|
+
}
|
|
323
|
+
const entries = readdirSync(full, { withFileTypes: true })
|
|
324
|
+
.map((e) => `${e.name}${e.isDirectory() ? "/" : ""}`)
|
|
325
|
+
.sort()
|
|
326
|
+
.join("\n");
|
|
327
|
+
return { content: entries, is_error: false };
|
|
328
|
+
}
|
|
329
|
+
case "find_references": {
|
|
330
|
+
const symbol = String(input["symbol"] ?? "").trim();
|
|
331
|
+
if (!symbol)
|
|
332
|
+
return { content: "symbol is required", is_error: true };
|
|
333
|
+
const refs = findReferences(repoRoot, symbol, { excludeDefinitions: false });
|
|
334
|
+
return { content: renderReferences(refs), is_error: false };
|
|
335
|
+
}
|
|
336
|
+
case "find_definition": {
|
|
337
|
+
const symbol = String(input["symbol"] ?? "").trim();
|
|
338
|
+
if (!symbol)
|
|
339
|
+
return { content: "symbol is required", is_error: true };
|
|
340
|
+
const def = findDefinition(repoRoot, symbol);
|
|
341
|
+
if (!def)
|
|
342
|
+
return { content: `(no declaration found for ${symbol})`, is_error: false };
|
|
343
|
+
return {
|
|
344
|
+
content: `${def.kind} | ${def.file}:${def.line}:${def.column} | ${def.context}`,
|
|
345
|
+
is_error: false,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
case "grep":
|
|
349
|
+
return runGrep(repoRoot, String(input["pattern"] ?? ""), input["glob"] ? String(input["glob"]) : undefined);
|
|
350
|
+
default:
|
|
351
|
+
return { content: `Unknown tool: ${tool.name}`, is_error: true };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
return { content: `Tool error: ${e.message}`, is_error: true };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function isPathSafe(repoRoot, relPath) {
|
|
359
|
+
if (isAbsolute(relPath))
|
|
360
|
+
return false;
|
|
361
|
+
const resolved = resolve(repoRoot, relPath);
|
|
362
|
+
return resolved.startsWith(resolve(repoRoot));
|
|
363
|
+
}
|
|
364
|
+
function runGrep(repoRoot, pattern, glob) {
|
|
365
|
+
if (!pattern)
|
|
366
|
+
return { content: "pattern is required", is_error: true };
|
|
367
|
+
const safe = pattern.replace(/'/g, "'\\''");
|
|
368
|
+
const cmd = glob
|
|
369
|
+
? `rg --line-number --max-count=50 -e '${safe}' --glob '${glob.replace(/'/g, "'\\''")}'`
|
|
370
|
+
: `rg --line-number --max-count=50 -e '${safe}'`;
|
|
371
|
+
try {
|
|
372
|
+
const out = execSync(cmd, {
|
|
373
|
+
cwd: repoRoot,
|
|
374
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
375
|
+
maxBuffer: 1024 * 256,
|
|
376
|
+
encoding: "utf8",
|
|
377
|
+
});
|
|
378
|
+
if (!out.trim())
|
|
379
|
+
return { content: "(no matches)", is_error: false };
|
|
380
|
+
return {
|
|
381
|
+
content: out.length > MAX_FILE_READ_BYTES
|
|
382
|
+
? out.slice(0, MAX_FILE_READ_BYTES) + "\n…(truncated)"
|
|
383
|
+
: out,
|
|
384
|
+
is_error: false,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
const exit = e.status;
|
|
389
|
+
if (exit === 1)
|
|
390
|
+
return { content: "(no matches)", is_error: false };
|
|
391
|
+
return { content: `grep error: ${e.message}`, is_error: true };
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// -------------------------------------------------------------------------
|
|
395
|
+
// Output parsing
|
|
396
|
+
// -------------------------------------------------------------------------
|
|
397
|
+
export function parseTestFileBlock(text) {
|
|
398
|
+
const m = text.match(/<test_file>([\s\S]*?)<\/test_file>/);
|
|
399
|
+
if (!m || !m[1])
|
|
400
|
+
return null;
|
|
401
|
+
// Strip optional ```ts/``` fences inside the block (common LLM
|
|
402
|
+
// habit — they wrap the test in markdown code fences inside the
|
|
403
|
+
// tag).
|
|
404
|
+
let body = m[1].trim();
|
|
405
|
+
body = body.replace(/^```(?:ts|tsx|typescript|js|jsx)?\n?/, "");
|
|
406
|
+
body = body.replace(/\n?```$/, "");
|
|
407
|
+
return body.trim() + "\n";
|
|
408
|
+
}
|
|
409
|
+
export function hasTestFileBlock(text) {
|
|
410
|
+
return /<test_file>[\s\S]*?<\/test_file>/.test(text);
|
|
411
|
+
}
|
|
412
|
+
export function parseHalt(text) {
|
|
413
|
+
const m = text.match(/<halt>[\s\S]*?<reason>([\s\S]*?)<\/reason>[\s\S]*?<\/halt>/);
|
|
414
|
+
if (m && m[1])
|
|
415
|
+
return m[1].trim();
|
|
416
|
+
const fallback = text.match(/<halt>([\s\S]*?)<\/halt>/);
|
|
417
|
+
return fallback ? (fallback[1] ?? "").trim() : null;
|
|
418
|
+
}
|
|
419
|
+
// -------------------------------------------------------------------------
|
|
420
|
+
// Cost (mirrors investigate's pricing)
|
|
421
|
+
// -------------------------------------------------------------------------
|
|
422
|
+
const PRICING_PER_M_TOKENS = {
|
|
423
|
+
"claude-opus-4-7": { input: 15, output: 75 },
|
|
424
|
+
"claude-sonnet-4-6": { input: 3, output: 15 },
|
|
425
|
+
"claude-sonnet-4-5": { input: 3, output: 15 },
|
|
426
|
+
"claude-haiku-4-5": { input: 1, output: 5 },
|
|
427
|
+
};
|
|
428
|
+
function costUsd(response, model) {
|
|
429
|
+
const pricing = PRICING_PER_M_TOKENS[model] ??
|
|
430
|
+
Object.entries(PRICING_PER_M_TOKENS).find(([k]) => model.startsWith(k))?.[1];
|
|
431
|
+
if (!pricing)
|
|
432
|
+
return 0;
|
|
433
|
+
const usage = response.usage;
|
|
434
|
+
const input = usage?.input_tokens ?? 0;
|
|
435
|
+
const output = usage?.output_tokens ?? 0;
|
|
436
|
+
const cacheRead = usage?.cache_read_input_tokens ?? 0;
|
|
437
|
+
const cacheCreate = usage?.cache_creation_input_tokens ?? 0;
|
|
438
|
+
const effectiveInput = input + cacheRead * 0.1 + cacheCreate * 1.25;
|
|
439
|
+
return (effectiveInput / 1_000_000) * pricing.input + (output / 1_000_000) * pricing.output;
|
|
440
|
+
}
|
|
441
|
+
void join;
|
|
442
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../src/commands/recipe-regression/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAElC,MAAM,CAAC,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DvC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,8BAA8B;QAC3C,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,qBAAqB,EAAE;aACtE;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,mEAAmE;QAChF,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,qBAAqB,EAAE;aACtE;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,8BAA8B;QAC3C,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,qBAAqB,EAAE;aACtE;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,4CAA4C;QACzD,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,kBAAkB,EAAE;aACrE;YACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;SACrB;KACF;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,6BAA6B;QAC1C,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,kBAAkB,EAAE;aACrE;YACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;SACrB;KACF;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,oBAAoB;QACjC,YAAY,EAAE;YACZ,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,OAAO,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,UAAU,EAAE;gBAC7D,IAAI,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,4BAA4B,EAAE;aAC7E;YACD,QAAQ,EAAE,CAAC,SAAS,CAAC;SACtB;KACF;CACF,CAAC;AA0BF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAA4B;IAE5B,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAsC;QAClD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE;KACtC,CAAC;IAEF,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC;QAChD,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC;QACnB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC/C,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,wBAAwB;YAChC,KAAK,EAAE,uBAAuB;YAC9B,QAAQ;SACT,CAAC,CAAC;QACH,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAEzC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;QACpD,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CACtC,CAAC,CAAC,EAAwC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CACnE,CAAC;QACF,IAAI,QAAQ,CAAC,WAAW,KAAK,UAAU,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/D,MAAM,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACrC,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBACvC,OAAO;oBACL,IAAI,EAAE,aAAsB;oBAC5B,WAAW,EAAE,CAAC,CAAC,EAAE;oBACjB,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;iBACrB,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YACtD,SAAS;QACX,CAAC;QAED,MAAM;IACR,CAAC;IAED,0DAA0D;IAC1D,yDAAyD;IACzD,8CAA8C;IAC9C,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,CAAC,CAAC;QACZ,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACzD,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,MAAM;YACZ,OAAO,EACL,+TAA+T;SAClU,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,wBAAwB;YAChC,KAAK,EAAE,uBAAuB;YAC9B,QAAQ;SACT,CAAC,CAAC;QACH,QAAQ,IAAI,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;QACpD,CAAC;IACH,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,YAAY,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM;YACN,QAAQ;YACR,UAAU,EACR,sFAAsF;YACxF,SAAS;SACV,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,eAAe,CAAC,OAAmB;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,iCAAiC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC7B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC7B,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpE,IAAI,OAAO,CAAC,aAAa,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,CAAC,aAAa,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,oBAAoB;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1E,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3B,KAAK,CAAC,IAAI,CACR,2FAA2F,CAC5F,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;IAChG,KAAK,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;IACpF,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC7E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,gDAAgD,OAAO,CAAC,MAAM,6HAA6H,CAC5L,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAWD,SAAS,WAAW,CAClB,QAAgB,EAChB,IAAqC;IAErC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAgC,CAAC;IACpD,IAAI,CAAC;QACH,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC7B,OAAO,EAAE,OAAO,EAAE,0BAA0B,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACpE,CAAC;gBACD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAClF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE;oBAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACrF,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvC,OAAO;oBACL,OAAO,EACL,GAAG,CAAC,MAAM,GAAG,mBAAmB;wBAC9B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,GAAG,gBAAgB;wBACtD,CAAC,CAAC,GAAG;oBACT,QAAQ,EAAE,KAAK;iBAChB,CAAC;YACJ,CAAC;YACD,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC7B,OAAO,EAAE,OAAO,EAAE,0BAA0B,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACpE,CAAC;gBACD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAClF,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC3D,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;oBAC7B,OAAO,EAAE,OAAO,EAAE,0BAA0B,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACpE,CAAC;gBACD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBAAE,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAC7E,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;oBAClC,OAAO,EAAE,OAAO,EAAE,oBAAoB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBAC9D,CAAC;gBACD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;qBACvD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;qBACpD,IAAI,EAAE;qBACN,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC/C,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACpD,IAAI,CAAC,MAAM;oBAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACtE,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC7E,OAAO,EAAE,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;YAC9D,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;gBACpD,IAAI,CAAC,MAAM;oBAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;gBACtE,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC7C,IAAI,CAAC,GAAG;oBAAE,OAAO,EAAE,OAAO,EAAE,6BAA6B,MAAM,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;gBACtF,OAAO;oBACL,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE;oBAC/E,QAAQ,EAAE,KAAK;iBAChB,CAAC;YACJ,CAAC;YACD,KAAK,MAAM;gBACT,OAAO,OAAO,CACZ,QAAQ,EACR,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EAC9B,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAClD,CAAC;YACJ;gBACE,OAAO,EAAE,OAAO,EAAE,iBAAiB,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QACrE,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,OAAO,EAAE,eAAgB,CAAW,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB,EAAE,OAAe;IACnD,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,OAAO,CACd,QAAgB,EAChB,OAAe,EACf,IAAa;IAEb,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI;QACd,CAAC,CAAC,uCAAuC,IAAI,aAAa,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG;QACxF,CAAC,CAAC,uCAAuC,IAAI,GAAG,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE;YACxB,GAAG,EAAE,QAAQ;YACb,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACjC,SAAS,EAAE,IAAI,GAAG,GAAG;YACrB,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACrE,OAAO;YACL,OAAO,EACL,GAAG,CAAC,MAAM,GAAG,mBAAmB;gBAC9B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,GAAG,gBAAgB;gBACtD,CAAC,CAAC,GAAG;YACT,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,GAAI,CAAyB,CAAC,MAAM,CAAC;QAC/C,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,eAAgB,CAAW,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,iBAAiB;AACjB,4EAA4E;AAE5E,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAC3D,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,+DAA+D;IAC/D,gEAAgE;IAChE,QAAQ;IACR,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,sCAAsC,EAAE,EAAE,CAAC,CAAC;IAChE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;IACnF,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxD,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,4EAA4E;AAC5E,uCAAuC;AACvC,4EAA4E;AAE5E,MAAM,oBAAoB,GAAsD;IAC9E,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IAC5C,mBAAmB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;IAC7C,mBAAmB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;IAC7C,kBAAkB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;CAC5C,CAAC;AAEF,SAAS,OAAO,CACd,QAAoC,EACpC,KAAa;IAEb,MAAM,OAAO,GACX,oBAAoB,CAAC,KAAK,CAAC;QAC3B,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IACvB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAOV,CAAC;IACd,MAAM,KAAK,GAAG,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,KAAK,EAAE,uBAAuB,IAAI,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,KAAK,EAAE,2BAA2B,IAAI,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,GAAG,GAAG,GAAG,WAAW,GAAG,IAAI,CAAC;IACpE,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;AAC9F,CAAC;AAED,KAAK,IAAI,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook recipe --regression --bug B-<n>` — emits a regression
|
|
3
|
+
* test from a bug-profile.
|
|
4
|
+
*
|
|
5
|
+
* **Status: alpha.3a**. Stub-only emitter: writes a deterministic
|
|
6
|
+
* vitest skeleton at `tests/regression/B-<n>-<slug>.test.ts` that
|
|
7
|
+
* asserts via `expect.fail()` so the test is red until sift replaces
|
|
8
|
+
* the body with real assertions (alpha.4) or alpha.3b upgrades this
|
|
9
|
+
* emitter to write real tests via an LLM agent.
|
|
10
|
+
*
|
|
11
|
+
* The skeleton structure is what sift expects to see:
|
|
12
|
+
* - one `describe` named for the bug id + title
|
|
13
|
+
* - one `it` per regression_assertion line
|
|
14
|
+
* - body of each `it` calls `expect.fail(...)` referencing the bug
|
|
15
|
+
* profile so the failure message points the operator at the right
|
|
16
|
+
* artefact when the test runs.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* slowcook recipe --regression --bug B-1 [--cwd <path>]
|
|
20
|
+
*
|
|
21
|
+
* Internally invoked from the CLI's `recipe`/`testgen` dispatch when
|
|
22
|
+
* `--regression` is present (see cli.ts wiring).
|
|
23
|
+
*/
|
|
24
|
+
import { type BugProfile } from "../investigate/schema.js";
|
|
25
|
+
export interface RecipeRegressionArgs {
|
|
26
|
+
bugId: string;
|
|
27
|
+
repoRoot: string;
|
|
28
|
+
dryRun: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* 0.13.0-alpha.3b: when true, route through the LLM-backed emitter
|
|
31
|
+
* (real test, can be flipped green by sift). When false, fall back
|
|
32
|
+
* to the deterministic alpha.3a stub (expect.fail body — only
|
|
33
|
+
* useful for testing the file-system layout). Stub stays as the
|
|
34
|
+
* default through 0.13.0 because LLM mode requires ANTHROPIC_API_KEY
|
|
35
|
+
* + actually exercises the agent; CI / scripted runs that just
|
|
36
|
+
* want a placeholder file don't want an LLM call.
|
|
37
|
+
*/
|
|
38
|
+
useLlm: boolean;
|
|
39
|
+
/** Anthropic model (LLM mode only). Default sonnet — single-shot
|
|
40
|
+
* regression test emission shouldn't need Opus. */
|
|
41
|
+
model: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function parseRecipeRegressionArgs(argv: string[]): RecipeRegressionArgs;
|
|
44
|
+
/**
|
|
45
|
+
* Accept "1", "B-1", or "B1" and normalise to "B-<n>".
|
|
46
|
+
*/
|
|
47
|
+
export declare function normaliseBugId(raw: string): string;
|
|
48
|
+
export declare function recipeRegression(argv: string[], cliVersion: string): Promise<void>;
|
|
49
|
+
export declare function loadBugProfile(repoRoot: string, bugId: string): BugProfile;
|
|
50
|
+
export interface RegressionFile {
|
|
51
|
+
path: string;
|
|
52
|
+
contents: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the stub regression test for a bug profile. Deterministic —
|
|
56
|
+
* same profile → same file. Sift overwrites with real assertions in
|
|
57
|
+
* alpha.4; alpha.3b will replace this stub emitter with an LLM-written
|
|
58
|
+
* real test.
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderRegressionStub(profile: BugProfile, cliVersion: string): RegressionFile;
|
|
61
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/recipe-regression/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI/E,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB;wDACoD;IACpD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,oBAAoB,CA2B9E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAyDf;AAUD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAe1E;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,MAAM,GACjB,cAAc,CAkChB"}
|