@slowcook-ai/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,33 @@ CLI for the slowcook brewing harness. Installs the `slowcook` binary.
8
8
  npm i -D @slowcook-ai/cli
9
9
  ```
10
10
 
11
- ## Commands (v0.1)
11
+ ## Commands (v0.3)
12
+
13
+ ### `slowcook init`
14
+
15
+ Scaffold slowcook configuration in a consumer project. Writes `.brewing/*`, `.github/workflows/slowcook.yml`, and a `CODEOWNERS` section. Idempotent — re-running skips existing files unless `--force`.
16
+
17
+ ```bash
18
+ npx slowcook init [--owner <handle>] [--force] [--dry-run] [--cwd <path>]
19
+ ```
20
+
21
+ **Options:**
22
+
23
+ | Flag | Default | Description |
24
+ |---|---|---|
25
+ | `--cwd <path>` | `.` | Target project directory |
26
+ | `--owner <handle>` | detected from git remote | CODEOWNERS handle/team (e.g. `@aminazar`, `@acme/frontend`) |
27
+ | `--force` | false | Overwrite existing slowcook files |
28
+ | `--dry-run` | false | Print the plan without writing anything |
29
+
30
+ **Stack detection (0.3):** reads `package.json`. Requires Vitest in `devDependencies`. If Playwright is present, it's noted as a warning and left out of `stack.json` until slowcook supports Playwright discovery.
31
+
32
+ **CODEOWNERS handling:** uses `# --- slowcook:frozen-paths BEGIN/END ---` markers so re-running or adopting slowcook in a repo that already has a `CODEOWNERS` is safe.
33
+
34
+ **Exit codes:**
35
+
36
+ - `0` — success (or dry-run completed)
37
+ - `2` — script error (no `package.json`, vitest not found, invalid JSON)
12
38
 
13
39
  ### `slowcook guard`
14
40
 
package/dist/cli.js CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { guard } from "./commands/guard.js";
3
3
  import { manifest } from "./commands/manifest.js";
4
- const VERSION = "0.2.0";
4
+ import { init } from "./commands/init/index.js";
5
+ const VERSION = "0.3.0";
5
6
  const USAGE = `
6
7
  slowcook — TDD-first agentic development harness
7
8
 
8
9
  Usage:
10
+ slowcook init [--owner <handle>] [--force] [--dry-run] [--cwd <path>]
9
11
  slowcook guard --base <ref> --head <ref> [--override] [--config <path>]
10
12
  slowcook manifest record [--stack-config <path>] [--manifest <path>] [--story <id>]
11
13
  slowcook manifest verify [--stack-config <path>] [--manifest <path>] [--story <id>]
@@ -13,11 +15,12 @@ Usage:
13
15
  slowcook help
14
16
 
15
17
  Commands available in ${VERSION}:
18
+ init Scaffold slowcook configuration in a consumer project.
16
19
  guard Check for frozen-path violations between two git refs.
17
20
  manifest Record or verify the set of discoverable tests.
18
21
 
19
22
  Coming in later versions:
20
- init, refine, testgen, brew, review, dashboard
23
+ refine, testgen, brew, review, dashboard
21
24
 
22
25
  Docs: https://github.com/aminazar/slowcook
23
26
  `;
@@ -25,6 +28,9 @@ async function main() {
25
28
  const args = process.argv.slice(2);
26
29
  const command = args[0];
27
30
  switch (command) {
31
+ case "init":
32
+ await init(args.slice(1), VERSION);
33
+ return;
28
34
  case "guard":
29
35
  await guard(args.slice(1));
30
36
  return;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,MAAM,KAAK,GAAG;;;;;;;;;;wBAUU,OAAO;;;;;;;;CAQ9B,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAExB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,OAAO;QACT,KAAK,UAAU;YACb,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,WAAW,CAAC;QACjB,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;YACnC,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;QACT;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;IACjC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAEhD,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,MAAM,KAAK,GAAG;;;;;;;;;;;wBAWU,OAAO;;;;;;;;;CAS9B,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAExB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACnC,OAAO;QACT,KAAK,OAAO;YACV,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,OAAO;QACT,KAAK,UAAU;YACb,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9B,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,WAAW,CAAC;QACjB,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;YACnC,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO;QACT;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;IACjC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function init(argv: string[], cliVersion: string): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"AAmIA,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2E5E"}
@@ -0,0 +1,181 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, } from "node:fs";
2
+ import { dirname, resolve, relative, isAbsolute } from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import { buildPlan, InitError } from "./plan.js";
5
+ function parseArgs(argv) {
6
+ const args = {
7
+ cwd: process.cwd(),
8
+ owner: undefined,
9
+ force: false,
10
+ dryRun: false,
11
+ };
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const arg = argv[i];
14
+ const next = argv[i + 1];
15
+ if (arg === "--cwd" && next) {
16
+ args.cwd = resolve(next);
17
+ i++;
18
+ }
19
+ else if (arg === "--owner" && next) {
20
+ args.owner = next;
21
+ i++;
22
+ }
23
+ else if (arg === "--force") {
24
+ args.force = true;
25
+ }
26
+ else if (arg === "--dry-run") {
27
+ args.dryRun = true;
28
+ }
29
+ else if (arg === "--help" || arg === "-h") {
30
+ printHelp();
31
+ process.exit(0);
32
+ }
33
+ }
34
+ return args;
35
+ }
36
+ function printHelp() {
37
+ console.log(`
38
+ slowcook init — scaffold slowcook configuration in a consumer project
39
+
40
+ Usage:
41
+ slowcook init [options]
42
+
43
+ Options:
44
+ --cwd <path> Target project directory (default: current directory)
45
+ --owner <handle> CODEOWNERS handle/team, e.g. "@aminazar" or "@acme/frontend".
46
+ If omitted, slowcook tries to detect from \`git remote get-url origin\`;
47
+ falls back to "@TODO-OWNER" with a warning.
48
+ --force Overwrite existing slowcook files (default: skip existing)
49
+ --dry-run Print the plan without writing anything to disk
50
+ --help, -h Show this help
51
+
52
+ Writes:
53
+ .brewing/frozen-paths.json
54
+ .brewing/stack.json
55
+ .brewing/README.md
56
+ .brewing/manifests/.gitkeep
57
+ .github/workflows/slowcook.yml
58
+ CODEOWNERS (appends slowcook section if file exists)
59
+
60
+ Exit codes:
61
+ 0 success (or dry-run completed)
62
+ 2 script error (no package.json, vitest not detected, etc.)
63
+ `);
64
+ }
65
+ function makeReader(cwd) {
66
+ return {
67
+ exists: (p) => existsSync(resolveIn(cwd, p)),
68
+ read: (p) => readFileSync(resolveIn(cwd, p), "utf8"),
69
+ };
70
+ }
71
+ function resolveIn(cwd, path) {
72
+ if (isAbsolute(path))
73
+ return path;
74
+ return resolve(cwd, path);
75
+ }
76
+ function detectOwnerFromGitRemote(cwd) {
77
+ try {
78
+ const url = execSync("git remote get-url origin", {
79
+ cwd,
80
+ encoding: "utf8",
81
+ stdio: ["ignore", "pipe", "ignore"],
82
+ }).trim();
83
+ // https://github.com/USER/REPO.git | git@github.com:USER/REPO.git
84
+ const m = url.match(/github\.com[:/]([^/]+)\/[^/.]+(?:\.git)?$/) ??
85
+ url.match(/gitlab\.com[:/]([^/]+)\/[^/.]+(?:\.git)?$/);
86
+ if (m && m[1])
87
+ return `@${m[1]}`;
88
+ }
89
+ catch {
90
+ // not a git repo, or no origin — fine, fall through
91
+ }
92
+ return null;
93
+ }
94
+ function formatAction(a) {
95
+ switch (a.kind) {
96
+ case "create":
97
+ return ` CREATE ${a.path}`;
98
+ case "overwrite":
99
+ return ` OVERWRITE ${a.path}`;
100
+ case "append":
101
+ return ` APPEND ${a.path} (preserving existing content)`;
102
+ case "skip-exists":
103
+ return ` SKIP ${a.path} (${a.reason})`;
104
+ case "conflict":
105
+ return ` CONFLICT ${a.path} (${a.reason})`;
106
+ }
107
+ }
108
+ function applyAction(cwd, a) {
109
+ if (a.kind === "skip-exists" || a.kind === "conflict")
110
+ return;
111
+ const full = resolveIn(cwd, a.path);
112
+ mkdirSync(dirname(full), { recursive: true });
113
+ const contents = a.kind === "create" || a.kind === "overwrite" || a.kind === "append"
114
+ ? a.contents
115
+ : "";
116
+ writeFileSync(full, contents, "utf8");
117
+ }
118
+ export async function init(argv, cliVersion) {
119
+ const args = parseArgs(argv);
120
+ const reader = makeReader(args.cwd);
121
+ let owner = args.owner;
122
+ if (!owner) {
123
+ const detected = detectOwnerFromGitRemote(args.cwd);
124
+ if (detected) {
125
+ owner = detected;
126
+ console.log(`Detected CODEOWNERS handle from git remote: ${owner}`);
127
+ }
128
+ else {
129
+ owner = "@TODO-OWNER";
130
+ console.log("::warning::Could not detect a CODEOWNERS handle from git remote. Using @TODO-OWNER as a placeholder — replace it after init.");
131
+ }
132
+ }
133
+ let plan;
134
+ try {
135
+ plan = buildPlan(reader, {
136
+ cwd: args.cwd,
137
+ owner,
138
+ force: args.force,
139
+ cliVersion,
140
+ });
141
+ }
142
+ catch (e) {
143
+ if (e instanceof InitError) {
144
+ console.error(`slowcook init: ${e.message}`);
145
+ process.exit(2);
146
+ }
147
+ throw e;
148
+ }
149
+ console.log(`Detected stack:`);
150
+ console.log(` language: ${plan.detected.language}`);
151
+ console.log(` vitest: ${plan.detected.hasVitest}`);
152
+ console.log(` playwright: ${plan.detected.hasPlaywright}`);
153
+ console.log();
154
+ if (plan.warnings.length > 0) {
155
+ console.log("Warnings:");
156
+ for (const w of plan.warnings)
157
+ console.log(` - ${w}`);
158
+ console.log();
159
+ }
160
+ console.log(`Planned file actions (cwd: ${relative(process.cwd(), args.cwd) || "."}):`);
161
+ for (const a of plan.actions)
162
+ console.log(formatAction(a));
163
+ console.log();
164
+ if (args.dryRun) {
165
+ console.log("Dry-run complete — no files written.");
166
+ return;
167
+ }
168
+ for (const a of plan.actions)
169
+ applyAction(args.cwd, a);
170
+ const skippedCount = plan.actions.filter((a) => a.kind === "skip-exists").length;
171
+ console.log(`slowcook init: ${plan.actions.length - skippedCount} file(s) written, ${skippedCount} skipped.`);
172
+ console.log();
173
+ console.log("Next steps:");
174
+ console.log(` 1. Review .brewing/frozen-paths.json — add/remove directories to match your repo.`);
175
+ if (owner === "@TODO-OWNER") {
176
+ console.log(` 2. Replace @TODO-OWNER in CODEOWNERS with your GitHub handle/team.`);
177
+ }
178
+ console.log(` ${owner === "@TODO-OWNER" ? "3." : "2."} Run \`slowcook manifest record\` once your test set is stable.`);
179
+ console.log(` ${owner === "@TODO-OWNER" ? "4." : "3."} Commit and open a PR; slowcook CI will run on it.`);
180
+ }
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export interface PackageJson {
2
+ dependencies?: Record<string, string>;
3
+ devDependencies?: Record<string, string>;
4
+ scripts?: Record<string, string>;
5
+ }
6
+ export interface DetectedStack {
7
+ language: "typescript";
8
+ hasVitest: boolean;
9
+ hasPlaywright: boolean;
10
+ }
11
+ /** Minimal filesystem reader so planning is pure. */
12
+ export interface FileReader {
13
+ exists(path: string): boolean;
14
+ read(path: string): string;
15
+ }
16
+ export interface PlanOptions {
17
+ /** Consumer project root (where package.json lives). */
18
+ cwd: string;
19
+ /** CODEOWNERS handle/team (e.g., "@aminazar"). */
20
+ owner: string;
21
+ /** Overwrite existing files (default: skip-if-exists). */
22
+ force: boolean;
23
+ /** Version of the CLI invoking init; drives the CI workflow pin. */
24
+ cliVersion?: string;
25
+ }
26
+ export type FileAction = {
27
+ kind: "create";
28
+ path: string;
29
+ contents: string;
30
+ } | {
31
+ kind: "skip-exists";
32
+ path: string;
33
+ reason: string;
34
+ } | {
35
+ kind: "overwrite";
36
+ path: string;
37
+ contents: string;
38
+ } | {
39
+ kind: "append";
40
+ path: string;
41
+ contents: string;
42
+ existingContents: string;
43
+ } | {
44
+ kind: "conflict";
45
+ path: string;
46
+ reason: string;
47
+ };
48
+ export interface Plan {
49
+ detected: DetectedStack;
50
+ actions: FileAction[];
51
+ warnings: string[];
52
+ }
53
+ export declare function detectStack(pkg: PackageJson): DetectedStack;
54
+ export declare class InitError extends Error {
55
+ constructor(message: string);
56
+ }
57
+ export declare function buildPlan(reader: FileReader, options: PlanOptions): Plan;
58
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/commands/init/plan.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,KAAK,EAAE,OAAO,CAAC;IACf,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,WAAW,IAAI;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAYD,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAO3D;AAED,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAiExE"}
@@ -0,0 +1,136 @@
1
+ // Pure planning logic for `slowcook init`. Takes a snapshot of the filesystem
2
+ // (via an injected reader), returns the planned actions. No I/O side effects
3
+ // here — all writes happen in the CLI wrapper so this is trivially testable.
4
+ import { frozenPathsJson, stackJson, brewingReadme, slowcookWorkflow, codeownersFullFile, codeownersSection, gitkeep, CLI_VERSION_FOR_TEMPLATES, SLOWCOOK_CODEOWNERS_MARKER_BEGIN, SLOWCOOK_CODEOWNERS_MARKER_END, } from "./templates.js";
5
+ const TARGETS = {
6
+ frozenPaths: ".brewing/frozen-paths.json",
7
+ stack: ".brewing/stack.json",
8
+ brewingReadme: ".brewing/README.md",
9
+ manifestsGitkeep: ".brewing/manifests/.gitkeep",
10
+ workflow: ".github/workflows/slowcook.yml",
11
+ codeowners: "CODEOWNERS",
12
+ packageJson: "package.json",
13
+ };
14
+ export function detectStack(pkg) {
15
+ const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
16
+ return {
17
+ language: "typescript",
18
+ hasVitest: "vitest" in deps,
19
+ hasPlaywright: "@playwright/test" in deps || "playwright" in deps,
20
+ };
21
+ }
22
+ export class InitError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "InitError";
26
+ }
27
+ }
28
+ export function buildPlan(reader, options) {
29
+ if (!reader.exists(TARGETS.packageJson)) {
30
+ throw new InitError(`No package.json found at ${options.cwd}. \`slowcook init\` expects to run in a TS/JS project root.`);
31
+ }
32
+ let pkg;
33
+ try {
34
+ pkg = JSON.parse(reader.read(TARGETS.packageJson));
35
+ }
36
+ catch (e) {
37
+ throw new InitError(`package.json is not valid JSON: ${e.message}`);
38
+ }
39
+ const detected = detectStack(pkg);
40
+ const warnings = [];
41
+ const actions = [];
42
+ const cliVersion = options.cliVersion ?? CLI_VERSION_FOR_TEMPLATES;
43
+ if (!detected.hasVitest) {
44
+ throw new InitError(`No test runner detected. slowcook 0.3 requires Vitest (found in devDependencies). ` +
45
+ `Install it with \`npm i -D vitest\` and re-run.`);
46
+ }
47
+ if (detected.hasPlaywright) {
48
+ warnings.push("Playwright detected. slowcook 0.3 doesn't yet implement Playwright discovery, " +
49
+ "so the e2e suite is intentionally omitted from stack.json. Add it back when " +
50
+ "playwright discovery ships in a later slowcook release.");
51
+ }
52
+ const tmplParams = {
53
+ owner: options.owner,
54
+ hasPlaywright: detected.hasPlaywright,
55
+ };
56
+ // 1. .brewing/frozen-paths.json
57
+ addSimpleFile(actions, reader, options.force, TARGETS.frozenPaths, frozenPathsJson());
58
+ // 2. .brewing/stack.json
59
+ addSimpleFile(actions, reader, options.force, TARGETS.stack, stackJson(tmplParams));
60
+ // 3. .brewing/README.md
61
+ addSimpleFile(actions, reader, options.force, TARGETS.brewingReadme, brewingReadme());
62
+ // 4. .brewing/manifests/.gitkeep (only if we're creating the dir)
63
+ if (!reader.exists(".brewing/manifests/")) {
64
+ actions.push({ kind: "create", path: TARGETS.manifestsGitkeep, contents: gitkeep() });
65
+ }
66
+ // 5. .github/workflows/slowcook.yml
67
+ addSimpleFile(actions, reader, options.force, TARGETS.workflow, slowcookWorkflow(cliVersion));
68
+ // 6. CODEOWNERS — special case (append if exists without our markers)
69
+ actions.push(planCodeowners(reader, options, tmplParams));
70
+ return { detected, actions, warnings };
71
+ }
72
+ function addSimpleFile(actions, reader, force, path, contents) {
73
+ if (!reader.exists(path)) {
74
+ actions.push({ kind: "create", path, contents });
75
+ return;
76
+ }
77
+ const existing = reader.read(path);
78
+ if (existing === contents) {
79
+ actions.push({ kind: "skip-exists", path, reason: "file already matches template" });
80
+ return;
81
+ }
82
+ if (force) {
83
+ actions.push({ kind: "overwrite", path, contents });
84
+ return;
85
+ }
86
+ actions.push({
87
+ kind: "skip-exists",
88
+ path,
89
+ reason: "file exists and differs from template (use --force to overwrite)",
90
+ });
91
+ }
92
+ function planCodeowners(reader, options, params) {
93
+ const path = TARGETS.codeowners;
94
+ if (!reader.exists(path)) {
95
+ return { kind: "create", path, contents: codeownersFullFile(params) };
96
+ }
97
+ const existing = reader.read(path);
98
+ if (existing.includes(SLOWCOOK_CODEOWNERS_MARKER_BEGIN) &&
99
+ existing.includes(SLOWCOOK_CODEOWNERS_MARKER_END)) {
100
+ if (options.force) {
101
+ // Replace the slowcook section inline
102
+ const replaced = replaceCodeownersSection(existing, params);
103
+ return { kind: "overwrite", path, contents: replaced };
104
+ }
105
+ return {
106
+ kind: "skip-exists",
107
+ path,
108
+ reason: "slowcook section already present (use --force to regenerate)",
109
+ };
110
+ }
111
+ // Existing CODEOWNERS without our markers → append
112
+ const toAppend = (existing.endsWith("\n") ? existing : existing + "\n") +
113
+ "\n" +
114
+ codeownersSection(params);
115
+ return {
116
+ kind: "append",
117
+ path,
118
+ contents: toAppend,
119
+ existingContents: existing,
120
+ };
121
+ }
122
+ function replaceCodeownersSection(existing, params) {
123
+ const begin = existing.indexOf(SLOWCOOK_CODEOWNERS_MARKER_BEGIN);
124
+ const endMarkerStart = existing.indexOf(SLOWCOOK_CODEOWNERS_MARKER_END);
125
+ if (begin === -1 || endMarkerStart === -1 || endMarkerStart < begin) {
126
+ // Shouldn't happen if caller verified markers exist, but be defensive
127
+ return existing + "\n" + codeownersSection(params);
128
+ }
129
+ const endMarkerEnd = endMarkerStart + SLOWCOOK_CODEOWNERS_MARKER_END.length;
130
+ // Include the trailing newline if present
131
+ const after = existing[endMarkerEnd] === "\n"
132
+ ? existing.slice(endMarkerEnd + 1)
133
+ : existing.slice(endMarkerEnd);
134
+ return existing.slice(0, begin) + codeownersSection(params) + after;
135
+ }
136
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/commands/init/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export declare const CLI_VERSION_FOR_TEMPLATES = "0.3.0";
2
+ export interface TemplateParams {
3
+ /** CODEOWNERS handle or team (e.g. "@aminazar" or "@acme/frontend"). */
4
+ owner: string;
5
+ /** Whether the project has Playwright installed (affects stack.json comments). */
6
+ hasPlaywright: boolean;
7
+ }
8
+ export declare const SLOWCOOK_CODEOWNERS_MARKER_BEGIN = "# --- slowcook:frozen-paths BEGIN ---";
9
+ export declare const SLOWCOOK_CODEOWNERS_MARKER_END = "# --- slowcook:frozen-paths END ---";
10
+ export declare function frozenPathsJson(): string;
11
+ export declare function stackJson(params: TemplateParams): string;
12
+ export declare function brewingReadme(): string;
13
+ export declare function slowcookWorkflow(cliVersion: string): string;
14
+ export declare function codeownersSection(params: TemplateParams): string;
15
+ export declare function codeownersFullFile(params: TemplateParams): string;
16
+ export declare function gitkeep(): string;
17
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/commands/init/templates.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,yBAAyB,UAAU,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,gCAAgC,0CAA0C,CAAC;AACxF,eAAO,MAAM,8BAA8B,wCAAwC,CAAC;AAEpF,wBAAgB,eAAe,IAAI,MAAM,CAiCxC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAkCxD;AAED,wBAAgB,aAAa,IAAI,MAAM,CA+BtC;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA2C3D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAchE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAQjE;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC"}
@@ -0,0 +1,163 @@
1
+ // Static and parameterized file contents written by `slowcook init`.
2
+ // Version is bumped in lockstep with the CLI package.
3
+ export const CLI_VERSION_FOR_TEMPLATES = "0.3.0";
4
+ export const SLOWCOOK_CODEOWNERS_MARKER_BEGIN = "# --- slowcook:frozen-paths BEGIN ---";
5
+ export const SLOWCOOK_CODEOWNERS_MARKER_END = "# --- slowcook:frozen-paths END ---";
6
+ export function frozenPathsJson() {
7
+ return (JSON.stringify({
8
+ $schema: "./frozen-paths.schema.json",
9
+ $doc: "Paths frozen by slowcook. See https://github.com/aminazar/slowcook for the design. " +
10
+ "To modify any of these: either get CODEOWNERS approval, or add the 'override-freeze' label " +
11
+ "to the PR (guard runs in advisory mode, audit trail preserved).",
12
+ directories: [
13
+ "tests/",
14
+ "tests-fixtures/",
15
+ "tests-helpers/",
16
+ ".brewing/manifests/",
17
+ ],
18
+ files: [
19
+ "vitest.config.ts",
20
+ "vitest.config.mjs",
21
+ "vitest.config.js",
22
+ ".brewing/frozen-paths.json",
23
+ ".brewing/stack.json",
24
+ ".github/workflows/slowcook.yml",
25
+ ],
26
+ partial: {
27
+ "package.json": {
28
+ frozen_key_paths: ["scripts.test", "scripts.test:watch"],
29
+ },
30
+ },
31
+ }, null, 2) + "\n");
32
+ }
33
+ export function stackJson(params) {
34
+ const doc = "Project-level stack configuration consumed by slowcook (@slowcook-ai/stack-ts). " +
35
+ "Tells the harness how to discover and run tests. Only include suites that are " +
36
+ "actually runnable — slowcook refuses to record an incomplete manifest." +
37
+ (params.hasPlaywright
38
+ ? " (Playwright detected in package.json; slowcook's playwright discovery is not yet " +
39
+ "implemented, so the e2e suite is intentionally omitted. Add it back post-upgrade.)"
40
+ : "");
41
+ return (JSON.stringify({
42
+ $schema: "./stack.schema.json",
43
+ $doc: doc,
44
+ language: "typescript",
45
+ package_manager: "npm",
46
+ test: {
47
+ backend: {
48
+ runner: "vitest",
49
+ run_command: "npx vitest run",
50
+ discover_command: "npx vitest list",
51
+ reporter_format: "vitest-list-lines",
52
+ },
53
+ },
54
+ lint: {
55
+ lint_command: "npm run lint",
56
+ typecheck_command: "npm run typecheck",
57
+ },
58
+ }, null, 2) + "\n");
59
+ }
60
+ export function brewingReadme() {
61
+ return `# \`.brewing/\`
62
+
63
+ Consumer-side configuration for [slowcook](https://github.com/aminazar/slowcook), a TDD-first agentic development harness.
64
+
65
+ ## Contents
66
+
67
+ | Path | Purpose |
68
+ |---|---|
69
+ | \`frozen-paths.json\` | What's immutable during brewing (tests, configs, manifests) |
70
+ | \`stack.json\` | How slowcook invokes tests / coverage / lint for this project |
71
+ | \`manifests/\` | Per-story test manifests; populated by \`slowcook manifest record\` |
72
+
73
+ ## Running slowcook locally
74
+
75
+ \`\`\`bash
76
+ npx --yes @slowcook-ai/cli@latest guard --base origin/main --head HEAD
77
+ npx --yes @slowcook-ai/cli@latest manifest record
78
+ npx --yes @slowcook-ai/cli@latest manifest verify
79
+ \`\`\`
80
+
81
+ ## When you legitimately need to modify a frozen path
82
+
83
+ 1. Open a PR with the change.
84
+ 2. Add the \`override-freeze\` label to the PR.
85
+ 3. Guard runs in advisory mode (surfaces violations but doesn't fail).
86
+ 4. CODEOWNERS still requires explicit approval.
87
+ 5. Merge audit trail: PR number + \`override-freeze\` label + approval.
88
+
89
+ Deliberately slightly inconvenient. Frozen-path changes are rare events that deserve a reviewer's eyes.
90
+ `;
91
+ }
92
+ export function slowcookWorkflow(cliVersion) {
93
+ return `name: slowcook
94
+
95
+ on:
96
+ pull_request:
97
+ types: [opened, synchronize, reopened, labeled, unlabeled]
98
+
99
+ concurrency:
100
+ group: slowcook-\${{ github.event.pull_request.number }}
101
+ cancel-in-progress: true
102
+
103
+ # Pin CLI version for reproducibility; bump deliberately via a PR.
104
+ env:
105
+ SLOWCOOK_CLI: "@slowcook-ai/cli@${cliVersion}"
106
+
107
+ jobs:
108
+ check:
109
+ name: slowcook checks
110
+ runs-on: ubuntu-latest
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+ with:
114
+ fetch-depth: 0
115
+
116
+ - uses: actions/setup-node@v4
117
+ with:
118
+ node-version: 20
119
+
120
+ - name: Guard — frozen paths
121
+ env:
122
+ HAS_OVERRIDE: \${{ contains(github.event.pull_request.labels.*.name, 'override-freeze') }}
123
+ run: |
124
+ set -eu
125
+ ARGS="--base origin/\${{ github.base_ref }} --head HEAD"
126
+ if [ "$HAS_OVERRIDE" = "true" ]; then
127
+ ARGS="$ARGS --override"
128
+ echo "::notice::'override-freeze' label present — guard runs in advisory mode."
129
+ fi
130
+ npx --yes "$SLOWCOOK_CLI" guard $ARGS
131
+
132
+ - name: Manifest — verify discoverable tests
133
+ run: npx --yes "$SLOWCOOK_CLI" manifest verify
134
+ `;
135
+ }
136
+ export function codeownersSection(params) {
137
+ return `${SLOWCOOK_CODEOWNERS_MARKER_BEGIN}
138
+ # Paths frozen by slowcook. Agent-authored PRs cannot modify them;
139
+ # human edits must be reviewed. See https://github.com/aminazar/slowcook.
140
+
141
+ /tests/ ${params.owner}
142
+ /tests-fixtures/ ${params.owner}
143
+ /tests-helpers/ ${params.owner}
144
+ /vitest.config.* ${params.owner}
145
+ /.brewing/ ${params.owner}
146
+ /.github/workflows/slowcook.yml ${params.owner}
147
+ /CODEOWNERS ${params.owner}
148
+ ${SLOWCOOK_CODEOWNERS_MARKER_END}
149
+ `;
150
+ }
151
+ export function codeownersFullFile(params) {
152
+ // For repos that don't have CODEOWNERS yet — prepend a short header.
153
+ return `# CODEOWNERS
154
+ #
155
+ # Generated by \`slowcook init\`. The slowcook-managed section is between
156
+ # the marker comments; edit outside those markers freely.
157
+
158
+ ${codeownersSection(params)}`;
159
+ }
160
+ export function gitkeep() {
161
+ return "";
162
+ }
163
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/commands/init/templates.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sDAAsD;AAEtD,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;AASjD,MAAM,CAAC,MAAM,gCAAgC,GAAG,uCAAuC,CAAC;AACxF,MAAM,CAAC,MAAM,8BAA8B,GAAG,qCAAqC,CAAC;AAEpF,MAAM,UAAU,eAAe;IAC7B,OAAO,CACL,IAAI,CAAC,SAAS,CACZ;QACE,OAAO,EAAE,4BAA4B;QACrC,IAAI,EACF,qFAAqF;YACrF,6FAA6F;YAC7F,iEAAiE;QACnE,WAAW,EAAE;YACX,QAAQ;YACR,iBAAiB;YACjB,gBAAgB;YAChB,qBAAqB;SACtB;QACD,KAAK,EAAE;YACL,kBAAkB;YAClB,mBAAmB;YACnB,kBAAkB;YAClB,4BAA4B;YAC5B,qBAAqB;YACrB,gCAAgC;SACjC;QACD,OAAO,EAAE;YACP,cAAc,EAAE;gBACd,gBAAgB,EAAE,CAAC,cAAc,EAAE,oBAAoB,CAAC;aACzD;SACF;KACF,EACD,IAAI,EACJ,CAAC,CACF,GAAG,IAAI,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAsB;IAC9C,MAAM,GAAG,GACP,kFAAkF;QAClF,gFAAgF;QAChF,wEAAwE;QACxE,CAAC,MAAM,CAAC,aAAa;YACnB,CAAC,CAAC,oFAAoF;gBACpF,oFAAoF;YACtF,CAAC,CAAC,EAAE,CAAC,CAAC;IAEV,OAAO,CACL,IAAI,CAAC,SAAS,CACZ;QACE,OAAO,EAAE,qBAAqB;QAC9B,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,YAAY;QACtB,eAAe,EAAE,KAAK;QACtB,IAAI,EAAE;YACJ,OAAO,EAAE;gBACP,MAAM,EAAE,QAAQ;gBAChB,WAAW,EAAE,gBAAgB;gBAC7B,gBAAgB,EAAE,iBAAiB;gBACnC,eAAe,EAAE,mBAAmB;aACrC;SACF;QACD,IAAI,EAAE;YACJ,YAAY,EAAE,cAAc;YAC5B,iBAAiB,EAAE,mBAAmB;SACvC;KACF,EACD,IAAI,EACJ,CAAC,CACF,GAAG,IAAI,CACT,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BR,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,OAAO;;;;;;;;;;;;oCAY2B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B7C,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,OAAO,GAAG,gCAAgC;;;;kCAIV,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;kCACZ,MAAM,CAAC,KAAK;EAC5C,8BAA8B;CAC/B,CAAC;AACF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,qEAAqE;IACrE,OAAO;;;;;EAKP,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,EAAE,CAAC;AACZ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slowcook-ai/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for the slowcook brewing harness",
5
5
  "license": "MIT",
6
6
  "author": "aminazar",