@slowcook-ai/cli 0.19.0-alpha.0 → 0.19.0-alpha.11
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 +27 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/brew/agent.d.ts +129 -0
- package/dist/commands/brew/agent.d.ts.map +1 -1
- package/dist/commands/brew/agent.js +132 -0
- package/dist/commands/brew/agent.js.map +1 -1
- package/dist/commands/brew/index.d.ts.map +1 -1
- package/dist/commands/brew/index.js +72 -0
- package/dist/commands/brew/index.js.map +1 -1
- package/dist/commands/brew/pair-navigator.d.ts +112 -0
- package/dist/commands/brew/pair-navigator.d.ts.map +1 -0
- package/dist/commands/brew/pair-navigator.js +183 -0
- package/dist/commands/brew/pair-navigator.js.map +1 -0
- package/dist/commands/chef/drift-fix.d.ts +13 -2
- package/dist/commands/chef/drift-fix.d.ts.map +1 -1
- package/dist/commands/chef/drift-fix.js +58 -22
- package/dist/commands/chef/drift-fix.js.map +1 -1
- package/dist/commands/chef/orchestrate.d.ts +34 -0
- package/dist/commands/chef/orchestrate.d.ts.map +1 -0
- package/dist/commands/chef/orchestrate.js +385 -0
- package/dist/commands/chef/orchestrate.js.map +1 -0
- package/dist/commands/init/mock.d.ts +46 -0
- package/dist/commands/init/mock.d.ts.map +1 -1
- package/dist/commands/init/mock.js +142 -2
- package/dist/commands/init/mock.js.map +1 -1
- package/dist/commands/recon/index.d.ts.map +1 -1
- package/dist/commands/recon/index.js +288 -5
- package/dist/commands/recon/index.js.map +1 -1
- package/dist/commands/recon/reuse.d.ts +150 -0
- package/dist/commands/recon/reuse.d.ts.map +1 -0
- package/dist/commands/recon/reuse.js +335 -0
- package/dist/commands/recon/reuse.js.map +1 -0
- package/dist/commands/recon/shape-preserve.d.ts +46 -0
- package/dist/commands/recon/shape-preserve.d.ts.map +1 -1
- package/dist/commands/recon/shape-preserve.js +126 -0
- package/dist/commands/recon/shape-preserve.js.map +1 -1
- package/dist/commands/recon/stale-stubs.d.ts +62 -0
- package/dist/commands/recon/stale-stubs.d.ts.map +1 -0
- package/dist/commands/recon/stale-stubs.js +79 -0
- package/dist/commands/recon/stale-stubs.js.map +1 -0
- package/dist/commands/refactor/index.d.ts +15 -0
- package/dist/commands/refactor/index.d.ts.map +1 -0
- package/dist/commands/refactor/index.js +126 -0
- package/dist/commands/refactor/index.js.map +1 -0
- package/dist/commands/refactor/score.d.ts +38 -0
- package/dist/commands/refactor/score.d.ts.map +1 -0
- package/dist/commands/refactor/score.js +79 -0
- package/dist/commands/refactor/score.js.map +1 -0
- package/dist/commands/refactor/types.d.ts +64 -0
- package/dist/commands/refactor/types.d.ts.map +1 -0
- package/dist/commands/refactor/types.js +26 -0
- package/dist/commands/refactor/types.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-α.11 (#84) — stale-stub detector.
|
|
3
|
+
*
|
|
4
|
+
* Empirical finding from rewo reuse-scan 2026-05-07: 6 @slowcook-stub
|
|
5
|
+
* files survived from incomplete story-016/018 brews. They're not
|
|
6
|
+
* refactor candidates (reuse-scan misclassified them at 100% similar
|
|
7
|
+
* because they're literally the same template body); they're
|
|
8
|
+
* INCOMPLETE WORK markers that should escalate to PM after a grace
|
|
9
|
+
* period.
|
|
10
|
+
*
|
|
11
|
+
* This module: walk src/, find @slowcook-stub markers, age each via
|
|
12
|
+
* `git log --diff-filter=A` (first-add date), report stale ones
|
|
13
|
+
* + (optionally) escalate to PM via gh comment on the source issue.
|
|
14
|
+
*
|
|
15
|
+
* Pure helpers below; cli wiring in recon's --stub-scan flag.
|
|
16
|
+
*/
|
|
17
|
+
export interface StubFile {
|
|
18
|
+
/** Repo-relative path. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** Story id extracted from the stub marker (e.g., "016", "018"). */
|
|
21
|
+
storyId: string | null;
|
|
22
|
+
/** ISO timestamp of the file's first-add commit. Null if git log
|
|
23
|
+
* couldn't resolve (file isn't tracked, repo isn't a git repo,
|
|
24
|
+
* etc.). */
|
|
25
|
+
firstAddedAt: string | null;
|
|
26
|
+
/** Age in days, computed against `now`. Null when firstAddedAt
|
|
27
|
+
* is null. */
|
|
28
|
+
ageDays: number | null;
|
|
29
|
+
/** Classification: fresh < gracePeriod, stale ≥ gracePeriod. */
|
|
30
|
+
classification: "fresh" | "stale" | "unknown";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detect a `@slowcook-stub` marker in file content + extract the
|
|
34
|
+
* story id when one is named alongside it. Pure: no IO.
|
|
35
|
+
*
|
|
36
|
+
* Patterns recognized:
|
|
37
|
+
* `// @slowcook-stub story-016` — explicit story id
|
|
38
|
+
* `// @slowcook-stub` — bare; storyId = null
|
|
39
|
+
* `/* @slowcook-stub story-018 *\/` — block comment form
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectStubMarker(content: string): {
|
|
42
|
+
isStub: boolean;
|
|
43
|
+
storyId: string | null;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Compute days between two ISO timestamps. Pure: no clock IO. Returns
|
|
47
|
+
* a non-negative number rounded to 1 decimal.
|
|
48
|
+
*/
|
|
49
|
+
export declare function daysBetween(earlierIso: string, laterIso: string): number;
|
|
50
|
+
/**
|
|
51
|
+
* Classify a stub by age against a grace period. Pure.
|
|
52
|
+
* ageDays === null → "unknown"
|
|
53
|
+
* ageDays < graceDays → "fresh"
|
|
54
|
+
* ageDays >= graceDays → "stale"
|
|
55
|
+
*/
|
|
56
|
+
export declare function classifyStubAge(ageDays: number | null, graceDays: number): "fresh" | "stale" | "unknown";
|
|
57
|
+
/**
|
|
58
|
+
* Synthesize a PM-actionable comment body for an overdue stub.
|
|
59
|
+
* Pure: no IO. Caller posts via gh.
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildStaleStubComment(stub: StubFile, graceDays: number): string;
|
|
62
|
+
//# sourceMappingURL=stale-stubs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-stubs.d.ts","sourceRoot":"","sources":["../../../src/commands/recon/stale-stubs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;iBAEa;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;mBACe;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gEAAgE;IAChE,cAAc,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;CAC/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAK7F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMxE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,GAAG,OAAO,GAAG,SAAS,CAI/B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAgB/E"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-α.11 (#84) — stale-stub detector.
|
|
3
|
+
*
|
|
4
|
+
* Empirical finding from rewo reuse-scan 2026-05-07: 6 @slowcook-stub
|
|
5
|
+
* files survived from incomplete story-016/018 brews. They're not
|
|
6
|
+
* refactor candidates (reuse-scan misclassified them at 100% similar
|
|
7
|
+
* because they're literally the same template body); they're
|
|
8
|
+
* INCOMPLETE WORK markers that should escalate to PM after a grace
|
|
9
|
+
* period.
|
|
10
|
+
*
|
|
11
|
+
* This module: walk src/, find @slowcook-stub markers, age each via
|
|
12
|
+
* `git log --diff-filter=A` (first-add date), report stale ones
|
|
13
|
+
* + (optionally) escalate to PM via gh comment on the source issue.
|
|
14
|
+
*
|
|
15
|
+
* Pure helpers below; cli wiring in recon's --stub-scan flag.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Detect a `@slowcook-stub` marker in file content + extract the
|
|
19
|
+
* story id when one is named alongside it. Pure: no IO.
|
|
20
|
+
*
|
|
21
|
+
* Patterns recognized:
|
|
22
|
+
* `// @slowcook-stub story-016` — explicit story id
|
|
23
|
+
* `// @slowcook-stub` — bare; storyId = null
|
|
24
|
+
* `/* @slowcook-stub story-018 *\/` — block comment form
|
|
25
|
+
*/
|
|
26
|
+
export function detectStubMarker(content) {
|
|
27
|
+
const head = content.slice(0, 500);
|
|
28
|
+
if (!head.includes("@slowcook-stub"))
|
|
29
|
+
return { isStub: false, storyId: null };
|
|
30
|
+
const m = head.match(/@slowcook-stub\s+story-(\d+)/);
|
|
31
|
+
return { isStub: true, storyId: m ? m[1] : null };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Compute days between two ISO timestamps. Pure: no clock IO. Returns
|
|
35
|
+
* a non-negative number rounded to 1 decimal.
|
|
36
|
+
*/
|
|
37
|
+
export function daysBetween(earlierIso, laterIso) {
|
|
38
|
+
const a = new Date(earlierIso).getTime();
|
|
39
|
+
const b = new Date(laterIso).getTime();
|
|
40
|
+
if (Number.isNaN(a) || Number.isNaN(b))
|
|
41
|
+
return 0;
|
|
42
|
+
const ms = Math.max(0, b - a);
|
|
43
|
+
return Math.round((ms / (1000 * 60 * 60 * 24)) * 10) / 10;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Classify a stub by age against a grace period. Pure.
|
|
47
|
+
* ageDays === null → "unknown"
|
|
48
|
+
* ageDays < graceDays → "fresh"
|
|
49
|
+
* ageDays >= graceDays → "stale"
|
|
50
|
+
*/
|
|
51
|
+
export function classifyStubAge(ageDays, graceDays) {
|
|
52
|
+
if (ageDays === null)
|
|
53
|
+
return "unknown";
|
|
54
|
+
if (ageDays < graceDays)
|
|
55
|
+
return "fresh";
|
|
56
|
+
return "stale";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Synthesize a PM-actionable comment body for an overdue stub.
|
|
60
|
+
* Pure: no IO. Caller posts via gh.
|
|
61
|
+
*/
|
|
62
|
+
export function buildStaleStubComment(stub, graceDays) {
|
|
63
|
+
const lines = [];
|
|
64
|
+
lines.push(`### slowcook · stub still incomplete after ${graceDays} days`);
|
|
65
|
+
lines.push("");
|
|
66
|
+
lines.push(`A \`@slowcook-stub\` marker still exists at \`${stub.path}\`${stub.firstAddedAt ? ` (added ${stub.ageDays} day(s) ago — ${stub.firstAddedAt.slice(0, 10)})` : ""}.`);
|
|
67
|
+
lines.push("");
|
|
68
|
+
lines.push("This means a brew never converged on this file — it's a placeholder that throws at runtime / returns a 501 stub response. The story it belongs to is incomplete.");
|
|
69
|
+
lines.push("");
|
|
70
|
+
lines.push("**PM action**: choose one:");
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push("1. **Re-dispatch brew** for the story. The stub will be replaced.");
|
|
73
|
+
lines.push("2. **Hand-write the implementation** if brew can't converge after multiple attempts.");
|
|
74
|
+
lines.push("3. **Withdraw the story** if the feature is no longer wanted — close the issue + delete the stub file.");
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push(`<sub>Posted by \`slowcook recon --stub-scan\`. Re-running this scan will re-post if the stub is still here in another ${graceDays} days.</sub>`);
|
|
77
|
+
return lines.join("\n");
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=stale-stubs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-stubs.js","sourceRoot":"","sources":["../../../src/commands/recon/stale-stubs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC9E,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACrD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,QAAgB;IAC9D,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACzC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IACvC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAsB,EACtB,SAAiB;IAEjB,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACvC,IAAI,OAAO,GAAG,SAAS;QAAE,OAAO,OAAO,CAAC;IACxC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAc,EAAE,SAAiB;IACrE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,8CAA8C,SAAS,OAAO,CAAC,CAAC;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,iDAAiD,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,OAAO,iBAAiB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjL,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,kKAAkK,CAAC,CAAC;IAC/K,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IACzC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAChF,KAAK,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IACnG,KAAK,CAAC,IAAI,CAAC,wGAAwG,CAAC,CAAC;IACrH,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,yHAAyH,SAAS,cAAc,CAAC,CAAC;IAC7J,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook refactor` — α.7 (#64).
|
|
3
|
+
*
|
|
4
|
+
* Reads candidate refactor proposals from
|
|
5
|
+
* .brewing/refactor/proposals.json
|
|
6
|
+
* Filters by --scope patterns, ranks by benefit/cost, prints a table.
|
|
7
|
+
*
|
|
8
|
+
* α.7 ships ranking + reporting only. LLM-backed proposal generation
|
|
9
|
+
* + auto-application land in later alphas. This command's job today
|
|
10
|
+
* is to give the architecture room: any proposer (hand-authored
|
|
11
|
+
* JSON, recon emissions, future LLM agent) drops files into the
|
|
12
|
+
* standard input path + this command consumes them.
|
|
13
|
+
*/
|
|
14
|
+
export declare function refactor(argv: string[], _cliVersion: string): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/refactor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoEH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CjF"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slowcook refactor` — α.7 (#64).
|
|
3
|
+
*
|
|
4
|
+
* Reads candidate refactor proposals from
|
|
5
|
+
* .brewing/refactor/proposals.json
|
|
6
|
+
* Filters by --scope patterns, ranks by benefit/cost, prints a table.
|
|
7
|
+
*
|
|
8
|
+
* α.7 ships ranking + reporting only. LLM-backed proposal generation
|
|
9
|
+
* + auto-application land in later alphas. This command's job today
|
|
10
|
+
* is to give the architecture room: any proposer (hand-authored
|
|
11
|
+
* JSON, recon emissions, future LLM agent) drops files into the
|
|
12
|
+
* standard input path + this command consumes them.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { filterProposalsByScope, rankProposals } from "./score.js";
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const args = {
|
|
19
|
+
repoRoot: process.cwd(),
|
|
20
|
+
proposalsPath: ".brewing/refactor/proposals.json",
|
|
21
|
+
scope: [],
|
|
22
|
+
json: false,
|
|
23
|
+
limit: 0,
|
|
24
|
+
};
|
|
25
|
+
for (let i = 0; i < argv.length; i++) {
|
|
26
|
+
const a = argv[i];
|
|
27
|
+
const next = argv[i + 1];
|
|
28
|
+
if (a === "--cwd" && next) {
|
|
29
|
+
args.repoRoot = next;
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
else if (a === "--proposals" && next) {
|
|
33
|
+
args.proposalsPath = next;
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
else if (a === "--scope" && next) {
|
|
37
|
+
args.scope.push(next);
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
else if (a === "--json") {
|
|
41
|
+
args.json = true;
|
|
42
|
+
}
|
|
43
|
+
else if (a === "--limit" && next) {
|
|
44
|
+
args.limit = parseInt(next, 10);
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
else if (a === "--help" || a === "-h") {
|
|
48
|
+
printHelp();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return args;
|
|
53
|
+
}
|
|
54
|
+
function printHelp() {
|
|
55
|
+
console.log(`
|
|
56
|
+
slowcook refactor — rank refactor proposals by benefit/cost (cli α.7)
|
|
57
|
+
|
|
58
|
+
Reads .brewing/refactor/proposals.json (an array of RefactorProposal),
|
|
59
|
+
filters by --scope, ranks by benefit-per-cost (descending), prints
|
|
60
|
+
a table or JSON.
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
slowcook refactor [options]
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
--cwd <path> Repo root (default: cwd).
|
|
67
|
+
--proposals <path> Proposals JSON path (default: .brewing/refactor/proposals.json).
|
|
68
|
+
--scope <pattern> Repeatable. Glob-ish patterns; proposal must
|
|
69
|
+
have ALL filesAffected match at least one. Examples:
|
|
70
|
+
--scope src/**
|
|
71
|
+
--scope src/components/*
|
|
72
|
+
--scope src/lib/
|
|
73
|
+
--limit <n> Show top N. Default 0 = all.
|
|
74
|
+
--json Emit JSON (default: human-readable table).
|
|
75
|
+
|
|
76
|
+
Proposal shape (RefactorProposal):
|
|
77
|
+
{ id, title, rationale, filesAffected[], estimatedLocDelta,
|
|
78
|
+
estimatedValueScore, evidence? }
|
|
79
|
+
|
|
80
|
+
Cost = |estimatedLocDelta| × filesAffected.length, clamped ≥1.
|
|
81
|
+
Benefit = estimatedValueScore (caller-supplied 0-10).
|
|
82
|
+
Ranking key = benefit / cost.
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
export async function refactor(argv, _cliVersion) {
|
|
86
|
+
const args = parseArgs(argv);
|
|
87
|
+
const abs = join(args.repoRoot, args.proposalsPath);
|
|
88
|
+
if (!existsSync(abs)) {
|
|
89
|
+
console.error(`No proposals file at ${args.proposalsPath} (cwd=${args.repoRoot}).`);
|
|
90
|
+
console.error(`Drop a JSON array of RefactorProposal there. See \`slowcook refactor --help\`.`);
|
|
91
|
+
process.exit(2);
|
|
92
|
+
}
|
|
93
|
+
let proposals;
|
|
94
|
+
try {
|
|
95
|
+
const raw = readFileSync(abs, "utf8");
|
|
96
|
+
proposals = JSON.parse(raw);
|
|
97
|
+
if (!Array.isArray(proposals))
|
|
98
|
+
throw new Error("proposals JSON must be an array");
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
console.error(`Could not parse ${args.proposalsPath}: ${e.message}`);
|
|
102
|
+
process.exit(2);
|
|
103
|
+
}
|
|
104
|
+
const filtered = filterProposalsByScope(proposals, args.scope);
|
|
105
|
+
const ranked = rankProposals(filtered);
|
|
106
|
+
const out = args.limit > 0 ? ranked.slice(0, args.limit) : ranked;
|
|
107
|
+
if (args.json) {
|
|
108
|
+
console.log(JSON.stringify(out, null, 2));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log(`slowcook refactor · ${proposals.length} proposal(s) · ${filtered.length} after scope filter`);
|
|
112
|
+
if (args.scope.length > 0) {
|
|
113
|
+
console.log(` scope: ${args.scope.join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
console.log();
|
|
116
|
+
if (out.length === 0) {
|
|
117
|
+
console.log("(no proposals to rank)");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
for (const r of out) {
|
|
121
|
+
const a = r.assessment;
|
|
122
|
+
console.log(` ${r.proposal.id} benefit/cost=${a.benefitPerCost.toFixed(3)} benefit=${a.benefitScore} cost=${a.costScore} files=${r.proposal.filesAffected.length}`);
|
|
123
|
+
console.log(` ${r.proposal.title}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/refactor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-alpha.7 (#64) — pure scoring + filtering helpers for the
|
|
3
|
+
* refactor command. No IO; testable in isolation.
|
|
4
|
+
*/
|
|
5
|
+
import type { ProposalAssessment, RefactorProposal } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Compute the cost / benefit / benefit-per-cost numbers for one
|
|
8
|
+
* proposal. Cost is `abs(estimatedLocDelta) * filesAffected.length`
|
|
9
|
+
* (clamped to ≥1 so the ratio is finite for zero-cost edge cases).
|
|
10
|
+
*/
|
|
11
|
+
export declare function assessProposal(p: RefactorProposal): ProposalAssessment;
|
|
12
|
+
/**
|
|
13
|
+
* Match a single file path against a glob-ish scope pattern. Supports:
|
|
14
|
+
* - exact match: "src/foo.ts"
|
|
15
|
+
* - directory prefix: "src/" (matches anything under src/)
|
|
16
|
+
* - star at the end: "src/components/*" (one segment after)
|
|
17
|
+
* - double-star: "src/**" (any depth)
|
|
18
|
+
*
|
|
19
|
+
* Pure regex translation; no fs lookup.
|
|
20
|
+
*/
|
|
21
|
+
export declare function matchesScope(filePath: string, pattern: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Filter proposals to those whose `filesAffected` ALL match at least
|
|
24
|
+
* one scope pattern. A proposal that touches even one out-of-scope
|
|
25
|
+
* file is excluded entirely (boundedness rule — refactors stay
|
|
26
|
+
* surgical).
|
|
27
|
+
*/
|
|
28
|
+
export declare function filterProposalsByScope(proposals: RefactorProposal[], scope: string[]): RefactorProposal[];
|
|
29
|
+
/**
|
|
30
|
+
* Rank a list of proposals by benefit-per-cost (descending). Returns
|
|
31
|
+
* a NEW array of [proposal, assessment] pairs; doesn't mutate input.
|
|
32
|
+
* Stable for ties (preserves original order).
|
|
33
|
+
*/
|
|
34
|
+
export declare function rankProposals(proposals: RefactorProposal[]): Array<{
|
|
35
|
+
proposal: RefactorProposal;
|
|
36
|
+
assessment: ProposalAssessment;
|
|
37
|
+
}>;
|
|
38
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../../src/commands/refactor/score.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,gBAAgB,GAAG,kBAAkB,CAWtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAevE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,KAAK,EAAE,MAAM,EAAE,GACd,gBAAgB,EAAE,CAKpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,gBAAgB,EAAE,GAC5B,KAAK,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAYvE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-alpha.7 (#64) — pure scoring + filtering helpers for the
|
|
3
|
+
* refactor command. No IO; testable in isolation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Compute the cost / benefit / benefit-per-cost numbers for one
|
|
7
|
+
* proposal. Cost is `abs(estimatedLocDelta) * filesAffected.length`
|
|
8
|
+
* (clamped to ≥1 so the ratio is finite for zero-cost edge cases).
|
|
9
|
+
*/
|
|
10
|
+
export function assessProposal(p) {
|
|
11
|
+
const fileCount = p.filesAffected.length || 0;
|
|
12
|
+
const rawCost = Math.abs(p.estimatedLocDelta) * Math.max(1, fileCount);
|
|
13
|
+
const cost = Math.max(1, rawCost);
|
|
14
|
+
const benefit = p.estimatedValueScore;
|
|
15
|
+
return {
|
|
16
|
+
proposalId: p.id,
|
|
17
|
+
costScore: cost,
|
|
18
|
+
benefitScore: benefit,
|
|
19
|
+
benefitPerCost: benefit / cost,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Match a single file path against a glob-ish scope pattern. Supports:
|
|
24
|
+
* - exact match: "src/foo.ts"
|
|
25
|
+
* - directory prefix: "src/" (matches anything under src/)
|
|
26
|
+
* - star at the end: "src/components/*" (one segment after)
|
|
27
|
+
* - double-star: "src/**" (any depth)
|
|
28
|
+
*
|
|
29
|
+
* Pure regex translation; no fs lookup.
|
|
30
|
+
*/
|
|
31
|
+
export function matchesScope(filePath, pattern) {
|
|
32
|
+
if (pattern === filePath)
|
|
33
|
+
return true;
|
|
34
|
+
if (pattern.endsWith("/")) {
|
|
35
|
+
return filePath.startsWith(pattern);
|
|
36
|
+
}
|
|
37
|
+
if (pattern.endsWith("/**")) {
|
|
38
|
+
return filePath.startsWith(pattern.slice(0, -3) + "/") || filePath === pattern.slice(0, -3);
|
|
39
|
+
}
|
|
40
|
+
if (pattern.endsWith("/*")) {
|
|
41
|
+
const dir = pattern.slice(0, -2);
|
|
42
|
+
if (!filePath.startsWith(dir + "/"))
|
|
43
|
+
return false;
|
|
44
|
+
const rest = filePath.slice(dir.length + 1);
|
|
45
|
+
return !rest.includes("/");
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Filter proposals to those whose `filesAffected` ALL match at least
|
|
51
|
+
* one scope pattern. A proposal that touches even one out-of-scope
|
|
52
|
+
* file is excluded entirely (boundedness rule — refactors stay
|
|
53
|
+
* surgical).
|
|
54
|
+
*/
|
|
55
|
+
export function filterProposalsByScope(proposals, scope) {
|
|
56
|
+
if (scope.length === 0)
|
|
57
|
+
return proposals.slice();
|
|
58
|
+
return proposals.filter((p) => p.filesAffected.every((f) => scope.some((pat) => matchesScope(f, pat))));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Rank a list of proposals by benefit-per-cost (descending). Returns
|
|
62
|
+
* a NEW array of [proposal, assessment] pairs; doesn't mutate input.
|
|
63
|
+
* Stable for ties (preserves original order).
|
|
64
|
+
*/
|
|
65
|
+
export function rankProposals(proposals) {
|
|
66
|
+
const indexed = proposals.map((p, i) => ({
|
|
67
|
+
proposal: p,
|
|
68
|
+
assessment: assessProposal(p),
|
|
69
|
+
origIndex: i,
|
|
70
|
+
}));
|
|
71
|
+
indexed.sort((a, b) => {
|
|
72
|
+
const diff = b.assessment.benefitPerCost - a.assessment.benefitPerCost;
|
|
73
|
+
if (diff !== 0)
|
|
74
|
+
return diff;
|
|
75
|
+
return a.origIndex - b.origIndex; // stable
|
|
76
|
+
});
|
|
77
|
+
return indexed.map(({ proposal, assessment }) => ({ proposal, assessment }));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../../src/commands/refactor/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-alpha.7 (#64) — refactor command data shapes.
|
|
3
|
+
*
|
|
4
|
+
* The refactor command takes a list of candidate refactors (proposals)
|
|
5
|
+
* + computes per-proposal cost/benefit, filters by codebase scope,
|
|
6
|
+
* and ranks them. Proposals come from a source the cli treats as
|
|
7
|
+
* opaque today — could be hand-authored JSON, recon emissions, or a
|
|
8
|
+
* future LLM proposer agent. The scoring + filtering logic stays
|
|
9
|
+
* separate so any source feeds into the same ranking.
|
|
10
|
+
*
|
|
11
|
+
* Goals:
|
|
12
|
+
* - Boundedness: caller specifies scope (e.g. `src/components/**`)
|
|
13
|
+
* so refactors don't sprawl across the repo.
|
|
14
|
+
* - Cheapness-first: prefer high-value low-cost proposals; defer
|
|
15
|
+
* expensive ones until they pay back.
|
|
16
|
+
* - Auditability: each proposal carries a rationale + estimated
|
|
17
|
+
* deltas so the cli can show its work + a PM can sanity-check.
|
|
18
|
+
*
|
|
19
|
+
* Out of scope for α.7 (deferred):
|
|
20
|
+
* - LLM-backed proposer that READS the codebase + suggests
|
|
21
|
+
* refactors. The pure ranking layer ships first; proposer comes
|
|
22
|
+
* when the data shape has settled empirically.
|
|
23
|
+
* - Auto-application. α.7 ranks + reports; a future α may apply.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* A single candidate refactor. The cli treats `id` as opaque (caller
|
|
27
|
+
* picks; e.g., a slug or hash). `filesAffected` is canonical (no
|
|
28
|
+
* globs in the proposal itself — globs live in scope filters).
|
|
29
|
+
*/
|
|
30
|
+
export interface RefactorProposal {
|
|
31
|
+
/** Stable identifier, caller-chosen. */
|
|
32
|
+
id: string;
|
|
33
|
+
/** One-line summary (≤120 chars). Shown in ranked output. */
|
|
34
|
+
title: string;
|
|
35
|
+
/** What the refactor changes + WHY it's worth doing. */
|
|
36
|
+
rationale: string;
|
|
37
|
+
/** Concrete file paths the refactor would touch. Repo-relative. */
|
|
38
|
+
filesAffected: string[];
|
|
39
|
+
/** Net lines changed estimate (added - removed). Negative is fine
|
|
40
|
+
* (refactors that reduce LOC are usually high-value). */
|
|
41
|
+
estimatedLocDelta: number;
|
|
42
|
+
/** Subjective value score, 0-10. Caller's job to populate. */
|
|
43
|
+
estimatedValueScore: number;
|
|
44
|
+
/** Optional: external value signals (e.g., dup-count eliminated,
|
|
45
|
+
* cyclomatic-complexity drop). Surface-only; no scoring weight
|
|
46
|
+
* applied unless the caller folds them into estimatedValueScore. */
|
|
47
|
+
evidence?: Record<string, number | string>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The cost/benefit assessment of a single proposal. Pure: derived
|
|
51
|
+
* from the proposal alone (no IO).
|
|
52
|
+
*/
|
|
53
|
+
export interface ProposalAssessment {
|
|
54
|
+
proposalId: string;
|
|
55
|
+
/** Cost: |estimatedLocDelta| × number of files (handwave, but
|
|
56
|
+
* monotonic with reviewer effort). Higher = more expensive. */
|
|
57
|
+
costScore: number;
|
|
58
|
+
/** Benefit: estimatedValueScore as-is. */
|
|
59
|
+
benefitScore: number;
|
|
60
|
+
/** Ratio: benefit / max(cost, 1). Higher = better return per unit
|
|
61
|
+
* effort. Used as the default ranking key. */
|
|
62
|
+
benefitPerCost: number;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/commands/refactor/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB;8DAC0D;IAC1D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;yEAEqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB;oEACgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB;mDAC+C;IAC/C,cAAc,EAAE,MAAM,CAAC;CACxB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.19.0-alpha.7 (#64) — refactor command data shapes.
|
|
3
|
+
*
|
|
4
|
+
* The refactor command takes a list of candidate refactors (proposals)
|
|
5
|
+
* + computes per-proposal cost/benefit, filters by codebase scope,
|
|
6
|
+
* and ranks them. Proposals come from a source the cli treats as
|
|
7
|
+
* opaque today — could be hand-authored JSON, recon emissions, or a
|
|
8
|
+
* future LLM proposer agent. The scoring + filtering logic stays
|
|
9
|
+
* separate so any source feeds into the same ranking.
|
|
10
|
+
*
|
|
11
|
+
* Goals:
|
|
12
|
+
* - Boundedness: caller specifies scope (e.g. `src/components/**`)
|
|
13
|
+
* so refactors don't sprawl across the repo.
|
|
14
|
+
* - Cheapness-first: prefer high-value low-cost proposals; defer
|
|
15
|
+
* expensive ones until they pay back.
|
|
16
|
+
* - Auditability: each proposal carries a rationale + estimated
|
|
17
|
+
* deltas so the cli can show its work + a PM can sanity-check.
|
|
18
|
+
*
|
|
19
|
+
* Out of scope for α.7 (deferred):
|
|
20
|
+
* - LLM-backed proposer that READS the codebase + suggests
|
|
21
|
+
* refactors. The pure ranking layer ships first; proposer comes
|
|
22
|
+
* when the data shape has settled empirically.
|
|
23
|
+
* - Auto-application. α.7 ranks + reports; a future α may apply.
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/commands/refactor/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slowcook-ai/cli",
|
|
3
|
-
"version": "0.19.0-alpha.
|
|
3
|
+
"version": "0.19.0-alpha.11",
|
|
4
4
|
"description": "CLI for the slowcook brewing harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "aminazar",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"zod": "^3.23.8",
|
|
41
41
|
"@slowcook-ai/core": "^0.13.0",
|
|
42
42
|
"@slowcook-ai/stack-ts": "^0.9.9-alpha.0",
|
|
43
|
-
"@slowcook-ai/llm-anthropic": "^0.16.0-alpha.0",
|
|
44
|
-
"@slowcook-ai/recorder": "^0.9.1",
|
|
45
43
|
"@slowcook-ai/review-overlay": "^0.5.5",
|
|
44
|
+
"@slowcook-ai/llm-anthropic": "^0.16.0-alpha.1",
|
|
45
|
+
"@slowcook-ai/recorder": "^0.9.1",
|
|
46
46
|
"@slowcook-ai/forge-github": "^0.12.0"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|