council-review 0.1.0 → 0.1.2

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.
Files changed (62) hide show
  1. package/README.md +44 -343
  2. package/dist/bin.js +0 -0
  3. package/dist/cli.d.ts +4 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +18 -5
  6. package/dist/cli.js.map +1 -1
  7. package/dist/codegraph.d.ts +36 -0
  8. package/dist/codegraph.d.ts.map +1 -0
  9. package/dist/codegraph.js +95 -0
  10. package/dist/codegraph.js.map +1 -0
  11. package/dist/config.d.ts +6 -0
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +13 -1
  14. package/dist/config.js.map +1 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/node-guard.d.ts +2 -1
  20. package/dist/node-guard.d.ts.map +1 -1
  21. package/dist/node-guard.js +27 -5
  22. package/dist/node-guard.js.map +1 -1
  23. package/dist/picker.d.ts +45 -1
  24. package/dist/picker.d.ts.map +1 -1
  25. package/dist/picker.js +127 -12
  26. package/dist/picker.js.map +1 -1
  27. package/dist/report.d.ts +2 -0
  28. package/dist/report.d.ts.map +1 -1
  29. package/dist/report.js +7 -0
  30. package/dist/report.js.map +1 -1
  31. package/dist/reviewer-spawn.d.ts +4 -2
  32. package/dist/reviewer-spawn.d.ts.map +1 -1
  33. package/dist/reviewer-spawn.js +4 -2
  34. package/dist/reviewer-spawn.js.map +1 -1
  35. package/dist/reviewer-tools.d.ts +21 -0
  36. package/dist/reviewer-tools.d.ts.map +1 -1
  37. package/dist/reviewer-tools.js +254 -7
  38. package/dist/reviewer-tools.js.map +1 -1
  39. package/dist/runner.d.ts.map +1 -1
  40. package/dist/runner.js +17 -10
  41. package/dist/runner.js.map +1 -1
  42. package/dist/snapshot.d.ts +11 -0
  43. package/dist/snapshot.d.ts.map +1 -1
  44. package/dist/snapshot.js +71 -6
  45. package/dist/snapshot.js.map +1 -1
  46. package/dist/status.d.ts +9 -3
  47. package/dist/status.d.ts.map +1 -1
  48. package/dist/status.js +48 -7
  49. package/dist/status.js.map +1 -1
  50. package/package.json +9 -9
  51. package/src/cli.ts +18 -4
  52. package/src/codegraph.ts +118 -0
  53. package/src/config.ts +23 -1
  54. package/src/index.ts +1 -0
  55. package/src/node-guard.ts +33 -5
  56. package/src/picker.ts +156 -15
  57. package/src/report.ts +11 -0
  58. package/src/reviewer-spawn.ts +5 -2
  59. package/src/reviewer-tools.ts +298 -5
  60. package/src/runner.ts +17 -10
  61. package/src/snapshot.ts +93 -8
  62. package/src/status.ts +47 -7
package/README.md CHANGED
@@ -2,372 +2,73 @@
2
2
 
3
3
  A read-only, multi-vendor code review panel over local git state.
4
4
 
5
- `council-review` sends the work in your git worktree to several independent models from
6
- **different vendors** not just different gateways and merges their findings in deterministic
7
- code, never through another model. Reviewers cannot write to anything: every host built-in is
8
- removed, the repository under review cannot load its own extensions or skills into the process
9
- reviewing it, and the only tools a reviewer has are four read-only ones scoped to a frozen,
10
- non-writable snapshot of your tree.
11
-
12
- Every run produces a timestamped directory holding a machine-readable manifest, each reviewer's
13
- raw output, the merged findings, a human report, and a handoff prompt addressed to a coding agent.
5
+ `council-review` sends the work in your git worktree to several models from **different vendors**
6
+ and merges their findings in deterministic code, never through another model. Reviewers cannot
7
+ write anything: every host built-in is removed, the repository under review cannot load its own
8
+ extensions, skills or context files into the process reviewing it, and a reviewer's whole tool
9
+ surface is four read-only tools scoped to a frozen, non-writable copy of your tree. That is not
10
+ container or VM isolation, and third-party models do read your source — see
11
+ [`HOST-VERSION.md`](./HOST-VERSION.md) and the
12
+ [design doc](./docs/design/council-review-design.md) for what was actually verified, and re-verify
13
+ after a host upgrade. Every run writes a timestamped directory under `.council/reviews/` holding a
14
+ manifest, each reviewer's raw output, the merged findings, a human report (`REPORT.md`) and a
15
+ handoff prompt addressed to a coding agent (`HANDOFF.md`).
14
16
 
15
17
  ## Install
16
18
 
17
- This package is not yet published to npm, so the recommended install is directly from GitHub.
18
-
19
- ### From GitHub (recommended)
20
-
21
- ```
22
- npm install -g github:ad-005/council-review
23
- ```
24
-
25
- This installs and builds straight from the repository and works today.
26
-
27
- ### From npm
28
-
29
19
  ```
30
20
  npm install -g council-review
31
21
  ```
32
22
 
33
- This will be the install once the package is published to npm. It is not published yet, so this
34
- command currently fails — the GitHub install above is the one that works.
35
-
36
- ### From source (development)
23
+ For unreleased changes from `main`, build from a clone:
37
24
 
38
25
  ```
39
- git clone https://github.com/ad-005/council-review.git
26
+ git clone https://github.com/ad-005/council-review
40
27
  cd council-review
41
28
  npm install
42
- npm run build
43
- npm link
29
+ npm pack
30
+ npm install -g ./council-review-*.tgz
44
31
  ```
45
32
 
46
- Use this if you're contributing to `council-review` itself. `npm link` makes the `council-review`
47
- command available globally from your checkout; alternatively, run it directly with
48
- `node dist/bin.js`.
33
+ Installing straight from the git URL is unreliable across npm versions: on the npm bundled with
34
+ Node 22 (10.x) `npm install -g github:ad-005/council-review` fails, because npm installs no
35
+ dependencies into its temporary clone and the `prepare` build cannot run; npm 12 fixed that but
36
+ disables git fetching by default, so it needs `--allow-git=all`. `npx github:ad-005/council-review`
37
+ works on npm 10, and needs `--allow-git=all` on npm 12.
49
38
 
50
- Requires Node.js 20 or later to install and run `council-review` itself, plus the
51
- [`pi`](#the-pi-host-dependency) coding-agent CLI on `PATH`, authenticated for at least one
52
- provider. `pi` itself requires Node.js **>= 22.19.0**, which is the effective floor for actually
53
- running a review — a Node 20 or 21 install satisfies `council-review`'s own guard but leaves every
54
- reviewer failing to spawn.
39
+ Requires the `pi` coding-agent CLI (`@earendil-works/pi-coding-agent`) on `PATH`, authenticated
40
+ for at least one provider through `pi`'s own credential store a key that exists only as a shell
41
+ variable never reaches a reviewer, because reviewer processes get a tight environment allowlist.
42
+ Both `council-review` and `pi` need Node **>= 22.19.0**.
55
43
 
56
- ## Quick start
44
+ ## Use
57
45
 
58
46
  ```
59
47
  cd your-project
60
- council-review init # pick a panel interactively; writes .council/config.json
61
- council-review # review the current worktree's work against its base branch
48
+ council-review init # pick a panel; writes .council/config.json (commit it)
49
+ council-review # review this worktree against its base branch
62
50
  ```
63
51
 
64
- `init` runs a three-stage picker providers, then models, then a thinking level per reasoning
65
- model and requires at least three models resolving to at least three distinct vendors before it
66
- will save a panel (the vendor-independence guard; see below). The saved panel is committed
67
- (`.council/config.json` is meant to be checked in) so the rest of the team reuses it without
68
- re-picking.
69
-
70
- Bare `council-review` reviews **the whole of the current worktree's divergence from its base
71
- branch** committed, staged, unstaged and untracked-but-not-gitignored changes, folded into one
72
- patch — not just what's staged. Use the scope flags below to narrow that.
73
-
74
- ## Scope flags
75
-
76
- | Flag | Meaning |
77
- | ---------------------------- | ------------------------------------------------ |
78
- | `--staged` | Review the staged index only |
79
- | `--range <A..B>` | Review a commit range |
80
- | `--revision <rev>` | Review a single revision's own change |
81
- | `--paths <glob>[,<glob>...]` | Narrow to matching paths (repeatable) |
82
- | `--base <branch>` | Override the configured base branch for this run |
83
-
84
- `--staged`, `--range` and `--revision` are mutually exclusive. An empty resolved scope (nothing to
85
- review) is not an error: the tool reports that and exits `0` without spawning a reviewer.
86
-
87
- ## Panel flags
88
-
89
- | Flag | Meaning |
90
- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
91
- | `--models <spec>` | One-off panel for this run only: comma-separated `provider/modelId[:level]` entries or globs. Does not touch the saved panel. |
92
- | `--pick` | Re-open interactive selection; the result **replaces** the saved panel |
93
- | `--thinking <level>` | Panel-wide thinking level (one of `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`) |
94
- | `--allow-correlated` | Waive the vendor-independence guard for this run (recorded in the manifest even when it wasn't needed) |
95
-
96
- `council-review models` lists every discovered, ready model with its provider, derived vendor,
97
- context window, cost rates and supported thinking levels — no prompt, no model call, works without
98
- a terminal.
99
-
100
- ### The vendor-independence guard
101
-
102
- A panel is admitted only with **at least three models resolving to at least three distinct
103
- vendors** (`unknown` counts as its own distinct vendor, so three unrecognised models still pass).
104
- This is checked at the end of selection and again immediately before every launch, so a
105
- hand-edited config is checked too. On refusal, nothing is spawned and the tool exits `4`. Pass
106
- `--allow-correlated` to waive both the vendor-count and the three-model minimum deliberately.
107
-
108
- ## Run flags
109
-
110
- | Flag | Meaning |
111
- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
112
- | `--timeout <seconds>` | Per-reviewer wall-clock timeout (overrides configured `timeoutSeconds`) |
113
- | `--max-tokens <n>` | Per-reviewer output-token ceiling (overrides configured `maxOutputTokens`); with neither the flag nor the config key set, there is no ceiling |
114
- | `--since <last\|run-id>` | Diff this run's findings against a previous one: `resolved` / `still-present` / `new` |
115
- | `--fail-on <level\|none>` | Severity threshold for exit code `1` (`critical`, `high`, `medium`, `low`, or `none` to disable) |
116
- | `--no-suppress` | Ignore `.council/ignore.json` for this run only; the file itself is untouched |
117
- | `--json` | Emit only the merged findings document on stdout; everything else (progress, diagnostics) goes to stderr |
118
-
119
- ## Environment (herdr) flags
120
-
121
- Only meaningful inside a [herdr](https://herdr.dev) pane (an agent multiplexer some users run
122
- coding agents under, detected via an environment marker). herdr is an optional integration —
123
- install it with `brew install herdr` if you want it — and every one of these flags is gated on
124
- that marker: outside that environment each degrades to exactly one printed notice, and the review
125
- itself proceeds and completes identically to how it would with none of these flags supplied — same
126
- artifacts, same exit code. A failing or unavailable herdr command is likewise reported as a warning
127
- only; it never changes the review's own outcome.
128
-
129
- | Flag | Meaning |
130
- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
131
- | `--pane` | Run the review in a new herdr pane (default direction: horizontal), current working directory carried over, focus left where it was |
132
- | `--direction <horizontal\|vertical>` | Direction for `--pane` |
133
- | `--no-pane` | Do not delegate to a new pane even if `--pane` is set |
134
- | `--handoff <agent>` | Deliver the written handoff prompt to a herdr-managed agent by name; delivery is not awaited, so the review command's own exit code reflects the review only |
135
- | `--no-notify` | Suppress the herdr completion notification (still raised, conveying completion, after a degraded run) |
136
-
137
- ## Subcommands
138
-
139
- - **`init [--pick]`** — bootstrap the current repository: run panel selection, write
140
- `.council/config.json` and `.council/ignore.json`, and ensure `.gitignore` excludes
141
- `.council/reviews/`. Safe to re-run: existing suppressions are preserved. Must be run inside a
142
- git repository.
143
- - **`models`** — list discovered, ready models. No prompt, no model call.
144
- - **`show [run-id]`** — render a stored run's `REPORT.md`. Defaults to the most recent run. No
145
- model call.
146
- - **`ignore <finding-id> [--reason <text>] [--run <run-id>]`** — resolve a finding id from a run's
147
- merged findings to its fingerprint and append it to `.council/ignore.json`. Idempotent.
148
- - **`gc [--keep <n>]`** — prune stored runs under `.council/reviews/`, keeping the `<n>` most
149
- recent (default: the configured `retain`, or 20). Never removes the run the `last` pointer
150
- resolves to. Also sweeps snapshot directories orphaned by a run that couldn't clean up after
151
- itself.
152
- - **`status [--json] [--verify]`** — report whether this project is configured, plus the resolved
153
- panel, independence-guard verdict, settings, suppression count, stored runs and
154
- gitignore/herdr state. This is the intended entry point for a coding agent to check in
155
- milliseconds whether it can run a review here at all, before doing anything else. Exits `0`
156
- when configured, `2` when not — including outside a git repository, or with an invalid
157
- `.council/config.json` (the offending key is named in the report) — and never anything else.
158
- The default form makes no host call and no model call: it reads only files already on disk
159
- (plus the single `git rev-parse` used to find the repository root). `--verify` additionally
160
- discovers the model catalog and reports each panel entry's real readiness and effective
161
- thinking level — a stale panel entry (a model no longer in the catalog) is reported as not
162
- ready rather than causing the command to fail. `--json` emits the full report as JSON on
163
- stdout instead of the human-readable form, e.g.:
164
- ```json
165
- {
166
- "version": "0.1.0",
167
- "configured": true,
168
- "repoRoot": "/path/to/project",
169
- "config": {
170
- "path": "/path/to/project/.council/config.json",
171
- "present": true,
172
- "valid": true,
173
- "version": 1,
174
- "error": null,
175
- "errorKeyPath": null
176
- },
177
- "panel": [
178
- {
179
- "provider": "anthropic",
180
- "model": "claude-opus-4",
181
- "vendor": "anthropic",
182
- "thinking": "high",
183
- "effectiveThinking": null,
184
- "clamped": null,
185
- "ready": null,
186
- "readyReason": null
187
- }
188
- ],
189
- "independence": {
190
- "ok": true,
191
- "modelCount": 3,
192
- "vendorCount": 3,
193
- "vendors": { "anthropic": ["anthropic/claude-opus-4"] },
194
- "reason": null
195
- },
196
- "settings": {
197
- "baseBranch": "main",
198
- "failOn": "high",
199
- "timeoutSeconds": 600,
200
- "maxOutputTokens": null,
201
- "mergeWindow": 10,
202
- "claimSimilarity": 0.6,
203
- "includeContextFiles": false,
204
- "retain": 20
205
- },
206
- "suppressions": {
207
- "path": "/path/to/project/.council/ignore.json",
208
- "present": true,
209
- "count": 4
210
- },
211
- "runs": {
212
- "count": 12,
213
- "last": {
214
- "id": "20260904T153012123Z",
215
- "path": "...",
216
- "reportPath": "...",
217
- "findingsPath": "..."
218
- }
219
- },
220
- "gitignore": { "excludesReviews": true },
221
- "herdr": { "detected": false },
222
- "verified": false
223
- }
224
- ```
225
-
226
- Run `council-review <subcommand> --help` for a subcommand's own usage, or `council-review --help`
227
- for the full flag surface.
52
+ A bare `council-review` reviews the whole divergence from the base branch committed, staged,
53
+ unstaged and untracked changes folded into one patch not just the index; narrow it with
54
+ `--staged`, `--range`, `--revision`, `--paths` or `--base`. A panel is admitted only with at least
55
+ three models resolving to at least three distinct vendors, checked when it is saved and again
56
+ before every launch, so a hand-edited config is checked too (`--allow-correlated` waives it). The
57
+ other subcommands are `models`, `status` (a millisecond, no-model check of whether a review can run
58
+ here at all the intended entry point for a coding agent), `show`, `ignore` and `gc`; run
59
+ `council-review --help` for the full flag surface, and see the design doc for every
60
+ `.council/config.json` key.
228
61
 
229
62
  ## Exit codes
230
63
 
231
- | Code | Meaning |
232
- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
233
- | `0` | Clean: nothing at or above the `failOn` threshold |
234
- | `1` | Findings at or above the `failOn` threshold |
235
- | `2` | Configuration or usage error |
236
- | `3` | Degraded: one or more reviewers timed out, exceeded budget, or failed validation a partial report was still written. **Outranks `1`**, so a CI job checking for "findings" never mistakes a partial panel for a clean measurement. |
237
- | `4` | The vendor-independence guard refused the panel; no reviewer was spawned |
238
- | `130` | Interrupted (SIGINT/SIGTERM) |
239
-
240
- ## What a run leaves behind
241
-
242
- Every run writes a timestamped directory under `.council/reviews/` (excluded from version control
243
- by an entry `init` adds to `.gitignore` — only the configuration in `.council/config.json` and
244
- `.council/ignore.json` is meant to be committed):
245
-
246
- ```
247
- .council/reviews/<run-id>/
248
- manifest.json reviewed state, resolved scope, panel + vendors + thinking levels,
249
- per-reviewer state/timings/usage/cost/depth, overrides in effect
250
- patch.diff the diff patch — never inside the snapshot
251
- findings.json the merged findings, as a JSON array
252
- REPORT.md human-readable report
253
- HANDOFF.md a prompt addressed to a coding agent, not a person
254
- reviewers/<slug>.findings.json | .text.md | .trace.jsonl
255
- .council/reviews/last -> <run-id> symlink to the most recent run
256
- ```
257
-
258
- `HANDOFF.md` instructs the consuming agent to reproduce each finding before fixing it, to check a
259
- merged finding against the reviewers' verbatim raw output when its claim is ambiguous, and frames
260
- a low-agreement finding as a hypothesis to test, not an established defect.
261
-
262
- ## The isolation model and its limits
263
-
264
- Three third-party models read your repository's source under this tool. That is a real trust
265
- boundary, and this section describes it plainly rather than reassuringly. See
266
- [`docs/design/council-review-design.md`](./docs/design/council-review-design.md)'s
267
- "Decisions" and "Risks / Trade-offs" sections for the full reasoning; this is the summary a user
268
- needs before relying on it.
269
-
270
- **What the isolation actually is**, layered so that no single mistake removes it:
271
-
272
- - Every host built-in tool is removed (`-nbt`) — specifically no shell, no file-write, no
273
- file-edit capability exists for a reviewer to call.
274
- - Resource discovery from the reviewed repository is disabled wholesale: no repo-supplied
275
- extensions, no skills, no prompt templates, and project trust state grants no extra capability
276
- — so a repository under review cannot get its own code or instructions loaded into the process
277
- reviewing it.
278
- - Agent context files (e.g. project-level agent instructions) are **excluded by default**, because
279
- the host loads them regardless of trust and they are therefore an instruction-injection surface.
280
- Opt in only via `includeContextFiles` in `.council/config.json`; the choice is recorded in every
281
- run's manifest.
282
- - A reviewer's entire tool surface is exactly four read-only tools — `council_read`,
283
- `council_grep`, `council_list`, `council_git` — loaded from one file this package ships, never
284
- installed into the host. Every path any of them is given is realpath-resolved and rejected if it
285
- escapes the snapshot root, including via a symlink or an intermediate symlinked directory.
286
- `council_git` reaches the _real_ repository, but only through a fixed allowlist of read-only
287
- subcommands (log, show, blame, diff) with structured arguments — never a composed command line.
288
- - Reviewers run against a **frozen snapshot**, not your live worktree: a plain copy into scratch,
289
- made non-writable (`chmod -R a-w`) before any reviewer launches, so every reviewer reads the same
290
- photograph and your ongoing edits in the meantime have no effect on the run.
291
- - Removing the shell removes a reviewer's only path to arbitrary network egress. Its only outbound
292
- traffic is the host's own model API call.
293
- - Host session persistence is disabled (`--no-session`), so a reviewer's transcript — which
294
- contains your repository's source — is never written into the host's own session store.
295
- - Each reviewer process gets an **allowlisted** environment, not a denylisted one: the executable
296
- search path, the home directory (so the host can find its own config/credentials), the two roots
297
- the reviewer tools need, and the host's own `PI_*` variables. Every other variable from the
298
- invoking environment is dropped, so no unrelated secret — including credentials for other
299
- services — ever reaches a reviewer process.
300
- - No code path in this package ever opens the host's credential store
301
- (`~/.pi/agent/auth.json`). No artifact this tool writes can contain a token, key or secret.
302
-
303
- **What it is not:**
304
-
305
- - **Not container or VM isolation.** There is no sandbox boundary beyond what's described above.
306
- This is a deliberate v1 choice (see the design doc's "Decisions"), not an oversight, and a stronger
307
- implementation can be added later without changing what this tool guarantees observably.
308
- - **`chmod -R a-w` on the snapshot is not a security boundary against a process running as the
309
- same user.** A same-user process can `chmod` its way back to writable. Freezing the tree exists
310
- for _consistency_ — so line numbers mean the same thing to every reviewer and to the merge step
311
- — not as the source of the read-only guarantee. That guarantee comes entirely from the absence
312
- of a write tool in the reviewer's callable surface.
313
- - **POSIX-only.** The freeze step and the path-containment logic assume POSIX permission and
314
- symlink semantics. This has not been tested, and is not expected to work, on a non-POSIX
315
- filesystem.
316
- - **Verified empirically against one pinned host version.** The whole guarantee rests on flags and
317
- an event-stream shape belonging to the `pi` binary, which this package does not control. See
318
- [`HOST-VERSION.md`](./HOST-VERSION.md) for exactly what was verified and how, and re-verify
319
- after any host upgrade before trusting the isolation guarantee against the new version.
320
- - **A provider whose API key exists only as a shell environment variable will not authenticate
321
- inside a reviewer.** The environment allowlist above is intentionally tight and does not pass
322
- through any `*_API_KEY`-shaped variable — this was a deliberate, verified decision (a scrubbed
323
- environment containing only `PATH`/`HOME` was confirmed to authenticate correctly for providers
324
- whose credentials live in the host's own store), not an oversight. A provider only ever
325
- authenticates inside a reviewer through the host's own credential store under `HOME`. **Remedy:**
326
- authenticate that provider through the host itself (e.g. `pi auth login`) so its credential is
327
- saved into the host's store, rather than relying on an environment variable your shell happens
328
- to export. If a provider's credential exists only as a shell variable and was never run through
329
- the host's own auth flow, reviewers using that provider will fail to authenticate — this is the
330
- environment allowlist working as designed, not a bug to route around by widening it.
331
- - **Third-party models read your repository's source.** That is the whole premise of this tool.
332
- Nothing above prevents a model provider from seeing your code as part of serving the review
333
- request; it only prevents that model, or the repository it's reviewing, from writing anything,
334
- executing anything, or reaching the network on its own.
335
-
336
- ### The `pi` host dependency
337
-
338
- `council-review` spawns the `pi` coding-agent CLI (npm package `@earendil-works/pi-coding-agent`)
339
- headlessly per reviewer rather than calling each vendor's API directly, so it can reuse the
340
- authentication, model catalog and agentic tool loop you already have configured for it. `pi`
341
- declares `engines: { node: ">=22.19.0" }`, so that version — not `council-review`'s own `>=20`
342
- floor — is what actually determines whether a review can run. See
343
- [`HOST-VERSION.md`](./HOST-VERSION.md) for the exact verified version and the re-verification
344
- procedure after a host upgrade.
345
-
346
- ## Configuration reference (`.council/config.json`)
347
-
348
- Written by `init`, hand-editable afterward. An unrecognised `version` is a hard error, so a future
349
- schema change has a defined migration point.
350
-
351
- | Key | Meaning |
352
- | ---------------------- | -------------------------------------------------------------------------------------------------- |
353
- | `version` | Config schema version |
354
- | `baseBranch` | Default base branch for scope resolution |
355
- | `panel` | Ordered list of `{ provider, model, thinking? }` |
356
- | `defaultThinkingLevel` | Fallback thinking level when nothing more specific applies |
357
- | `modelThinkingLevels` | Per-model thinking level, keyed `"provider/modelId"` — what the picker actually writes |
358
- | `includeContextFiles` | Opt into loading agent context files into reviewers (default: excluded) |
359
- | `timeoutSeconds` | Per-reviewer wall-clock timeout |
360
- | `maxOutputTokens` | Per-reviewer output-token ceiling (default: none — reviewers are bounded by `timeoutSeconds` only) |
361
- | `mergeWindow` | Line-proximity window for clustering findings |
362
- | `claimSimilarity` | Claim-token-similarity threshold for clustering |
363
- | `failOn` | Default severity threshold for exit code `1` (`critical`\|`high`\|`medium`\|`low`\|`none`) |
364
- | `snapshot.include` | Path globs narrowing what's copied into the snapshot (large repos) |
365
- | `vendorOverrides` | Exact `"provider/modelId"` → vendor overrides for vendor derivation |
366
- | `retain` | Default number of runs `gc` keeps |
367
-
368
- `.council/ignore.json` holds suppressed findings by fingerprint (stable across line movement —
369
- see the design doc's fingerprinting rationale), each optionally carrying a human-supplied `reason`.
370
- Managed through `council-review ignore`, not normally hand-edited.
64
+ | Code | Meaning |
65
+ | ----- | --------------------------------------------------------------------------- |
66
+ | `0` | Clean: nothing at or above the `failOn` threshold |
67
+ | `1` | Findings at or above the threshold |
68
+ | `2` | Configuration or usage error |
69
+ | `3` | Degraded: a reviewer failed and a partial report was written. Outranks `1`. |
70
+ | `4` | The vendor-independence guard refused the panel; nothing was spawned |
71
+ | `130` | Interrupted (SIGINT/SIGTERM) |
371
72
 
372
73
  ## License
373
74
 
package/dist/bin.js CHANGED
File without changes
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { type PickerIO } from './picker.js';
2
+ /** The task instructions every reviewer receives. Exported (rather than kept module-private)
3
+ * so the live smoke test sends exactly what production sends instead of maintaining a copy
4
+ * that drifts the next time this paragraph changes. */
5
+ export declare const TASK_PROMPT = "You are one independent reviewer in a multi-model code review panel. You are\ngiven a unified diff patch and read-only access to a frozen snapshot of the repository at the\ntree the patch ends at, via your council_read, council_grep, council_list, council_git and\ncouncil_codegraph tools.\n\nStart with council_codegraph before plain-text search: explore the symbols the patch touches,\nthen trace their callers, callees, and transitive impact to find affected code that shares no\ntext with the diff. Use council_grep and council_read for what the index cannot answer, or for\neverything when council_codegraph reports its index is unavailable for this run.\n\nReview the patch for correctness bugs, security issues, and other defects a careful senior\nengineer would flag before merging: logic errors, unhandled edge cases, resource leaks, race\nconditions, broken error handling, security vulnerabilities, and violations of the codebase's own\nestablished conventions. Do not comment on style preferences and do not report merely cosmetic\nissues.\n\nYou do not know which other models, if any, are also reviewing this patch, and you will never see\ntheir output or the fact that they exist. Form your own independent judgment using only what you\ncan read yourself.";
2
6
  /** Dependencies `runCli` accepts beyond argv. `pickerIO` threads through to every call this
3
7
  * invocation makes to `pickPanel` (`init`, `init --pick`, and a review run's own `--pick`);
4
8
  * omitting it (the production path) leaves `pickPanel` on its own default, real-stdio behaviour
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAu6BvD;;;;2CAI2C;AAC3C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;sBAEsB;AACtB,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzF"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsCA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAoVvD;;wDAEwD;AACxD,eAAO,MAAM,WAAW,2vCAkBL,CAAC;AA2kBpB;;;;2CAI2C;AAC3C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;sBAEsB;AACtB,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzF"}
package/dist/cli.js CHANGED
@@ -14,7 +14,7 @@ import fs from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { isThinkingLevel } from './levels.js';
17
- import { ConfigError, CONFIG_DEFAULTS, configPath, ignorePath, loadConfig, writeConfig, loadIgnore, appendIgnore, } from './config.js';
17
+ import { ConfigError, CONFIG_DEFAULTS, CODEGRAPH_DEFAULTS, configPath, ignorePath, loadConfig, writeConfig, loadIgnore, appendIgnore, } from './config.js';
18
18
  import { loadCatalog, readyModels } from './providers.js';
19
19
  import { supportedLevels, formatClampNotice } from './thinking.js';
20
20
  import { resolveConfiguredPanel, resolveSpecPanel, enforceIndependence, } from './panel.js';
@@ -235,8 +235,9 @@ function ensureIgnoreFile(repoRoot) {
235
235
  }
236
236
  const GITIGNORE_ENTRY = '.council/reviews/';
237
237
  /** The write half of the gitignore check; `gitignoreExcludesReviews` (status.ts) is the read
238
- * half -- both match the same two accepted forms, so a project `status` reports as already
239
- * excluded is never one `init` would append a duplicate entry to, or vice versa. */
238
+ * half -- both go through the same matcher (exact entry, root-anchored variant, or a parent
239
+ * `.council/` entry), so a project `status` reports as already excluded is never one `init`
240
+ * would append a redundant entry to, or vice versa. */
240
241
  function ensureGitignoreEntry(repoRoot) {
241
242
  if (gitignoreExcludesReviews(repoRoot))
242
243
  return;
@@ -309,9 +310,18 @@ async function withStdoutRedirectedToStderr(active, fn) {
309
310
  process.stdout.write = original;
310
311
  }
311
312
  }
312
- const TASK_PROMPT = `You are one independent reviewer in a multi-model code review panel. You are
313
+ /** The task instructions every reviewer receives. Exported (rather than kept module-private)
314
+ * so the live smoke test sends exactly what production sends instead of maintaining a copy
315
+ * that drifts the next time this paragraph changes. */
316
+ export const TASK_PROMPT = `You are one independent reviewer in a multi-model code review panel. You are
313
317
  given a unified diff patch and read-only access to a frozen snapshot of the repository at the
314
- tree the patch ends at, via your council_read, council_grep, council_list and council_git tools.
318
+ tree the patch ends at, via your council_read, council_grep, council_list, council_git and
319
+ council_codegraph tools.
320
+
321
+ Start with council_codegraph before plain-text search: explore the symbols the patch touches,
322
+ then trace their callers, callees, and transitive impact to find affected code that shares no
323
+ text with the diff. Use council_grep and council_read for what the index cannot answer, or for
324
+ everything when council_codegraph reports its index is unavailable for this run.
315
325
 
316
326
  Review the patch for correctness bugs, security issues, and other defects a careful senior
317
327
  engineer would flag before merging: logic errors, unhandled edge cases, resource leaks, race
@@ -595,6 +605,7 @@ async function cmdReview(args, pickerIO) {
595
605
  claimSimilarity: CONFIG_DEFAULTS.claimSimilarity,
596
606
  failOn: CONFIG_DEFAULTS.failOn,
597
607
  retain: CONFIG_DEFAULTS.retain,
608
+ codegraph: { ...CODEGRAPH_DEFAULTS },
598
609
  };
599
610
  }
600
611
  else {
@@ -659,6 +670,7 @@ async function cmdReview(args, pickerIO) {
659
670
  snapshot = await buildSnapshot(repoRoot, scope, {
660
671
  include: cfg.snapshot?.include,
661
672
  handleSignals: false,
673
+ codegraph: cfg.codegraph,
662
674
  });
663
675
  const timeoutSeconds = values.timeout !== undefined
664
676
  ? parsePositiveInt('--timeout', values.timeout)
@@ -721,6 +733,7 @@ async function cmdReview(args, pickerIO) {
721
733
  suppressed: mergeOutcome.suppressed,
722
734
  overrides: { allowCorrelated, includeContextFiles: cfg.includeContextFiles, noSuppress },
723
735
  hostVersion: null,
736
+ codegraph: activeSnapshot.codegraph,
724
737
  };
725
738
  writeManifest(manifestInput);
726
739
  const reportPath = writeReport(run, manifestInput, mergeOutcome.findings, resolution);