council-review 0.1.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/LICENSE +21 -0
- package/README.md +374 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +19 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +829 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +61 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +235 -0
- package/dist/config.js.map +1 -0
- package/dist/herdr.d.ts +36 -0
- package/dist/herdr.d.ts.map +1 -0
- package/dist/herdr.js +196 -0
- package/dist/herdr.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/levels.d.ts +9 -0
- package/dist/levels.d.ts.map +1 -0
- package/dist/levels.js +19 -0
- package/dist/levels.js.map +1 -0
- package/dist/merge.d.ts +90 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +441 -0
- package/dist/merge.js.map +1 -0
- package/dist/node-guard.d.ts +9 -0
- package/dist/node-guard.d.ts.map +1 -0
- package/dist/node-guard.js +21 -0
- package/dist/node-guard.js.map +1 -0
- package/dist/panel.d.ts +89 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +275 -0
- package/dist/panel.js.map +1 -0
- package/dist/picker.d.ts +24 -0
- package/dist/picker.d.ts.map +1 -0
- package/dist/picker.js +182 -0
- package/dist/picker.js.map +1 -0
- package/dist/providers.d.ts +66 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +544 -0
- package/dist/providers.js.map +1 -0
- package/dist/report.d.ts +116 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +493 -0
- package/dist/report.js.map +1 -0
- package/dist/resolve.d.ts +58 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +147 -0
- package/dist/resolve.js.map +1 -0
- package/dist/reviewer-spawn.d.ts +67 -0
- package/dist/reviewer-spawn.d.ts.map +1 -0
- package/dist/reviewer-spawn.js +94 -0
- package/dist/reviewer-spawn.js.map +1 -0
- package/dist/reviewer-tools.d.ts +113 -0
- package/dist/reviewer-tools.d.ts.map +1 -0
- package/dist/reviewer-tools.js +557 -0
- package/dist/reviewer-tools.js.map +1 -0
- package/dist/runner.d.ts +79 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +811 -0
- package/dist/runner.js.map +1 -0
- package/dist/schema.d.ts +63 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +302 -0
- package/dist/schema.js.map +1 -0
- package/dist/scope.d.ts +40 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +372 -0
- package/dist/scope.js.map +1 -0
- package/dist/snapshot.d.ts +57 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +471 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/status.d.ts +90 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +336 -0
- package/dist/status.js.map +1 -0
- package/dist/thinking.d.ts +53 -0
- package/dist/thinking.d.ts.map +1 -0
- package/dist/thinking.js +116 -0
- package/dist/thinking.js.map +1 -0
- package/package.json +81 -0
- package/src/bin.ts +20 -0
- package/src/cli.ts +996 -0
- package/src/config.ts +338 -0
- package/src/herdr.ts +231 -0
- package/src/index.ts +30 -0
- package/src/levels.ts +23 -0
- package/src/merge.ts +548 -0
- package/src/node-guard.ts +25 -0
- package/src/panel.ts +354 -0
- package/src/picker.ts +230 -0
- package/src/providers.ts +778 -0
- package/src/report.ts +594 -0
- package/src/resolve.ts +179 -0
- package/src/reviewer-spawn.ts +131 -0
- package/src/reviewer-tools.ts +715 -0
- package/src/runner.ts +1120 -0
- package/src/schema.ts +373 -0
- package/src/scope.ts +448 -0
- package/src/snapshot.ts +536 -0
- package/src/status.ts +470 -0
- package/src/thinking.ts +148 -0
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { MergedFinding } from './merge.js';
|
|
2
|
+
import type { ResolutionOutcome } from './resolve.js';
|
|
3
|
+
import type { ReviewerResult, RunPanelOutcome } from './runner.js';
|
|
4
|
+
import type { ResolvedScope } from './scope.js';
|
|
5
|
+
import type { SnapshotIdentity } from './snapshot.js';
|
|
6
|
+
export interface RunDir {
|
|
7
|
+
id: string;
|
|
8
|
+
path: string;
|
|
9
|
+
reviewsDir: string;
|
|
10
|
+
}
|
|
11
|
+
/** `<projectRoot>/.council/reviews` — the container directory for every run. */
|
|
12
|
+
export declare function reviewsDirPath(projectRoot: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a fresh, uniquely named run directory (plus its `reviewers/` subdirectory) under
|
|
15
|
+
* `<projectRoot>/.council/reviews`. On the vanishingly unlikely event that two runs start in the
|
|
16
|
+
* same millisecond, a numeric suffix is appended to the id until directory creation succeeds —
|
|
17
|
+
* the suffixed id is still lexicographically greater than its unsuffixed base (a string that is a
|
|
18
|
+
* strict prefix of another always sorts before it), so chronological ordering is preserved either
|
|
19
|
+
* way.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createRunDir(projectRoot: string): RunDir;
|
|
22
|
+
/**
|
|
23
|
+
* Atomically creates or replaces the `last` symlink to point at `run`. Symlinking to the run's
|
|
24
|
+
* own id (a relative target, not an absolute path) keeps the pointer valid even if the whole
|
|
25
|
+
* `.council/reviews` directory is later moved or copied.
|
|
26
|
+
*
|
|
27
|
+
* `fs.renameSync` is used rather than removing then recreating `last`, because rename is atomic
|
|
28
|
+
* on POSIX: there is no window in which `last` is absent or pointing at a half-written target.
|
|
29
|
+
* The caller must be able to rely on this — it's what makes "the pointer is updated even by a
|
|
30
|
+
* degraded run" true in the presence of a crash immediately after this call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function updateLastPointer(run: RunDir): void;
|
|
33
|
+
/**
|
|
34
|
+
* Deterministic, collision-free filename slug for a `provider/model` pair. A pure
|
|
35
|
+
* lowercase-and-replace scheme is not collision-free on its own — `openrouter/a/b` and
|
|
36
|
+
* `openrouter/a-b` would both sanitize to the same text — so the slug always carries an 8-hex-char
|
|
37
|
+
* SHA-256 prefix of the exact, untouched `"provider/model"` string. Two different models collide
|
|
38
|
+
* on a filename only if they collide on that hash, which is what "collision-free" is grounded in
|
|
39
|
+
* here rather than in the sanitization itself.
|
|
40
|
+
*/
|
|
41
|
+
export declare function slugifyModel(provider: string, model: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Writes the three raw artifacts for one reviewer, regardless of its outcome — including a
|
|
44
|
+
* reviewer that failed validation on both attempts. `findings` is written as `null` (valid JSON)
|
|
45
|
+
* when the reviewer never produced a schema-valid block, which is itself informative: the file's
|
|
46
|
+
* presence, not its content, is what the spec requires for every reviewer.
|
|
47
|
+
*/
|
|
48
|
+
export declare function writeReviewerArtifacts(run: RunDir, r: ReviewerResult): void;
|
|
49
|
+
/** The exact text written to `findings.json` and emitted in machine-readable mode — kept as one
|
|
50
|
+
* function so the two can never drift apart. */
|
|
51
|
+
export declare function serializeFindings(findings: readonly MergedFinding[]): string;
|
|
52
|
+
export declare function writeFindings(run: RunDir, findings: readonly MergedFinding[]): string;
|
|
53
|
+
/**
|
|
54
|
+
* Machine-readable output mode (task 15.8): writes ONLY the merged findings document to `stream`
|
|
55
|
+
* (defaulting to `process.stdout`, overridable for tests). This function touches no other stream
|
|
56
|
+
* and prints nothing else, which is what keeps stdout parseable in that mode — the caller (the
|
|
57
|
+
* CLI) is responsible for routing progress and diagnostics to stderr instead of calling
|
|
58
|
+
* `console.log`/`process.stdout.write` itself while this mode is active.
|
|
59
|
+
*/
|
|
60
|
+
export declare function emitMachineReadableFindings(findings: readonly MergedFinding[], stream?: NodeJS.WritableStream): void;
|
|
61
|
+
export interface ManifestInput {
|
|
62
|
+
run: RunDir;
|
|
63
|
+
identity: SnapshotIdentity;
|
|
64
|
+
scope: ResolvedScope;
|
|
65
|
+
outcome: RunPanelOutcome;
|
|
66
|
+
suppressed: number;
|
|
67
|
+
overrides: {
|
|
68
|
+
allowCorrelated: boolean;
|
|
69
|
+
includeContextFiles: boolean;
|
|
70
|
+
noSuppress: boolean;
|
|
71
|
+
};
|
|
72
|
+
hostVersion: string | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Writes the manifest: everything needed to explain and reproduce a run, per the reporting
|
|
76
|
+
* spec's "Manifest contract" requirement. Deliberately omits `scope.patch` (the diff text) —
|
|
77
|
+
* that content already exists verbatim as `patch.diff`, and there is no reason for a document
|
|
78
|
+
* whose scenarios are about reproducibility metadata to carry a second copy of it.
|
|
79
|
+
*
|
|
80
|
+
* Nothing here ever reads `process.env`, a credential store, or reviewer stdout beyond the
|
|
81
|
+
* already-structured `ReviewerResult` fields (`usage`, `cost`, `depth`, `state`, `error`) — see
|
|
82
|
+
* the "Manifest carries no credentials" test in `test/unit/report.test.ts`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function writeManifest(i: ManifestInput): string;
|
|
85
|
+
/**
|
|
86
|
+
* Writes the human report. Ordering is fixed by the spec and is exactly the order this function
|
|
87
|
+
* builds it in: the resolution summary (when resolution diffing was requested — i.e. `resolution
|
|
88
|
+
* !== null` — whether or not a baseline turned out to be available), then the panel, then
|
|
89
|
+
* findings in merged (agreement) order, then the footer's suppressed/degraded/cost summary.
|
|
90
|
+
*
|
|
91
|
+
* A run with no findings still produces a complete, valid report: the panel section is unaffected
|
|
92
|
+
* and the findings section states plainly that none were raised, per the "Report for a run with
|
|
93
|
+
* no findings" scenario.
|
|
94
|
+
*/
|
|
95
|
+
export declare function writeReport(run: RunDir, i: ManifestInput, findings: readonly MergedFinding[], resolution: ResolutionOutcome | null): string;
|
|
96
|
+
/**
|
|
97
|
+
* Writes the handoff prompt — addressed to a coding agent, not to a person — naming the run
|
|
98
|
+
* directory and the merged findings file, requiring reproduction before any fix, directing
|
|
99
|
+
* ambiguity to the raw per-reviewer artifacts, and framing a low-agreement finding as a
|
|
100
|
+
* hypothesis to test rather than an established defect.
|
|
101
|
+
*/
|
|
102
|
+
export declare function writeHandoff(run: RunDir, findingsPath: string): string;
|
|
103
|
+
/** Every run directory under `<projectRoot>/.council/reviews`, newest first. Lexicographic sort
|
|
104
|
+
* is chronological sort, per `createRunDir`'s id format. Returns `[]` when no run has ever been
|
|
105
|
+
* written (the reviews directory does not exist yet), rather than throwing. */
|
|
106
|
+
export declare function listRuns(projectRoot: string): RunDir[];
|
|
107
|
+
/**
|
|
108
|
+
* Prunes stored runs, retaining the `keep` most recent ones, and never removing the run `last`
|
|
109
|
+
* resolves to even if it would otherwise fall outside that count (it never does in practice,
|
|
110
|
+
* since `last` always points at the newest run, but the protection is explicit rather than
|
|
111
|
+
* incidental). Returns the ids removed; an empty array when there was nothing to prune.
|
|
112
|
+
*/
|
|
113
|
+
export declare function gcRuns(projectRoot: string, keep: number): {
|
|
114
|
+
removed: string[];
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtD,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE1D;AAsBD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAqBxD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAiBnD;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpE;AAMD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,cAAc,GAAG,IAAI,CAoB3E;AAMD;gDACgD;AAChD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAE5E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAIrF;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,MAAM,GAAE,MAAM,CAAC,cAA+B,GAC7C,IAAI,CAEN;AAMD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE;QAAE,eAAe,EAAE,OAAO,CAAC;QAAC,mBAAmB,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3F,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAoEtD;AAoFD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,aAAa,EAChB,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,UAAU,EAAE,iBAAiB,GAAG,IAAI,GACnC,MAAM,CA8DR;AAMD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CA4CtE;AAMD;;+EAE+E;AAC/E,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBtD;AAYD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAgB/E"}
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes everything a run leaves behind: the run directory itself and the most-recent pointer,
|
|
3
|
+
* the manifest, the per-reviewer raw artifacts, the merged findings document, the human report,
|
|
4
|
+
* the handoff prompt, and the machine-readable stdout form of the findings document. Also reads
|
|
5
|
+
* and prunes past runs (`listRuns`, `gcRuns`), which back the `show` and `gc` CLI commands.
|
|
6
|
+
*
|
|
7
|
+
* See `openspec/changes/add-council-review/specs/council-review/reporting/spec.md` for the
|
|
8
|
+
* requirements this implements, and CONTRACT.md's "PINNED: run-directory layout and the
|
|
9
|
+
* most-recent pointer" for the exact on-disk shape other sections (`resolve.ts`, `cli.ts`) read
|
|
10
|
+
* against:
|
|
11
|
+
*
|
|
12
|
+
* .council/reviews/
|
|
13
|
+
* <run-id>/
|
|
14
|
+
* manifest.json
|
|
15
|
+
* patch.diff written by snapshot.ts's writePatch(), never by this module
|
|
16
|
+
* findings.json JSON array of MergedFinding
|
|
17
|
+
* REPORT.md
|
|
18
|
+
* HANDOFF.md
|
|
19
|
+
* reviewers/<slug>.findings.json | .text.md | .trace.jsonl
|
|
20
|
+
* last -> <run-id> SYMLINK to the most recent run directory
|
|
21
|
+
*
|
|
22
|
+
* This module never calls a model, never touches the network, and never calls `process.exit`.
|
|
23
|
+
*/
|
|
24
|
+
import fs from 'node:fs';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { createHash } from 'node:crypto';
|
|
27
|
+
/** `<projectRoot>/.council/reviews` — the container directory for every run. */
|
|
28
|
+
export function reviewsDirPath(projectRoot) {
|
|
29
|
+
return path.join(projectRoot, '.council', 'reviews');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A timestamp id down to the millisecond, in a fixed-width, purely numeric-plus-`T`/`Z` form
|
|
33
|
+
* (`20260904T153012123Z`). Fixed-width fields with no separators mean lexicographic string order
|
|
34
|
+
* is exactly chronological order, and every character is filesystem-safe on POSIX (and Windows,
|
|
35
|
+
* incidentally, since the usual offender — `:` — never appears).
|
|
36
|
+
*/
|
|
37
|
+
function timestampId(now) {
|
|
38
|
+
const pad = (n, len = 2) => String(n).padStart(len, '0');
|
|
39
|
+
return (`${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}T` +
|
|
40
|
+
`${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(now.getUTCSeconds())}${pad(now.getUTCMilliseconds(), 3)}Z`);
|
|
41
|
+
}
|
|
42
|
+
function isEexistError(err) {
|
|
43
|
+
return (typeof err === 'object' && err !== null && err.code === 'EEXIST');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a fresh, uniquely named run directory (plus its `reviewers/` subdirectory) under
|
|
47
|
+
* `<projectRoot>/.council/reviews`. On the vanishingly unlikely event that two runs start in the
|
|
48
|
+
* same millisecond, a numeric suffix is appended to the id until directory creation succeeds —
|
|
49
|
+
* the suffixed id is still lexicographically greater than its unsuffixed base (a string that is a
|
|
50
|
+
* strict prefix of another always sorts before it), so chronological ordering is preserved either
|
|
51
|
+
* way.
|
|
52
|
+
*/
|
|
53
|
+
export function createRunDir(projectRoot) {
|
|
54
|
+
const reviewsDir = reviewsDirPath(projectRoot);
|
|
55
|
+
fs.mkdirSync(reviewsDir, { recursive: true });
|
|
56
|
+
const base = timestampId(new Date());
|
|
57
|
+
let id = base;
|
|
58
|
+
let dir = path.join(reviewsDir, id);
|
|
59
|
+
let suffix = 1;
|
|
60
|
+
for (;;) {
|
|
61
|
+
try {
|
|
62
|
+
fs.mkdirSync(dir);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (!isEexistError(err))
|
|
67
|
+
throw err;
|
|
68
|
+
suffix += 1;
|
|
69
|
+
id = `${base}-${suffix}`;
|
|
70
|
+
dir = path.join(reviewsDir, id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
fs.mkdirSync(path.join(dir, 'reviewers'));
|
|
74
|
+
return { id, path: dir, reviewsDir };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Atomically creates or replaces the `last` symlink to point at `run`. Symlinking to the run's
|
|
78
|
+
* own id (a relative target, not an absolute path) keeps the pointer valid even if the whole
|
|
79
|
+
* `.council/reviews` directory is later moved or copied.
|
|
80
|
+
*
|
|
81
|
+
* `fs.renameSync` is used rather than removing then recreating `last`, because rename is atomic
|
|
82
|
+
* on POSIX: there is no window in which `last` is absent or pointing at a half-written target.
|
|
83
|
+
* The caller must be able to rely on this — it's what makes "the pointer is updated even by a
|
|
84
|
+
* degraded run" true in the presence of a crash immediately after this call.
|
|
85
|
+
*/
|
|
86
|
+
export function updateLastPointer(run) {
|
|
87
|
+
const lastPath = path.join(run.reviewsDir, 'last');
|
|
88
|
+
const tmpPath = path.join(run.reviewsDir, `.last.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
89
|
+
fs.symlinkSync(run.id, tmpPath, 'dir');
|
|
90
|
+
try {
|
|
91
|
+
fs.renameSync(tmpPath, lastPath);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
try {
|
|
95
|
+
fs.rmSync(tmpPath, { force: true });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// best-effort cleanup of the temp link; the rename error is what matters
|
|
99
|
+
}
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// -------------------------------------------------------------------------------------------
|
|
104
|
+
// Filename slugification (task 15.2)
|
|
105
|
+
// -------------------------------------------------------------------------------------------
|
|
106
|
+
function sanitizeSegment(s) {
|
|
107
|
+
const cleaned = s
|
|
108
|
+
.toLowerCase()
|
|
109
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
110
|
+
.replace(/^-+|-+$/g, '');
|
|
111
|
+
return cleaned.length > 0 ? cleaned : 'x';
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Deterministic, collision-free filename slug for a `provider/model` pair. A pure
|
|
115
|
+
* lowercase-and-replace scheme is not collision-free on its own — `openrouter/a/b` and
|
|
116
|
+
* `openrouter/a-b` would both sanitize to the same text — so the slug always carries an 8-hex-char
|
|
117
|
+
* SHA-256 prefix of the exact, untouched `"provider/model"` string. Two different models collide
|
|
118
|
+
* on a filename only if they collide on that hash, which is what "collision-free" is grounded in
|
|
119
|
+
* here rather than in the sanitization itself.
|
|
120
|
+
*/
|
|
121
|
+
export function slugifyModel(provider, model) {
|
|
122
|
+
const key = `${provider}/${model}`;
|
|
123
|
+
const hash = createHash('sha256').update(key, 'utf8').digest('hex').slice(0, 8);
|
|
124
|
+
return `${hash}-${sanitizeSegment(provider)}__${sanitizeSegment(model)}`;
|
|
125
|
+
}
|
|
126
|
+
// -------------------------------------------------------------------------------------------
|
|
127
|
+
// Per-reviewer raw artifacts (task 15.3)
|
|
128
|
+
// -------------------------------------------------------------------------------------------
|
|
129
|
+
/**
|
|
130
|
+
* Writes the three raw artifacts for one reviewer, regardless of its outcome — including a
|
|
131
|
+
* reviewer that failed validation on both attempts. `findings` is written as `null` (valid JSON)
|
|
132
|
+
* when the reviewer never produced a schema-valid block, which is itself informative: the file's
|
|
133
|
+
* presence, not its content, is what the spec requires for every reviewer.
|
|
134
|
+
*/
|
|
135
|
+
export function writeReviewerArtifacts(run, r) {
|
|
136
|
+
const slug = slugifyModel(r.reviewer.provider, r.reviewer.model);
|
|
137
|
+
const dir = path.join(run.path, 'reviewers');
|
|
138
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
139
|
+
fs.writeFileSync(path.join(dir, `${slug}.findings.json`), `${JSON.stringify(r.findings, null, 2)}\n`, 'utf8');
|
|
140
|
+
const heading = `${r.reviewer.provider}/${r.reviewer.model}`;
|
|
141
|
+
const textParts = [`# ${heading} — attempt 1\n\n${r.finalText}`];
|
|
142
|
+
if (r.repairText !== null) {
|
|
143
|
+
textParts.push(`\n\n---\n\n# ${heading} — repair attempt\n\n${r.repairText}`);
|
|
144
|
+
}
|
|
145
|
+
fs.writeFileSync(path.join(dir, `${slug}.text.md`), `${textParts.join('')}\n`, 'utf8');
|
|
146
|
+
const trace = r.rawTrace.length > 0 ? `${r.rawTrace.join('\n')}\n` : '';
|
|
147
|
+
fs.writeFileSync(path.join(dir, `${slug}.trace.jsonl`), trace, 'utf8');
|
|
148
|
+
}
|
|
149
|
+
// -------------------------------------------------------------------------------------------
|
|
150
|
+
// Merged findings document
|
|
151
|
+
// -------------------------------------------------------------------------------------------
|
|
152
|
+
/** The exact text written to `findings.json` and emitted in machine-readable mode — kept as one
|
|
153
|
+
* function so the two can never drift apart. */
|
|
154
|
+
export function serializeFindings(findings) {
|
|
155
|
+
return `${JSON.stringify(findings, null, 2)}\n`;
|
|
156
|
+
}
|
|
157
|
+
export function writeFindings(run, findings) {
|
|
158
|
+
const dest = path.join(run.path, 'findings.json');
|
|
159
|
+
fs.writeFileSync(dest, serializeFindings(findings), 'utf8');
|
|
160
|
+
return dest;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Machine-readable output mode (task 15.8): writes ONLY the merged findings document to `stream`
|
|
164
|
+
* (defaulting to `process.stdout`, overridable for tests). This function touches no other stream
|
|
165
|
+
* and prints nothing else, which is what keeps stdout parseable in that mode — the caller (the
|
|
166
|
+
* CLI) is responsible for routing progress and diagnostics to stderr instead of calling
|
|
167
|
+
* `console.log`/`process.stdout.write` itself while this mode is active.
|
|
168
|
+
*/
|
|
169
|
+
export function emitMachineReadableFindings(findings, stream = process.stdout) {
|
|
170
|
+
stream.write(serializeFindings(findings));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Writes the manifest: everything needed to explain and reproduce a run, per the reporting
|
|
174
|
+
* spec's "Manifest contract" requirement. Deliberately omits `scope.patch` (the diff text) —
|
|
175
|
+
* that content already exists verbatim as `patch.diff`, and there is no reason for a document
|
|
176
|
+
* whose scenarios are about reproducibility metadata to carry a second copy of it.
|
|
177
|
+
*
|
|
178
|
+
* Nothing here ever reads `process.env`, a credential store, or reviewer stdout beyond the
|
|
179
|
+
* already-structured `ReviewerResult` fields (`usage`, `cost`, `depth`, `state`, `error`) — see
|
|
180
|
+
* the "Manifest carries no credentials" test in `test/unit/report.test.ts`.
|
|
181
|
+
*/
|
|
182
|
+
export function writeManifest(i) {
|
|
183
|
+
const manifest = {
|
|
184
|
+
runId: i.run.id,
|
|
185
|
+
createdAt: new Date().toISOString(),
|
|
186
|
+
hostVersion: i.hostVersion,
|
|
187
|
+
reviewed: {
|
|
188
|
+
commit: i.identity.head,
|
|
189
|
+
dirty: i.identity.dirty,
|
|
190
|
+
treeHash: i.identity.treeHash,
|
|
191
|
+
},
|
|
192
|
+
scope: {
|
|
193
|
+
mode: i.scope.mode,
|
|
194
|
+
selectors: i.scope.selectors,
|
|
195
|
+
baseBranch: i.scope.baseBranch,
|
|
196
|
+
mergeBase: i.scope.mergeBase,
|
|
197
|
+
endRevision: i.scope.endRevision,
|
|
198
|
+
head: i.scope.head,
|
|
199
|
+
dirty: i.scope.dirty,
|
|
200
|
+
empty: i.scope.empty,
|
|
201
|
+
files: i.scope.files,
|
|
202
|
+
},
|
|
203
|
+
panel: i.outcome.results.map((r) => ({
|
|
204
|
+
provider: r.reviewer.provider,
|
|
205
|
+
model: r.reviewer.model,
|
|
206
|
+
vendor: r.reviewer.vendor,
|
|
207
|
+
thinking: {
|
|
208
|
+
requested: r.reviewer.thinking.requested,
|
|
209
|
+
effective: r.reviewer.thinking.effective,
|
|
210
|
+
applicable: r.reviewer.thinking.applicable,
|
|
211
|
+
clamped: r.reviewer.thinking.clamped,
|
|
212
|
+
},
|
|
213
|
+
})),
|
|
214
|
+
reviewers: i.outcome.results.map((r) => ({
|
|
215
|
+
provider: r.reviewer.provider,
|
|
216
|
+
model: r.reviewer.model,
|
|
217
|
+
state: r.state,
|
|
218
|
+
startedAt: r.startedAt,
|
|
219
|
+
endedAt: r.endedAt,
|
|
220
|
+
durationMs: r.endedAt - r.startedAt,
|
|
221
|
+
usage: r.usage,
|
|
222
|
+
cost: r.cost,
|
|
223
|
+
depth: {
|
|
224
|
+
filesOpenedCount: r.depth.filesOpened.length,
|
|
225
|
+
filesOpened: r.depth.filesOpened,
|
|
226
|
+
searches: r.depth.searches,
|
|
227
|
+
},
|
|
228
|
+
...(r.error !== undefined ? { error: r.error } : {}),
|
|
229
|
+
})),
|
|
230
|
+
counts: {
|
|
231
|
+
launched: i.outcome.launched,
|
|
232
|
+
reporting: i.outcome.reporting,
|
|
233
|
+
suppressed: i.suppressed,
|
|
234
|
+
},
|
|
235
|
+
cost: {
|
|
236
|
+
total: i.outcome.totalCost, // null === unknown, never coerced to 0
|
|
237
|
+
incomplete: i.outcome.costIncomplete,
|
|
238
|
+
},
|
|
239
|
+
degraded: i.outcome.degraded,
|
|
240
|
+
overrides: {
|
|
241
|
+
allowCorrelated: i.overrides.allowCorrelated,
|
|
242
|
+
includeContextFiles: i.overrides.includeContextFiles,
|
|
243
|
+
noSuppress: i.overrides.noSuppress,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
const dest = path.join(i.run.path, 'manifest.json');
|
|
247
|
+
fs.writeFileSync(dest, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
248
|
+
return dest;
|
|
249
|
+
}
|
|
250
|
+
// -------------------------------------------------------------------------------------------
|
|
251
|
+
// REPORT.md (task 15.6)
|
|
252
|
+
// -------------------------------------------------------------------------------------------
|
|
253
|
+
function formatThinking(t) {
|
|
254
|
+
if (!t.applicable)
|
|
255
|
+
return 'n/a';
|
|
256
|
+
if (t.effective === null)
|
|
257
|
+
return 'none';
|
|
258
|
+
return t.clamped ? `${t.effective} (clamped from ${t.requested})` : t.effective;
|
|
259
|
+
}
|
|
260
|
+
function formatCost(cost) {
|
|
261
|
+
return cost === null ? 'unknown' : `$${cost.toFixed(4)}`;
|
|
262
|
+
}
|
|
263
|
+
function formatDepth(d) {
|
|
264
|
+
const files = d.filesOpened.length;
|
|
265
|
+
return `${files} file${files === 1 ? '' : 's'} opened, ${d.searches} search${d.searches === 1 ? '' : 'es'}`;
|
|
266
|
+
}
|
|
267
|
+
function renderResolutionSection(resolution) {
|
|
268
|
+
if (resolution.baselineRunId === null) {
|
|
269
|
+
return [
|
|
270
|
+
'## Resolution',
|
|
271
|
+
'',
|
|
272
|
+
'No baseline run was available for comparison, so findings below carry no resolution ' +
|
|
273
|
+
'marking. This is the first run to be diffed, not a run with zero new findings.',
|
|
274
|
+
'',
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
const { resolved, stillPresent, new: newCount } = resolution.counts;
|
|
278
|
+
const lines = [
|
|
279
|
+
'## Resolution',
|
|
280
|
+
'',
|
|
281
|
+
`Compared against run \`${resolution.baselineRunId}\`: ${resolved} resolved, ` +
|
|
282
|
+
`${stillPresent} still present, ${newCount} new.`,
|
|
283
|
+
'',
|
|
284
|
+
];
|
|
285
|
+
if (resolution.resolved.length > 0) {
|
|
286
|
+
lines.push('Resolved since the baseline run:', '');
|
|
287
|
+
for (const f of resolution.resolved) {
|
|
288
|
+
lines.push(`- \`${f.file}:${f.line}\` — ${f.claim}`);
|
|
289
|
+
}
|
|
290
|
+
lines.push('');
|
|
291
|
+
}
|
|
292
|
+
return lines;
|
|
293
|
+
}
|
|
294
|
+
function renderFinding(f, resolutionByFingerprint) {
|
|
295
|
+
const marker = resolutionByFingerprint?.get(f.fingerprint) ?? null;
|
|
296
|
+
const markerText = marker ? ` [${marker}]` : '';
|
|
297
|
+
const location = f.endLine !== null ? `${f.file}:${f.line}-${f.endLine}` : `${f.file}:${f.line}`;
|
|
298
|
+
const lines = [
|
|
299
|
+
`### ${f.id} — ${f.severity.toUpperCase()} · ${f.category}${markerText}`,
|
|
300
|
+
'',
|
|
301
|
+
`**Location:** \`${location}\``,
|
|
302
|
+
`**Agreement:** ${f.agreement.raisers}/${f.agreement.reporting} reviewers (${f.raisedBy.join(', ')})`,
|
|
303
|
+
`**Claim:** ${f.claim}`,
|
|
304
|
+
`**Impact:** ${f.impact}`,
|
|
305
|
+
];
|
|
306
|
+
if (f.unverifiable) {
|
|
307
|
+
lines.push('', '_This finding references a path absent from the reviewed snapshot; it could not be verified._');
|
|
308
|
+
}
|
|
309
|
+
if (f.evidence.length > 0) {
|
|
310
|
+
lines.push('', '**Evidence:**');
|
|
311
|
+
for (const e of f.evidence)
|
|
312
|
+
lines.push(`- ${e}`);
|
|
313
|
+
}
|
|
314
|
+
if (f.suggestions.length > 0) {
|
|
315
|
+
lines.push('', '**Suggestions:**');
|
|
316
|
+
for (const s of f.suggestions)
|
|
317
|
+
lines.push(`- ${s}`);
|
|
318
|
+
}
|
|
319
|
+
lines.push('');
|
|
320
|
+
return lines;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Writes the human report. Ordering is fixed by the spec and is exactly the order this function
|
|
324
|
+
* builds it in: the resolution summary (when resolution diffing was requested — i.e. `resolution
|
|
325
|
+
* !== null` — whether or not a baseline turned out to be available), then the panel, then
|
|
326
|
+
* findings in merged (agreement) order, then the footer's suppressed/degraded/cost summary.
|
|
327
|
+
*
|
|
328
|
+
* A run with no findings still produces a complete, valid report: the panel section is unaffected
|
|
329
|
+
* and the findings section states plainly that none were raised, per the "Report for a run with
|
|
330
|
+
* no findings" scenario.
|
|
331
|
+
*/
|
|
332
|
+
export function writeReport(run, i, findings, resolution) {
|
|
333
|
+
const lines = [`# Council Review — ${run.id}`, ''];
|
|
334
|
+
if (resolution !== null) {
|
|
335
|
+
lines.push(...renderResolutionSection(resolution));
|
|
336
|
+
}
|
|
337
|
+
lines.push('## Panel', '', '| Reviewer | Vendor | Thinking | Cost | Depth |', '|---|---|---|---|---|');
|
|
338
|
+
for (const r of i.outcome.results) {
|
|
339
|
+
lines.push(`| ${r.reviewer.provider}/${r.reviewer.model} | ${r.reviewer.vendor} | ` +
|
|
340
|
+
`${formatThinking(r.reviewer.thinking)} | ${formatCost(r.cost)} | ${formatDepth(r.depth)} |`);
|
|
341
|
+
}
|
|
342
|
+
lines.push('');
|
|
343
|
+
lines.push('## Findings', '');
|
|
344
|
+
if (findings.length === 0) {
|
|
345
|
+
lines.push('No findings were raised.', '');
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
const resolutionByFingerprint = resolution !== null
|
|
349
|
+
? new Map(resolution.current.map((f) => [f.fingerprint, f.resolution]))
|
|
350
|
+
: null;
|
|
351
|
+
for (const f of findings) {
|
|
352
|
+
lines.push(...renderFinding(f, resolutionByFingerprint));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
lines.push('## Summary', '');
|
|
356
|
+
lines.push(`- Launched: ${i.outcome.launched}, reporting: ${i.outcome.reporting}`);
|
|
357
|
+
lines.push(`- Suppressed: ${i.suppressed}`);
|
|
358
|
+
const degradedReviewers = i.outcome.results.filter((r) => r.state !== 'ok');
|
|
359
|
+
if (degradedReviewers.length > 0) {
|
|
360
|
+
const detail = degradedReviewers
|
|
361
|
+
.map((r) => `${r.reviewer.provider}/${r.reviewer.model} (${r.state}${r.error ? `: ${r.error}` : ''})`)
|
|
362
|
+
.join('; ');
|
|
363
|
+
lines.push(`- Degraded (${degradedReviewers.length}): ${detail}`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
lines.push('- Degraded: none');
|
|
367
|
+
}
|
|
368
|
+
const costSuffix = i.outcome.costIncomplete
|
|
369
|
+
? ' (incomplete — one or more reviewers had no cost data)'
|
|
370
|
+
: '';
|
|
371
|
+
lines.push(`- Total cost: ${i.outcome.totalCost === null ? 'unknown' : `$${i.outcome.totalCost.toFixed(4)}`}${costSuffix}`);
|
|
372
|
+
lines.push('');
|
|
373
|
+
const dest = path.join(run.path, 'REPORT.md');
|
|
374
|
+
fs.writeFileSync(dest, lines.join('\n'), 'utf8');
|
|
375
|
+
return dest;
|
|
376
|
+
}
|
|
377
|
+
// -------------------------------------------------------------------------------------------
|
|
378
|
+
// HANDOFF.md (task 15.7)
|
|
379
|
+
// -------------------------------------------------------------------------------------------
|
|
380
|
+
/**
|
|
381
|
+
* Writes the handoff prompt — addressed to a coding agent, not to a person — naming the run
|
|
382
|
+
* directory and the merged findings file, requiring reproduction before any fix, directing
|
|
383
|
+
* ambiguity to the raw per-reviewer artifacts, and framing a low-agreement finding as a
|
|
384
|
+
* hypothesis to test rather than an established defect.
|
|
385
|
+
*/
|
|
386
|
+
export function writeHandoff(run, findingsPath) {
|
|
387
|
+
const reviewersDir = path.join(run.path, 'reviewers');
|
|
388
|
+
const text = [
|
|
389
|
+
'# Council review handoff',
|
|
390
|
+
'',
|
|
391
|
+
'You are a coding agent picking up the output of an automated, multi-model code review panel.',
|
|
392
|
+
'Treat this document as your instructions for what to do next, not as a report to read.',
|
|
393
|
+
'',
|
|
394
|
+
`Run directory: \`${run.path}\``,
|
|
395
|
+
`Merged findings: \`${findingsPath}\``,
|
|
396
|
+
'',
|
|
397
|
+
'## Before you change any code',
|
|
398
|
+
'',
|
|
399
|
+
'Every entry in the merged findings file is a claim made by one or more independent reviewer ' +
|
|
400
|
+
'models reading a frozen snapshot of the changed code, not a confirmed defect. For each ' +
|
|
401
|
+
'finding you intend to act on: reproduce it first — read the referenced file and line range ' +
|
|
402
|
+
'yourself and confirm the claimed behavior actually occurs — before you change anything. Do ' +
|
|
403
|
+
'not edit code to address a finding you have not reproduced.',
|
|
404
|
+
'',
|
|
405
|
+
'## When a finding is ambiguous',
|
|
406
|
+
'',
|
|
407
|
+
"If a merged finding's claim, location or severity is unclear, do not guess at what the " +
|
|
408
|
+
`reviewer meant. Consult that reviewer's raw output under \`${reviewersDir}\`: the ` +
|
|
409
|
+
'`<slug>.text.md` file holds its verbatim response (both attempts, if it needed a repair), ' +
|
|
410
|
+
'and the `<slug>.trace.jsonl` file holds every tool call and stream event it produced. The ' +
|
|
411
|
+
'merged claim is a summary; the raw artifact is the source of truth for what a reviewer ' +
|
|
412
|
+
'actually said.',
|
|
413
|
+
'',
|
|
414
|
+
'## Low-agreement findings are hypotheses, not defects',
|
|
415
|
+
'',
|
|
416
|
+
"Each finding records how many of the panel's reviewers raised it out of how many reported " +
|
|
417
|
+
'(its `agreement` field). A finding raised by only one or two reviewers out of a larger ' +
|
|
418
|
+
'panel is a hypothesis worth testing, not an established defect: treat it exactly as you ' +
|
|
419
|
+
"would treat a colleague's unconfirmed suspicion — worth investigating, not worth acting on " +
|
|
420
|
+
'without confirmation. A finding every reporting reviewer agreed on still requires the same ' +
|
|
421
|
+
'reproduction step above, but a low-agreement one additionally requires you to explain, in ' +
|
|
422
|
+
'your own investigation, why the disagreeing reviewers did not see it before treating it as ' +
|
|
423
|
+
'real.',
|
|
424
|
+
'',
|
|
425
|
+
].join('\n');
|
|
426
|
+
const dest = path.join(run.path, 'HANDOFF.md');
|
|
427
|
+
fs.writeFileSync(dest, text, 'utf8');
|
|
428
|
+
return dest;
|
|
429
|
+
}
|
|
430
|
+
// -------------------------------------------------------------------------------------------
|
|
431
|
+
// Reading and pruning past runs (tasks feeding the `show` and `gc` CLI commands)
|
|
432
|
+
// -------------------------------------------------------------------------------------------
|
|
433
|
+
/** Every run directory under `<projectRoot>/.council/reviews`, newest first. Lexicographic sort
|
|
434
|
+
* is chronological sort, per `createRunDir`'s id format. Returns `[]` when no run has ever been
|
|
435
|
+
* written (the reviews directory does not exist yet), rather than throwing. */
|
|
436
|
+
export function listRuns(projectRoot) {
|
|
437
|
+
const reviewsDir = reviewsDirPath(projectRoot);
|
|
438
|
+
let entries;
|
|
439
|
+
try {
|
|
440
|
+
entries = fs.readdirSync(reviewsDir, { withFileTypes: true });
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
return [];
|
|
444
|
+
}
|
|
445
|
+
const ids = entries
|
|
446
|
+
.filter((e) => e.name !== 'last' && !e.name.startsWith('.'))
|
|
447
|
+
.map((e) => e.name)
|
|
448
|
+
.filter((name) => {
|
|
449
|
+
try {
|
|
450
|
+
return fs.statSync(path.join(reviewsDir, name)).isDirectory();
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
})
|
|
456
|
+
.sort()
|
|
457
|
+
.reverse();
|
|
458
|
+
return ids.map((id) => ({ id, path: path.join(reviewsDir, id), reviewsDir }));
|
|
459
|
+
}
|
|
460
|
+
/** Resolves the `last` symlink to the run id it points at, or `null` if it is absent, dangling, or
|
|
461
|
+
* not a symlink at all. Mirrors `resolve.ts`'s own reading of this pointer. */
|
|
462
|
+
function resolveLastRunId(reviewsDir) {
|
|
463
|
+
try {
|
|
464
|
+
return path.basename(fs.realpathSync(path.join(reviewsDir, 'last')));
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Prunes stored runs, retaining the `keep` most recent ones, and never removing the run `last`
|
|
472
|
+
* resolves to even if it would otherwise fall outside that count (it never does in practice,
|
|
473
|
+
* since `last` always points at the newest run, but the protection is explicit rather than
|
|
474
|
+
* incidental). Returns the ids removed; an empty array when there was nothing to prune.
|
|
475
|
+
*/
|
|
476
|
+
export function gcRuns(projectRoot, keep) {
|
|
477
|
+
const reviewsDir = reviewsDirPath(projectRoot);
|
|
478
|
+
const runs = listRuns(projectRoot); // newest first
|
|
479
|
+
const protectedId = resolveLastRunId(reviewsDir);
|
|
480
|
+
const keepCount = Math.max(0, keep);
|
|
481
|
+
const toKeep = new Set(runs.slice(0, keepCount).map((r) => r.id));
|
|
482
|
+
if (protectedId !== null)
|
|
483
|
+
toKeep.add(protectedId);
|
|
484
|
+
const removed = [];
|
|
485
|
+
for (const run of runs) {
|
|
486
|
+
if (toKeep.has(run.id))
|
|
487
|
+
continue;
|
|
488
|
+
fs.rmSync(run.path, { recursive: true, force: true });
|
|
489
|
+
removed.push(run.id);
|
|
490
|
+
}
|
|
491
|
+
return { removed };
|
|
492
|
+
}
|
|
493
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|