@slowcook-ai/cli 0.4.4 → 0.4.6
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 +29 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/init/plan.d.ts.map +1 -1
- package/dist/commands/init/plan.js +7 -1
- package/dist/commands/init/plan.js.map +1 -1
- package/dist/commands/init/templates.d.ts +3 -1
- package/dist/commands/init/templates.d.ts.map +1 -1
- package/dist/commands/init/templates.js +98 -4
- package/dist/commands/init/templates.js.map +1 -1
- package/dist/commands/on-spec-merged/index.d.ts +2 -0
- package/dist/commands/on-spec-merged/index.d.ts.map +1 -0
- package/dist/commands/on-spec-merged/index.js +176 -0
- package/dist/commands/on-spec-merged/index.js.map +1 -0
- package/dist/commands/refine/agent.d.ts +1 -0
- package/dist/commands/refine/agent.d.ts.map +1 -1
- package/dist/commands/refine/agent.js +6 -3
- package/dist/commands/refine/agent.js.map +1 -1
- package/dist/commands/refine/context.d.ts +17 -0
- package/dist/commands/refine/context.d.ts.map +1 -0
- package/dist/commands/refine/context.js +69 -0
- package/dist/commands/refine/context.js.map +1 -0
- package/dist/commands/refine/prompts.d.ts +1 -1
- package/dist/commands/refine/prompts.d.ts.map +1 -1
- package/dist/commands/refine/prompts.js +7 -1
- package/dist/commands/refine/prompts.js.map +1 -1
- package/dist/commands/testgen/agent.d.ts +55 -0
- package/dist/commands/testgen/agent.d.ts.map +1 -0
- package/dist/commands/testgen/agent.js +317 -0
- package/dist/commands/testgen/agent.js.map +1 -0
- package/dist/commands/testgen/index.d.ts +2 -0
- package/dist/commands/testgen/index.d.ts.map +1 -0
- package/dist/commands/testgen/index.js +173 -0
- package/dist/commands/testgen/index.js.map +1 -0
- package/dist/commands/testgen/prompts.d.ts +12 -0
- package/dist/commands/testgen/prompts.d.ts.map +1 -0
- package/dist/commands/testgen/prompts.js +54 -0
- package/dist/commands/testgen/prompts.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { GitHubAdapter } from "@slowcook-ai/forge-github";
|
|
3
|
+
import { AnthropicClient } from "../refine/llm.js";
|
|
4
|
+
import { runTestgen } from "./agent.js";
|
|
5
|
+
function parseArgs(argv) {
|
|
6
|
+
const args = {
|
|
7
|
+
specId: null,
|
|
8
|
+
all: false,
|
|
9
|
+
repoRoot: process.cwd(),
|
|
10
|
+
baseBranch: "main",
|
|
11
|
+
model: "claude-opus-4-7",
|
|
12
|
+
};
|
|
13
|
+
for (let i = 0; i < argv.length; i++) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
const next = argv[i + 1];
|
|
16
|
+
if (arg === "--spec" && next) {
|
|
17
|
+
args.specId = next;
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
else if (arg === "--all") {
|
|
21
|
+
args.all = true;
|
|
22
|
+
}
|
|
23
|
+
else if (arg === "--cwd" && next) {
|
|
24
|
+
args.repoRoot = next;
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
else if (arg === "--owner" && next) {
|
|
28
|
+
args.owner = next;
|
|
29
|
+
i++;
|
|
30
|
+
}
|
|
31
|
+
else if (arg === "--repo" && next) {
|
|
32
|
+
args.repo = next;
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
else if (arg === "--base" && next) {
|
|
36
|
+
args.baseBranch = next;
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--model" && next) {
|
|
40
|
+
args.model = next;
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
else if (arg === "--help" || arg === "-h") {
|
|
44
|
+
printHelp();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Default: operate on all active specs lacking tests
|
|
49
|
+
if (!args.specId)
|
|
50
|
+
args.all = true;
|
|
51
|
+
return args;
|
|
52
|
+
}
|
|
53
|
+
function printHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
slowcook testgen — generate Vitest integration tests from merged specs
|
|
56
|
+
|
|
57
|
+
Runs over active specs that lack test files and produces a single draft PR
|
|
58
|
+
containing:
|
|
59
|
+
- tests/integration/story-N.test.ts (Vitest, HTTP-style integration)
|
|
60
|
+
- .brewing/manifests/story-N.json (the frozen test manifest for N)
|
|
61
|
+
|
|
62
|
+
If any spec has \`supersedes: [X, Y]\`, the old tests/manifests for X and Y
|
|
63
|
+
are removed in the same PR; \`override-freeze\` is auto-applied because the
|
|
64
|
+
supersede chain in the spec provides the audit trail.
|
|
65
|
+
|
|
66
|
+
Usage:
|
|
67
|
+
slowcook testgen [--spec <id>] [--all] [options]
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--spec <id> Generate tests for a specific story id (re-runs even if
|
|
71
|
+
tests exist).
|
|
72
|
+
--all Generate tests for every active spec that lacks them.
|
|
73
|
+
(Default when --spec is not set.)
|
|
74
|
+
--cwd <path> Repo working directory (default: .)
|
|
75
|
+
--owner <login> Repo owner (default: from \`git remote get-url origin\`)
|
|
76
|
+
--repo <name> Repo name (default: from \`git remote get-url origin\`)
|
|
77
|
+
--base <branch> Base branch for the tests PR (default: main)
|
|
78
|
+
--model <id> LLM model (default: claude-opus-4-7)
|
|
79
|
+
--help, -h Show this help
|
|
80
|
+
|
|
81
|
+
Environment:
|
|
82
|
+
ANTHROPIC_API_KEY (required) Anthropic API key
|
|
83
|
+
GITHUB_TOKEN (required) GitHub token with contents/pull-requests write
|
|
84
|
+
|
|
85
|
+
Exit codes:
|
|
86
|
+
0 outcome reached (PR opened, or nothing to generate)
|
|
87
|
+
2 script error (bad args, missing env, network failure)
|
|
88
|
+
`);
|
|
89
|
+
}
|
|
90
|
+
function detectOwnerRepo(cwd) {
|
|
91
|
+
try {
|
|
92
|
+
const url = execSync("git remote get-url origin", {
|
|
93
|
+
cwd,
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
96
|
+
}).trim();
|
|
97
|
+
const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);
|
|
98
|
+
if (m && m[1] && m[2])
|
|
99
|
+
return { owner: m[1], repo: m[2] };
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* not a git repo */
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
export async function testgen(argv, cliVersion) {
|
|
107
|
+
const args = parseArgs(argv);
|
|
108
|
+
const anthropicKey = process.env["ANTHROPIC_API_KEY"];
|
|
109
|
+
if (!anthropicKey) {
|
|
110
|
+
console.error("ANTHROPIC_API_KEY environment variable is not set.");
|
|
111
|
+
process.exit(2);
|
|
112
|
+
}
|
|
113
|
+
const githubToken = process.env["GITHUB_TOKEN"];
|
|
114
|
+
if (!githubToken) {
|
|
115
|
+
console.error("GITHUB_TOKEN environment variable is not set.");
|
|
116
|
+
process.exit(2);
|
|
117
|
+
}
|
|
118
|
+
let owner = args.owner;
|
|
119
|
+
let repo = args.repo;
|
|
120
|
+
if (!owner || !repo) {
|
|
121
|
+
const detected = detectOwnerRepo(args.repoRoot);
|
|
122
|
+
if (!detected) {
|
|
123
|
+
console.error("Could not detect owner/repo from git remote. Pass --owner and --repo explicitly.");
|
|
124
|
+
process.exit(2);
|
|
125
|
+
}
|
|
126
|
+
owner = owner ?? detected.owner;
|
|
127
|
+
repo = repo ?? detected.repo;
|
|
128
|
+
}
|
|
129
|
+
const forge = new GitHubAdapter({ owner, repo, token: githubToken });
|
|
130
|
+
const llm = new AnthropicClient(anthropicKey);
|
|
131
|
+
const branchName = args.specId
|
|
132
|
+
? `slowcook/tests/story-${args.specId}`
|
|
133
|
+
: `slowcook/tests/batch-${Date.now()}`;
|
|
134
|
+
const ctx = {
|
|
135
|
+
repoRoot: args.repoRoot,
|
|
136
|
+
forge,
|
|
137
|
+
llm,
|
|
138
|
+
model: args.model,
|
|
139
|
+
cliVersion,
|
|
140
|
+
baseBranch: args.baseBranch,
|
|
141
|
+
all: args.all,
|
|
142
|
+
specId: args.specId,
|
|
143
|
+
branchName,
|
|
144
|
+
now: new Date(),
|
|
145
|
+
};
|
|
146
|
+
console.log(`slowcook testgen · ${args.specId ? `story-${args.specId}` : "all active specs"} on ${owner}/${repo}`);
|
|
147
|
+
try {
|
|
148
|
+
const outcome = await runTestgen(ctx);
|
|
149
|
+
switch (outcome.kind) {
|
|
150
|
+
case "tests-emitted":
|
|
151
|
+
console.log(`Tests written for ${outcome.storyIds.map((s) => `story-${s}`).join(", ")}`);
|
|
152
|
+
if (outcome.removedStoryIds.length > 0) {
|
|
153
|
+
console.log(`Removed superseded tests: ${outcome.removedStoryIds.map((s) => `story-${s}`).join(", ")}`);
|
|
154
|
+
}
|
|
155
|
+
console.log(`Draft PR: ${outcome.prUrl}`);
|
|
156
|
+
break;
|
|
157
|
+
case "nothing-to-generate":
|
|
158
|
+
console.log(`Noop: ${outcome.reason}.`);
|
|
159
|
+
break;
|
|
160
|
+
case "pr-creation-blocked":
|
|
161
|
+
console.log(`Tests committed and pushed to branch '${outcome.branchName}', but PR creation was blocked (403). Enable "Allow GitHub Actions to create and approve pull requests" and re-run, or open the PR manually.`);
|
|
162
|
+
process.exit(2);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
console.error(`testgen failed: ${e.message}`);
|
|
167
|
+
if (process.env["SLOWCOOK_DEBUG"]) {
|
|
168
|
+
console.error(e);
|
|
169
|
+
}
|
|
170
|
+
process.exit(2);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/testgen/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt for the test-gen agent. Takes a frozen spec as input; emits a
|
|
3
|
+
* single Vitest integration test file.
|
|
4
|
+
*
|
|
5
|
+
* The agent does NOT read the consumer's src/. It tests the spec's contract,
|
|
6
|
+
* not the implementation. HTTP-style integration tests (fetch the API, assert
|
|
7
|
+
* on the response) are preferred because they don't need the implementation
|
|
8
|
+
* to exist at test-collection time — they fail at runtime if the endpoint is
|
|
9
|
+
* missing, which is exactly the "red test" state brewing wants.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TESTGEN_SYSTEM: (projectContext: string) => string;
|
|
12
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/commands/testgen/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,cAAc,GAAI,gBAAgB,MAAM,WA0CwK,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt for the test-gen agent. Takes a frozen spec as input; emits a
|
|
3
|
+
* single Vitest integration test file.
|
|
4
|
+
*
|
|
5
|
+
* The agent does NOT read the consumer's src/. It tests the spec's contract,
|
|
6
|
+
* not the implementation. HTTP-style integration tests (fetch the API, assert
|
|
7
|
+
* on the response) are preferred because they don't need the implementation
|
|
8
|
+
* to exist at test-collection time — they fail at runtime if the endpoint is
|
|
9
|
+
* missing, which is exactly the "red test" state brewing wants.
|
|
10
|
+
*/
|
|
11
|
+
export const TESTGEN_SYSTEM = (projectContext) => `You are a rigorous test engineer for the slowcook brewing harness.
|
|
12
|
+
|
|
13
|
+
Your job is to turn a frozen spec YAML into ONE Vitest integration test file that covers every acceptance scenario in the spec, plus invariant checks and key API-contract edge cases.
|
|
14
|
+
|
|
15
|
+
## Input
|
|
16
|
+
|
|
17
|
+
You will receive:
|
|
18
|
+
- The full spec YAML (already validated, frozen).
|
|
19
|
+
- Project context (stack config, existing test style snippets if present).
|
|
20
|
+
|
|
21
|
+
## Output format
|
|
22
|
+
|
|
23
|
+
Emit ONLY the TypeScript test file contents. No prose before or after, no code fences. Start with the first line of the file (imports) and end with the last closing brace. The file must:
|
|
24
|
+
|
|
25
|
+
- Be valid TypeScript that Vitest can collect (imports resolve or fail at RUNTIME, not at collection — prefer HTTP calls over direct function imports).
|
|
26
|
+
- Use \`describe\` / \`it\` from Vitest.
|
|
27
|
+
- Use \`beforeEach\` / \`afterEach\` for setup/teardown if needed.
|
|
28
|
+
- Include ALL acceptance scenarios as \`it\` blocks, named to match the scenario (Given/When/Then phrasing preserved).
|
|
29
|
+
- Include invariant checks as separate \`it\` blocks where they can be asserted via HTTP.
|
|
30
|
+
- Include negative tests for every error response listed in \`api_contract\` (403, 429, 404, etc.).
|
|
31
|
+
- Include edge cases for each invariant (e.g., "exactly at the boundary").
|
|
32
|
+
|
|
33
|
+
## Stylistic conventions
|
|
34
|
+
|
|
35
|
+
- One describe block per API endpoint or per cohesive topic.
|
|
36
|
+
- Test names use natural language matching acceptance scenarios, e.g. \`it("rejects a self-report with 409", ...)\`.
|
|
37
|
+
- Use \`fetch\` (global) for HTTP calls — avoid importing app code. This keeps tests runnable even before implementation exists.
|
|
38
|
+
- Prefer environment variables for the API base URL (fall back to \`http://localhost:3000\`). Expose via a local const.
|
|
39
|
+
- If the spec references a DB table, DO NOT import the DB client directly. Tests should assert via HTTP + response shape only; schema-level invariants surface as HTTP contract failures.
|
|
40
|
+
- Mark deliberately-incomplete assertions with a \`// TODO(story-N): ...\` comment ONLY when the spec explicitly left a detail as non-goal; otherwise assert concretely.
|
|
41
|
+
|
|
42
|
+
## Project context (consumer's conventions)
|
|
43
|
+
|
|
44
|
+
${projectContext}
|
|
45
|
+
|
|
46
|
+
## Do NOT
|
|
47
|
+
|
|
48
|
+
- Read or reference files outside the spec. Don't import types from the consumer's source code. The spec is the contract.
|
|
49
|
+
- Skip an acceptance scenario. Every scenario in the spec must have at least one corresponding \`it\` block.
|
|
50
|
+
- Write flaky tests. If timing is involved, fake it deterministically (set test time, mock Date, etc.).
|
|
51
|
+
- Use \`test.skip\` / \`test.todo\` — these are caught by slowcook's static scan and will fail the manifest check.
|
|
52
|
+
|
|
53
|
+
Produce a complete, parseable file that, when executed against an unimplemented API, fails with clear messages (status code mismatches, response shape mismatches). That is the desired "red" state brewing will turn green.`;
|
|
54
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/commands/testgen/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,cAAsB,EAAE,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCxD,cAAc;;;;;;;;;6NAS6M,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slowcook-ai/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "CLI for the slowcook brewing harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "aminazar",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@anthropic-ai/sdk": "^0.32.1",
|
|
37
|
+
"@octokit/rest": "^21.0.2",
|
|
37
38
|
"yaml": "^2.6.0",
|
|
38
39
|
"zod": "^3.23.8",
|
|
39
|
-
"@slowcook-ai/forge-github": "^0.4.2",
|
|
40
40
|
"@slowcook-ai/core": "^0.4.0",
|
|
41
|
-
"@slowcook-ai/stack-ts": "^0.4.0"
|
|
41
|
+
"@slowcook-ai/stack-ts": "^0.4.0",
|
|
42
|
+
"@slowcook-ai/forge-github": "^0.4.2"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
45
|
"access": "public"
|