amicus 4.5.1 → 4.5.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amicus",
3
- "version": "4.5.1",
3
+ "version": "4.5.3",
4
4
  "description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
5
5
  "author": {
6
6
  "name": "Christian Wagner"
package/CHANGELOG.md CHANGED
@@ -3,6 +3,81 @@
3
3
  All notable changes to Amicus are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semver.
5
5
 
6
+ ## [4.5.3] - 2026-08-01
7
+
8
+ ### Fixed
9
+
10
+ - **Unknown CLI flags are rejected instead of silently absorbed.** `parseArgs` treated any
11
+ `--token` as a flag: an unrecognized one landed on the parsed args object, no handler read it,
12
+ and the command ran as though it were never typed. Found while smoke-testing v4.5.2 —
13
+ `amicus start -m deepseek --prompt "…" --headless` printed no error and exited 0, but `start`
14
+ has no `--headless`; the run silently took the **interactive** path, ignored `-m`, and left a
15
+ session running against the default model. A typo (`--modl`), a flag borrowed from another
16
+ command, or an invented one all behaved the same way, and an unknown flag followed by a
17
+ positional would swallow it as its value. Unknown flags now get the same treatment amicus
18
+ already gave an unknown *command*: name it, suggest the nearest real flag, point at `--help`,
19
+ exit 1. The known-flag set is **derived from the usage text** (the same source `getCommandNames()`
20
+ uses, for the same anti-rot reason) plus the boolean-flag list, plus a small explicitly-documented
21
+ allowlist of internal MCP→CLI passthroughs (`--task-id`, `--run-id`, `--council-name`,
22
+ `--cowork-process`) and undocumented-but-working flags (`--briefing`, `--mode`, `--quiet`).
23
+ A regression test asserts every `args.<flag>` any CLI handler reads is in the known set, so the
24
+ check can never silently start rejecting a flag that works.
25
+
26
+ ### Changed
27
+
28
+ - **README Quick start is now four numbered steps**, with **Configure** promoted from a bold line
29
+ buried between two callouts to its own step and TOC entry. Installing without configuring is the
30
+ step people skip, and every council fails at the first model call when they do — so it now says
31
+ so plainly, notes that one OpenRouter key is enough to start, and ends with `amicus doctor` as
32
+ the confirmation that setup actually took.
33
+
34
+ ## [4.5.2] - 2026-07-31
35
+
36
+ ### Fixed
37
+
38
+ - **OpenCode server-start timeout is no longer pinned to the SDK's 5000 ms, and a start timeout
39
+ is now retried.** `@opencode-ai/sdk` defaults `createOpencodeServer`'s start timeout to 5 s and
40
+ lets the caller override it; amicus never passed one, so every start on every platform ran on
41
+ that default — undocumented, untunable, and invisible to `amicus doctor`. Worse, the existing
42
+ start retry (`retryOnLockRace`) classified only `database is locked` / `SQLITE_BUSY`, so a start
43
+ timeout fell straight through with **zero** retries, past machinery already wired in at every
44
+ call site. On a Windows box with the project on a OneDrive-synced volume and Defender active, a
45
+ cold OpenCode/SQLite start blew the window: the council's shared server failed to acquire, the
46
+ run degraded to exactly the per-wave configuration `src/council/run-server.js` exists to
47
+ eliminate, and the whole Stage-1 bench died with `COUNCIL_QUORUM: Only 0 Stage-1 review(s)
48
+ survived`. Three of the reporter's runs were lost this way. A start timeout is now classified as
49
+ transient (`isTimeoutClassStartFailure`) and retried on the same bounded 250/500/1000/2000 ms
50
+ schedule, the timeout is threaded through `buildServerOptions` and both upstream start sites,
51
+ and the default is raised to **30 s on Windows / 15 s elsewhere** — a slow start costs latency,
52
+ a failed start costs a review seat.
53
+ - **The Electron self-heal was dead code in every published install.** `src/sidecar/unzip.js`
54
+ did a bare, unguarded `require('extract-zip')` for a package that was never declared in
55
+ `dependencies` or `optionalDependencies`. It resolved in the dev tree only because `puppeteer`
56
+ (a devDependency) pulls it transitively — `npm ls extract-zip --omit=dev` returned empty — so on
57
+ a real `npm i -g amicus` `robustExtract` threw `MODULE_NOT_FOUND` before Strategy 1. That made
58
+ the native-unzip fallback below it unreachable, the bounded idle/max timers from the
59
+ extract-zip-node24 work inert, and `amicus doctor --fix` dead-end at `self-heal incomplete` —
60
+ while routing users toward antivirus allow-listing for what was actually a missing module.
61
+ `extract-zip` is now a declared production dependency, the `require` degrades into the native
62
+ strategies instead of throwing out of the function, and a new `no-phantom-dependencies` test
63
+ fails on any undeclared runtime require anywhere in `src/`, `bin/` or `electron/`.
64
+ - **A lost critic is now recorded on `verdict.json`.** The critic is a solo wave with one leg, and
65
+ unlike a dead bench wave (which trips the quorum gate and fails the run loudly) a dead critic is
66
+ survivable — so a run could reach a full verdict, tally and chair synthesis that had never seen
67
+ the adversarial seat, with the only record being `deadWaves` in `run.json`. Field run `dfb6a692`
68
+ did exactly that. `verdict.json` now carries an optional `seatLoss` block
69
+ (`criticRequested`/`criticSeated`/`reason`/`deadBenchSeats`) whenever `--critic` was requested,
70
+ so a reader of the verdict can see the critic never ran. Additive; `schemaVersion` stays `2`.
71
+
72
+ ### Added
73
+
74
+ - **`AMICUS_SERVER_START_TIMEOUT_MS`** — override the server-start window (see
75
+ [docs/configuration.md](docs/configuration.md#server-startup)). Values ≤ 0 are ignored rather
76
+ than honored, since a zero start timeout fails every start instantly.
77
+ - **Successful server starts are logged at debug level** with both `startMs` and the `timeoutMs`
78
+ ceiling in force, so headroom on a slow box is measurable rather than inferred — the question
79
+ the field report could not answer.
80
+
6
81
  ## [4.5.1] - 2026-07-30
7
82
 
8
83
  ### Added
package/README.md CHANGED
@@ -15,6 +15,8 @@ Hand Claude a plan, a design, a diff, an architecture decision, a manuscript —
15
15
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen?labelColor=1A1C29)](https://nodejs.org)
16
16
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?labelColor=1A1C29)](./CONTRIBUTING.md)
17
17
 
18
+ **[Quick start ↓](#quick-start)** · [Commands](#commands) · [Documentation](#documentation) · [Troubleshooting](#troubleshooting)
19
+
18
20
  > **Supported clients:** Claude Code CLI, Claude Desktop, and Claude Cowork are fully tested and supported. Claude Code web is experimental.
19
21
 
20
22
  </div>
@@ -24,10 +26,14 @@ Hand Claude a plan, a design, a diff, an architecture decision, a manuscript —
24
26
  ## Table of Contents
25
27
 
26
28
  - [What is Amicus](#what-is-amicus)
29
+ - [The Council](#the-council)
27
30
  - [Ways to run the council](#ways-to-run-the-council)
28
31
  - [Quick start](#quick-start)
32
+ - [1. Install](#1-install)
33
+ - [**2. Configure — don't skip this**](#2-configure--dont-skip-this)
34
+ - [3. Your first council](#3-your-first-council)
35
+ - [4. Your first sidecar](#4-your-first-sidecar)
29
36
  - [Requirements & Dependencies](#requirements--dependencies)
30
- - [The Council](#the-council)
31
37
  - [The parallel window](#the-parallel-window)
32
38
  - [Commands](#commands)
33
39
  - [Models](#models)
@@ -52,7 +58,7 @@ One install delivers six things that work together:
52
58
  - **The `amicus` CLI (with an `am` alias) and an MCP server.** The engine underneath both skills: launches sessions, shares context, runs parallel waves, and exposes the same surface to Claude as MCP tools.
53
59
  - **A self-updating model catalog.** Aliases and validation resolve against a live catalog fetched from provider APIs (cached locally), so model names stay current without a hard-coded table.
54
60
  - **Observability.** `amicus watch <id>` renders any live or finished run (fan-out or council) from any terminal; `--follow` streams milestones as they happen; `--on-complete` fires a hook when a run lands; `--retry-failed` plus opt-in cheaper-model fallbacks recover dead legs without relaunching the whole wave; `amicus spend` answers "what did this cost, and where" with per-run attribution.
55
- - **Council Workspace.** `amicus watch <runId> --ui`: a window that shows a council *thinking* — live seats, the anonymized judge packet, the adjudication matrix, dissent drill-in, chair verdict, and cost-by-seat — for both live and historical runs. It also **auto-opens** on an MCP-invoked council run from Claude Code (local), so you no longer have to remember the flag (see [The Council](#the-council)).
61
+ - **Council Workspace.** `amicus watch <runId> --ui`: a window that shows a council *thinking* — live seats, the anonymized judge packet, the adjudication matrix, dissent drill-in, chair verdict, and cost-by-seat — for both live and historical runs. It also **auto-opens** on an MCP-invoked council run from Claude Code (local), so you no longer have to remember the flag (see [docs/council.md](./docs/council.md#council-workspace-gui)).
56
62
 
57
63
  Claude is the orchestrator. The council and chat skills run *on top of* the engine; you talk to Claude, and Claude drives Amicus.
58
64
 
@@ -60,15 +66,103 @@ Claude is the orchestrator. The council and chat skills run *on top of* the engi
60
66
 
61
67
  ---
62
68
 
69
+ ## The Council
70
+
71
+ > Trigger it by saying *"council review this"* to Claude, or, on the plugin channel, run **`/amicus:council`** directly.
72
+
73
+ **Why multi-model.** Any single model — including the one running your session — has consistent blind spots. Route the *same* material through models from *different* families and the disagreements surface: missed issues, overstated confidence, claims one model alone would have waved through. The council is the structured version of that idea.
74
+
75
+ **The flow, in five beats:**
76
+
77
+ 1. **Independent reviews.** Each council model reviews the artifact on its own (one parallel wave), producing a structured findings list — claim, severity (`blocker | major | minor | nit`), location, rationale.
78
+ 2. **Anonymized cross-review.** Claude relabels every review (Review A, B, C…) and sends the identical bundle to every model. Each model ranks the reviews and adjudicates every finding (`agree | dispute | neutral`) — *unknowingly judging its own*, so self-bias washes out. This yields a **street-cred** ranking and sorts findings into **Disputed / Confirmed / Contested / Singleton** tiers.
79
+ 3. **Chair verdict.** A designated **non-Claude** chair receives the de-anonymized picture — all reviews, rankings, and adjudications — and synthesizes an independent verdict. Claude presents it verbatim; Claude does not synthesize.
80
+ 4. **Tiered decisions.** Confirmed findings get one bulk accept/deny; Contested and Singleton findings are decided one at a time (accept / deny / modify).
81
+ 5. **Outputs applied.** Accepted findings are written into a reviewed copy of the source; the full run is captured in the run folder.
82
+
83
+ ```mermaid
84
+ flowchart LR
85
+ A["Artifact"] --> B["Independent<br/>reviews"]
86
+ B --> C["Anonymized<br/>cross-review"]
87
+ C --> D["Chair verdict<br/>(non-Claude)"]
88
+ D --> E["Tiered<br/>accept / deny"]
89
+ E --> F["Reviewed copy<br/>+ run folder"]
90
+ ```
91
+
92
+ **What a run produces** (in `output/<stem>-council/`):
93
+
94
+ - `review-<model>.md` × N — each model's independent review.
95
+ - `crossreview-matrix.md` — the adjudication grid plus the de-anonymized street-cred table.
96
+ - `verdict.md` — the chair's synthesis.
97
+ - `report.md` — synthesis + the full decision log + a per-call run-stats table.
98
+ - `report.html` — the deterministic renderer output (adjudication matrix, street-cred table,
99
+ findings-by-tier, cost — no chair prose). This is the default artifact handed to the user.
100
+ - For an **editable source**, the accepted edits land in `<stem>-reviewed.<ext>` next to the original.
101
+
102
+ **Optional council elements** (v2.2.0, all default off): five opt-in behaviors, offered once as a menu at launch — nothing turns on unless you name it, and the confirmation lists exactly what's on.
103
+
104
+ - **Critic seat** — one reviewer swaps to a four-pass adversarial brief (adversarial pass, edge-case hunt, consistency check, executability test). Its findings enter the same anonymized bundle as everyone else's, so the bench disciplines the critic: manufactured negativity lands Disputed and dies in the tally.
105
+ - **Expert lenses** — each reviewer takes a distinct expert perspective; you pick the panel domain (business, technical, customer, financial, or custom). Lens runs never feed the reliability ledger, and the report discloses the weakened cross-review anonymity.
106
+ - **Debate mode** — after cross-review, every Contested or Disputed finding goes back to its raiser to **defend, amend, or withdraw**, and the disputing judges re-vote. Exactly one rebuttal round, then the final tally.
107
+ - **Chair verdict scale** — the chair closes with 3–5 hard questions and one parseable line: `VERDICT: Ship it | Fix these first | Fundamental rethink`.
108
+ - **Claude in the council** — Claude adds its own fresh review to the bundle so the bench ranks and adjudicates it. Claude is *judged* but never votes or chairs, so the verdict stays independent.
109
+
110
+ The critic and lens methodologies are adapted from the `/critic` and `/debate` agents in [John Renaldi's product-kit](https://github.com/jrenaldi79/plugin-marketplace) (MIT); the briefing boilerplate lives in [`skills/second-opinion/SEAT-BRIEFS.md`](./skills/second-opinion/SEAT-BRIEFS.md).
111
+
112
+ **Cost is disclosed up front.** Before any model launches, you see the run shape — including any enabled optional elements — for example:
113
+
114
+ > This run uses 3 council models across 2 fanout waves + 1 chair call, with critic seat + debate mode ON (~7 base runs + up to 6 rebuttal calls).
115
+
116
+ Then the council waits for your confirmation.
117
+
118
+ The skill lives at **[`skills/second-opinion/SKILL.md`](./skills/second-opinion/SKILL.md)**; the design spec behind it is **[`skills/second-opinion/COUNCIL-DESIGN.md`](./skills/second-opinion/COUNCIL-DESIGN.md)**. For what `amicus council tally|verdict|report|stats` actually take as input and produce — field-by-field schemas, verdict.json's provenance, and a full worked example run against the real CLI — see **[docs/council.md](./docs/council.md)**.
119
+
120
+ ---
121
+
63
122
  ## Ways to run the council
64
123
 
65
124
  The council is the hero — start with the everyday way, and reach for the more powerful ways when you need them:
66
125
 
67
- - **Just ask, in Claude Code.** Hand Claude a plan, diff, design, or manuscript and say *"council review this."* The `second-opinion` skill runs the whole ritual in your session — several models review independently → anonymized cross-review → a non-Claude chair verdict → tiered accept/deny edits — with no setup beyond your API keys. This is how most people use it. → [The Council](#the-council)
68
- - **Headless, in CI, with no Claude runtime.** `amicus council run --prompt-file plan.md --council free` runs that same pipeline in one command — reviews → cross-review → tally → chair verdict — writing `verdict.json` and `report.html`. It needs no Claude session, so it drops straight into CI. → [Headless council](./docs/council.md#amicus-council-run)
126
+ - **Just ask, in Claude Code.** Hand Claude a plan, diff, design, or manuscript and say *"council review this."* The `second-opinion` skill runs the whole ritual above in your session, with no setup beyond your API keys. This is how most people use it. → [Quick start](#quick-start)
127
+ - **Headless, in CI, with no Claude runtime.** `amicus council run --prompt-file plan.md --council free` runs that same pipeline in one command — reviews → cross-review → tally → chair verdict — writing `verdict.json` and `report.html`. It needs no Claude session, so it drops straight into CI. → [Headless council (CI)](#headless-council-ci)
69
128
  - **With a debate round.** Add `--debate` and every Contested or Disputed finding goes back to its raiser to **defend, amend, or withdraw** while the disputing judges re-vote — exactly one rebuttal round, then the final tally. → [The Council](#the-council)
70
129
  - **On free, local, private models — at $0.** Point the council (and sidecars) at an OpenAI-compatible server already running on your machine — Ollama, LM Studio, or vLLM — with `amicus provider add`. No API key, no per-token bill, nothing leaves your machine, and it works offline. → [`amicus provider`](./docs/usage.md#amicus-provider)
71
130
 
131
+ ### Headless council (CI)
132
+
133
+ The same pipeline runs with no Claude runtime at all: `amicus council run --prompt-file briefing.md --models gemini,glm --chair deepseek --json` executes the review waves, the anonymized cross-review, the tally, and the chair verdict in one command, and writes the full run directory (`verdict.json` with the chair's parsed `overallVerdict`, `report.html`, every review and judge output). That is what powers the repo's own **Council Review GitHub Action v2** — on PRs labeled `council-review` it posts an adjudicated verdict as a check run plus a sticky comment, uploads the run directory as an evidence artifact, and can optionally gate merges via its `fail_on` input (default: report-only). Reference: [docs/council.md](./docs/council.md#amicus-council-run).
134
+
135
+ ### Free council (zero-cost)
136
+
137
+ Want the cross-examination without the model spend? `amicus setup` offers a **Free OpenRouter council** mode — readline wizard option 2, and the Electron **Models** step. It detects the free `:free` models live from the catalog, lets you multi-pick (Enter takes a vendor-diverse default), and saves them as `councils.free` — a first-class `councils` config primitive seeded under collision-safe `free-*` aliases. Your `config.default` is left untouched, and all you need is an `OPENROUTER_API_KEY`.
138
+
139
+ Run it anywhere a council runs:
140
+
141
+ ```bash
142
+ amicus fanout --council free --prompt "Review this design"
143
+ ```
144
+
145
+ The `amicus_fanout` MCP tool takes the same `council` parameter, and the `second-opinion` skill reads `councils.free` automatically. A member that gets delisted is dropped with a warning — the council still runs as long as ≥2 survive. Free models are **rate-limited and quality-variable**, and some return 404 unless you enable data-sharing at [openrouter.ai/settings/privacy](https://openrouter.ai/settings/privacy).
146
+
147
+ ### Council presets
148
+
149
+ Save your own named member lists with `amicus council save <name> --models a,b,c` (≥2 resolvable aliases or `provider/model` IDs), then run them with `--council <name>` anywhere a council runs. `amicus council list` shows saved presets plus three built-in benches that work with no setup at all — `free` (the same zero-cost dynamic pick described above, used when you haven't seeded `councils.free`), `budget` (cheap workhorses, one per vendor family), and `frontier` (premium flagships, one per vendor family). `amicus council show <name>` resolves any of them (saved or built-in) and reports which members are currently usable. A saved council always shadows a built-in of the same name — exactly how the wizard's `councils.free` seeding already worked.
150
+
151
+ ### Policy packs (v4.5)
152
+
153
+ A council preset only saves the bench. A **pack** saves the whole run — bench, chair, critic/lenses, cost/timeout options, and a briefing template — as one named, shareable JSON file:
154
+
155
+ ```bash
156
+ amicus pack save review-bench --kind council --bench gemini,deepseek,gpt --chair opus --timeout 20 --max-cost 2
157
+ amicus council run --pack review-bench --prompt-file plan.md --json
158
+ ```
159
+
160
+ Any flag you also type on that second line overrides just that value — a pack only fills in what you didn't say explicitly, and it's recorded on the run either way. Packs work the same way on `fanout`/`start` and on the `amicus_fanout`/`amicus_start`/`amicus_council_run` MCP tools. `amicus pack list`/`show`/`rm` manage them, and `--from-run <id>` builds one from a run you already liked instead of typing flags at all. Full reference: [docs/usage.md § Policy packs](./docs/usage.md#policy-packs).
161
+
162
+ ### Briefing templates (v4.5)
163
+
164
+ `--template <name> --artifact <file>` (plus repeatable `--var k=v`) renders a `{{prompt}}`/`{{artifact}}`-style Markdown template before it's sent, on `start`/`fanout`/`council run` alike — templates live in `~/.config/amicus/templates/`, and a pack's `briefing.template` is how one reaches an MCP-invoked run (MCP has no template param of its own). `amicus template list|show` manage them; v4.5 ships one built-in, `review`. Full reference: [docs/usage.md § Briefing templates](./docs/usage.md#briefing-templates).
165
+
72
166
  ---
73
167
 
74
168
  ## Quick start
@@ -80,7 +174,9 @@ The council is the hero — start with the everyday way, and reach for the more
80
174
  >
81
175
  > **Convention used throughout this README:** plugin-channel users: prefix CLI examples with `npx -y amicus@latest` (skip the bare `amicus`/`am`). Individual code blocks are not duplicated per channel — this note is the one translation you need.
82
176
 
83
- **Install** — pick whichever fits. Every path delivers the MCP server and both skills; the `amicus`/`am` CLI lands on your PATH with the **npm and install-script paths** (the plugin path runs the CLI on demand via `npx -y amicus@latest <command>`):
177
+ ### 1. Install
178
+
179
+ Pick whichever fits. Every path delivers the MCP server and both skills; the `amicus`/`am` CLI lands on your PATH with the **npm and install-script paths** (the plugin path runs the CLI on demand via `npx -y amicus@latest <command>`):
84
180
 
85
181
  **As a Claude Code plugin** — the most native path if you use Claude Code:
86
182
 
@@ -117,7 +213,15 @@ For the **npm** and **install-script** paths, a postinstall auto-configures ever
117
213
 
118
214
  > **Skipped the postinstall?** `--ignore-scripts` npm installs never run it, and the plugin channel skips it by design (Claude Code registers the plugin's MCP server and skills itself). Either way, run `amicus init` (plugin channel: `npx -y amicus@latest init`) any time to (re)register on demand — e.g. to also wire up Claude Desktop, which the plugin path doesn't touch. See [`amicus init`](./docs/usage.md#amicus-init).
119
215
 
120
- **Configure:**
216
+ ---
217
+
218
+ ### 2. Configure — don't skip this
219
+
220
+ > ### ⚠️ Installing is not enough. Run this or nothing will work.
221
+ >
222
+ > Amicus has **no API keys of its own** — it drives *your* accounts at OpenRouter, Google,
223
+ > OpenAI, Anthropic, or DeepSeek. Until you add at least one key, every council and every
224
+ > sidecar fails at the first model call. **This is the step people skip.**
121
225
 
122
226
  ```bash
123
227
  amicus setup
@@ -125,6 +229,10 @@ amicus setup
125
229
  npx -y amicus@latest setup
126
230
  ```
127
231
 
232
+ **One key is enough to start.** [OpenRouter](https://openrouter.ai/keys) is the usual choice —
233
+ a single key reaches every model in the catalog, which is what makes a mixed-vendor council
234
+ work without four separate accounts.
235
+
128
236
  This opens a graphical wizard:
129
237
 
130
238
  | Step | What it does |
@@ -136,7 +244,20 @@ This opens a graphical wizard:
136
244
 
137
245
  > **Headless environments:** if Electron can't open a window, the wizard falls back to a readline-based setup in the terminal.
138
246
 
139
- **Your first council** — no flags to learn. In Claude Code or Cowork, give Claude a document and say:
247
+ **Confirm it took** — this is the fastest way to know you're actually ready:
248
+
249
+ ```bash
250
+ amicus doctor
251
+ ```
252
+
253
+ Green across the board (`0 error(s)`) means keys, catalog, engine, skills, and MCP registration
254
+ are all in place. Anything red, `amicus doctor` tells you exactly what to run next.
255
+
256
+ ---
257
+
258
+ ### 3. Your first council
259
+
260
+ No flags to learn. In Claude Code or Cowork, give Claude a document and say:
140
261
 
141
262
  > *council review this*
142
263
 
@@ -144,7 +265,9 @@ Plugin-channel users can also type **`/amicus:council`** directly instead of phr
144
265
 
145
266
  Claude prepares the material, recommends a bench of models, discloses the run shape and cost, and orchestrates the rest. You make the accept/deny calls at the end. (The `second-opinion` skill is what teaches Claude to recognize this — if nothing happens, run `amicus doctor` (or `npx -y amicus@latest doctor`). npm/install-script installs place the skill at `~/.claude/skills/second-opinion/`; plugin installs keep it inside the plugin itself — check `/plugin` in Claude Code to confirm amicus is enabled.)
146
267
 
147
- **Your first sidecar.** The sidecar is the lower-level path — you can invoke it by phrase through Claude too, but the CLI gives you the flags directly:
268
+ ### 4. Your first sidecar
269
+
270
+ The sidecar is the lower-level path — you can invoke it by phrase through Claude too, but the CLI gives you the flags directly:
148
271
 
149
272
  ```bash
150
273
  amicus start --model gemini --prompt "Fact-check the auth approach Claude just proposed"
@@ -152,6 +275,14 @@ amicus start --model gemini --prompt "Fact-check the auth approach Claude just p
152
275
 
153
276
  A window opens alongside your editor with Gemini ready, pre-loaded with your conversation. Work with it, then **Fold** the summary back.
154
277
 
278
+ ### Updating
279
+
280
+ Amicus checks the npm registry at most once every 24 hours (cached background check). When an update exists, the CLI prints a notice and the Electron toolbar shows a one-click **Update** banner. Or run it yourself:
281
+
282
+ ```bash
283
+ amicus update
284
+ ```
285
+
155
286
  ### Install from GitHub
156
287
 
157
288
  The npm package is the primary path. To install straight from the repo instead — the postinstall runs **identically** (same MCP registration, same two skills) — you just need `git` on your `PATH`:
@@ -204,82 +335,6 @@ Everything you need before your first run, and what's optional.
204
335
 
205
336
  ---
206
337
 
207
- ## The Council
208
-
209
- > Trigger it by saying *"council review this"* to Claude, or, on the plugin channel, run **`/amicus:council`** directly.
210
-
211
- **Why multi-model.** Any single model — including the one running your session — has consistent blind spots. Route the *same* material through models from *different* families and the disagreements surface: missed issues, overstated confidence, claims one model alone would have waved through. The council is the structured version of that idea.
212
-
213
- **The flow, in five beats:**
214
-
215
- 1. **Independent reviews.** Each council model reviews the artifact on its own (one parallel wave), producing a structured findings list — claim, severity (`blocker | major | minor | nit`), location, rationale.
216
- 2. **Anonymized cross-review.** Claude relabels every review (Review A, B, C…) and sends the identical bundle to every model. Each model ranks the reviews and adjudicates every finding (`agree | dispute | neutral`) — *unknowingly judging its own*, so self-bias washes out. This yields a **street-cred** ranking and sorts findings into **Disputed / Confirmed / Contested / Singleton** tiers.
217
- 3. **Chair verdict.** A designated **non-Claude** chair receives the de-anonymized picture — all reviews, rankings, and adjudications — and synthesizes an independent verdict. Claude presents it verbatim; Claude does not synthesize.
218
- 4. **Tiered decisions.** Confirmed findings get one bulk accept/deny; Contested and Singleton findings are decided one at a time (accept / deny / modify).
219
- 5. **Outputs applied.** Accepted findings are written into a reviewed copy of the source; the full run is captured in the run folder.
220
-
221
- **What a run produces** (in `output/<stem>-council/`):
222
-
223
- - `review-<model>.md` × N — each model's independent review.
224
- - `crossreview-matrix.md` — the adjudication grid plus the de-anonymized street-cred table.
225
- - `verdict.md` — the chair's synthesis.
226
- - `report.md` — synthesis + the full decision log + a per-call run-stats table.
227
- - `report.html` — the deterministic renderer output (adjudication matrix, street-cred table,
228
- findings-by-tier, cost — no chair prose). This is the default artifact handed to the user.
229
- - For an **editable source**, the accepted edits land in `<stem>-reviewed.<ext>` next to the original.
230
-
231
- **Optional council elements** (v2.2.0, all default off): five opt-in behaviors, offered once as a menu at launch — nothing turns on unless you name it, and the confirmation lists exactly what's on.
232
-
233
- - **Critic seat** — one reviewer swaps to a four-pass adversarial brief (adversarial pass, edge-case hunt, consistency check, executability test). Its findings enter the same anonymized bundle as everyone else's, so the bench disciplines the critic: manufactured negativity lands Disputed and dies in the tally.
234
- - **Expert lenses** — each reviewer takes a distinct expert perspective; you pick the panel domain (business, technical, customer, financial, or custom). Lens runs never feed the reliability ledger, and the report discloses the weakened cross-review anonymity.
235
- - **Debate mode** — after cross-review, every Contested or Disputed finding goes back to its raiser to **defend, amend, or withdraw**, and the disputing judges re-vote. Exactly one rebuttal round, then the final tally.
236
- - **Chair verdict scale** — the chair closes with 3–5 hard questions and one parseable line: `VERDICT: Ship it | Fix these first | Fundamental rethink`.
237
- - **Claude in the council** — Claude adds its own fresh review to the bundle so the bench ranks and adjudicates it. Claude is *judged* but never votes or chairs, so the verdict stays independent.
238
-
239
- The critic and lens methodologies are adapted from the `/critic` and `/debate` agents in [John Renaldi's product-kit](https://github.com/jrenaldi79/plugin-marketplace) (MIT); the briefing boilerplate lives in [`skills/second-opinion/SEAT-BRIEFS.md`](./skills/second-opinion/SEAT-BRIEFS.md).
240
-
241
- **Cost is disclosed up front.** Before any model launches, you see the run shape — including any enabled optional elements — for example:
242
-
243
- > This run uses 3 council models across 2 fanout waves + 1 chair call, with critic seat + debate mode ON (~7 base runs + up to 6 rebuttal calls).
244
-
245
- Then the council waits for your confirmation.
246
-
247
- The skill lives at **[`skills/second-opinion/SKILL.md`](./skills/second-opinion/SKILL.md)**; the design spec behind it is **[`skills/second-opinion/COUNCIL-DESIGN.md`](./skills/second-opinion/COUNCIL-DESIGN.md)**. For what `amicus council tally|verdict|report|stats` actually take as input and produce — field-by-field schemas, verdict.json's provenance, and a full worked example run against the real CLI — see **[docs/council.md](./docs/council.md)**.
248
-
249
- **Headless council (CI).** The same pipeline runs with no Claude runtime at all: `amicus council
250
- run --prompt-file briefing.md --models gemini,glm --chair deepseek --json` executes the review
251
- waves, the anonymized cross-review, the tally, and the chair verdict in one command, and writes
252
- the full run directory (`verdict.json` with the chair's parsed `overallVerdict`, `report.html`,
253
- every review and judge output). That is what powers the repo's own **Council Review GitHub Action
254
- v2** — on PRs labeled `council-review` it posts an adjudicated verdict as a check run plus a
255
- sticky comment, uploads the run directory as an evidence artifact, and can optionally gate merges
256
- via its `fail_on` input (default: report-only). Reference: [docs/council.md](./docs/council.md#amicus-council-run).
257
-
258
- **Free council (zero-cost).** Want the cross-examination without the model spend? `amicus setup` offers a **Free OpenRouter council** mode — readline wizard option 2, and the Electron **Models** step. It detects the free `:free` models live from the catalog, lets you multi-pick (Enter takes a vendor-diverse default), and saves them as `councils.free` — a first-class `councils` config primitive seeded under collision-safe `free-*` aliases. Your `config.default` is left untouched, and all you need is an `OPENROUTER_API_KEY`.
259
-
260
- Run it anywhere a council runs:
261
-
262
- ```bash
263
- amicus fanout --council free --prompt "Review this design"
264
- ```
265
-
266
- The `amicus_fanout` MCP tool takes the same `council` parameter, and the `second-opinion` skill reads `councils.free` automatically. A member that gets delisted is dropped with a warning — the council still runs as long as ≥2 survive. Free models are **rate-limited and quality-variable**, and some return 404 unless you enable data-sharing at [openrouter.ai/settings/privacy](https://openrouter.ai/settings/privacy).
267
-
268
- **Council presets.** Save your own named member lists with `amicus council save <name> --models a,b,c` (≥2 resolvable aliases or `provider/model` IDs), then run them with `--council <name>` anywhere a council runs. `amicus council list` shows saved presets plus three built-in benches that work with no setup at all — `free` (the same zero-cost dynamic pick described above, used when you haven't seeded `councils.free`), `budget` (cheap workhorses, one per vendor family), and `frontier` (premium flagships, one per vendor family). `amicus council show <name>` resolves any of them (saved or built-in) and reports which members are currently usable. A saved council always shadows a built-in of the same name — exactly how the wizard's `councils.free` seeding already worked.
269
-
270
- **Policy packs (v4.5).** A council preset only saves the bench. A **pack** saves the whole run — bench, chair, critic/lenses, cost/timeout options, and a briefing template — as one named, shareable JSON file:
271
-
272
- ```bash
273
- amicus pack save review-bench --kind council --bench gemini,deepseek,gpt --chair opus --timeout 20 --max-cost 2
274
- amicus council run --pack review-bench --prompt-file plan.md --json
275
- ```
276
-
277
- Any flag you also type on that second line overrides just that value — a pack only fills in what you didn't say explicitly, and it's recorded on the run either way. Packs work the same way on `fanout`/`start` and on the `amicus_fanout`/`amicus_start`/`amicus_council_run` MCP tools. `amicus pack list`/`show`/`rm` manage them, and `--from-run <id>` builds one from a run you already liked instead of typing flags at all. Full reference: [docs/usage.md § Policy packs](./docs/usage.md#policy-packs).
278
-
279
- **Briefing templates (v4.5).** `--template <name> --artifact <file>` (plus repeatable `--var k=v`) renders a `{{prompt}}`/`{{artifact}}`-style Markdown template before it's sent, on `start`/`fanout`/`council run` alike — templates live in `~/.config/amicus/templates/`, and a pack's `briefing.template` is how one reaches an MCP-invoked run (MCP has no template param of its own). `amicus template list|show` manage them; v4.5 ships one built-in, `review`. Full reference: [docs/usage.md § Briefing templates](./docs/usage.md#briefing-templates).
280
-
281
- ---
282
-
283
338
  ## The parallel window
284
339
 
285
340
  When you don't need a full council — just one other model's take — fork a conversation. Amicus extracts your current Claude Code context, opens a session pre-loaded with it, you **work** alongside it, and you **fold** a structured summary back into Claude's context when you're done.
@@ -305,12 +360,6 @@ When you don't need a full council — just one other model's take — fork a co
305
360
 
306
361
  **Safety.** Amicus warns on **file conflicts** (a file changed externally while the session ran) and on **context drift** (the shared context may be stale relative to your current session), so a fold never silently overwrites newer work.
307
362
 
308
- **Auto-update.** Amicus checks the npm registry at most once every 24 hours (cached background check). When an update exists, the CLI prints a notice and the Electron toolbar shows a one-click **Update** banner. Or run it yourself:
309
-
310
- ```bash
311
- amicus update
312
- ```
313
-
314
363
  ![The parallel-window architecture: fork, work, fold](./docs/architecture.png)
315
364
 
316
365
  ---
@@ -332,7 +381,7 @@ amicus update
332
381
  | `amicus spend` | Cross-run cost rollup from the spend ledger, with per-run attribution — total + per-model spend, tokens, and source mix, most-expensive first (`--wave`/`--council`/`--project`/`--model`/`--op`/`--failed` filter it, `--group-by` buckets it, `--since 7d` windows it; `--json` for a versioned doc; shows remaining OpenRouter credit when a key is configured). |
333
382
  | `amicus key` | Manage API keys non-interactively: `amicus key <provider> <key>` saves after live validation; `--remove`; bare `amicus key` lists providers. |
334
383
  | `amicus provider` | Add/list/test/remove local, OpenAI-compatible providers (LM Studio, Ollama, vLLM) — configured with `--preset` or `--url`, at **$0** marginal cost (`--json` on every subcommand). |
335
- | `amicus council` | Council math: `tally <input.json>` (deterministic tiers + ledger append), `stats` (reviewer reliability), `report <verdict.json> [--md\|--html]`, `validate <file>` (findings-block check, exit 0/2/1), `verdict <tally.json> [--decisions <d.json>] [-o <out.json>]` (build + write verdict.json). Presets: `save <name> --models a,b,c`, `list [--json]`, `show <name> [--json]` — see [The Council](#the-council) for the built-in `free`/`budget`/`frontier` benches. |
384
+ | `amicus council` | Council math: `tally <input.json>` (deterministic tiers + ledger append), `stats` (reviewer reliability), `report <verdict.json> [--md\|--html]`, `validate <file>` (findings-block check, exit 0/2/1), `verdict <tally.json> [--decisions <d.json>] [-o <out.json>]` (build + write verdict.json). Presets: `save <name> --models a,b,c`, `list [--json]`, `show <name> [--json]` — see [Council presets](#council-presets) for the built-in `free`/`budget`/`frontier` benches. |
336
385
  | `amicus council run` | The headless council engine: Stage-1 reviews → anonymized cross-review → deterministic tally → non-Claude chair verdict, in one command with no Claude runtime. Add `--debate` for a Stage-2.5 rebuttal round (raisers defend/amend/withdraw, disputing judges re-vote) and `--claude-review <file>` to enter Claude's own review as judged review N+1. Writes a run directory with `verdict.json` (including `overallVerdict`) and `report.html` — see [docs/council.md](./docs/council.md#amicus-council-run). |
337
386
  | `amicus pack` | Save a full run configuration — bench, chair/critic/lenses, options, briefing template — and invoke it by name: `save <name> --kind council\|fanout\|solo [flags]` (or `--from-run <id>`), `list`, `show <name>`, `rm <name>`. `--pack <name>` on `start`/`fanout`/`council run` loads one; explicit flags always override it. See [docs/usage.md § Policy packs](./docs/usage.md#policy-packs). |
338
387
  | `amicus template` | `list`/`show <name>` a briefing template. `--template <name> [--artifact <file>] [--var k=v]` on `start`/`fanout`/`council run` renders one before the briefing is sent. See [docs/usage.md § Briefing templates](./docs/usage.md#briefing-templates). |
@@ -364,7 +413,7 @@ $ amicus status demo123 --json
364
413
  "taskId": "demo123",
365
414
  "status": "complete",
366
415
  "elapsed": "5m 0s",
367
- "version": "4.5.1",
416
+ "version": "4.5.3",
368
417
  "model": "google/gemini-2.5-flash",
369
418
  "phase": "terminal"
370
419
  }
@@ -474,7 +523,7 @@ Run `amicus doctor` first — it checks keys, catalog, OpenCode binary, Electron
474
523
  | `npm install -g amicus` fails with `EEXIST: … claude-sidecar` | The old upstream `claude-sidecar` package is still installed globally; npm won't overwrite another package's bin shims | `npm uninstall -g claude-sidecar`, then `npm install -g amicus`. Your keys and past sessions are not lost, but v2.0.0 no longer reads the old paths automatically — see [docs/SHIMS.md](./docs/SHIMS.md) for the one-time migration steps (rename `~/.config/sidecar/` and any `.claude/sidecar_sessions/` dirs). |
475
524
  | Install fails partway, or `amicus doctor` reports the OpenCode binary "not found" | A **transient** error during the OpenCode engine's own postinstall (a spawn `ENOENT`, or an antivirus file-lock while it lays down its 11 per-platform binaries) can roll back the whole atomic install — retrying usually succeeds | Just re-run `npm install -g amicus`. If it still fails, clear the cache first: `npm cache clean --force && npm install -g amicus`. |
476
525
  | `401` / auth error | No usable key for the model's vendor — bare `provider/model` ids fall back to `OPENROUTER_API_KEY` automatically, so this means neither the direct key nor an OpenRouter key is configured (or `--gateway direct`/`openrouter` forced a gateway whose key is missing) | Run `amicus setup`, or `amicus key <provider> <key>` to add the missing key; see [Routing](#routing). |
477
- | `402` / "Payment Required" on first council review / `start` / `fanout` call | Your OpenRouter key is real but has no credit. Key save (`amicus key openrouter <key>` or the setup wizard's key step) only checks that the key **authenticates** — it doesn't check balance, so a zero-credit key saves cleanly and only fails later, on the first real model call. (The `amicus council` subcommand itself is deterministic math and never calls a model.) | Add credit at [openrouter.ai/credits](https://openrouter.ai/credits), **or** switch to a zero-cost council: `amicus setup` → option 2 (Free OpenRouter council) builds one from live `:free`-suffixed models and saves it as `councils.free` — then run `amicus fanout --council free …`. See "Free council (zero-cost)" under [The Council](#the-council) above. |
526
+ | `402` / "Payment Required" on first council review / `start` / `fanout` call | Your OpenRouter key is real but has no credit. Key save (`amicus key openrouter <key>` or the setup wizard's key step) only checks that the key **authenticates** — it doesn't check balance, so a zero-credit key saves cleanly and only fails later, on the first real model call. (The `amicus council` subcommand itself is deterministic math and never calls a model.) | Add credit at [openrouter.ai/credits](https://openrouter.ai/credits), **or** switch to a zero-cost council: `amicus setup` → option 2 (Free OpenRouter council) builds one from live `:free`-suffixed models and saves it as `councils.free` — then run `amicus fanout --council free …`. See [Free council (zero-cost)](#free-council-zero-cost) above. |
478
527
  | Every direct `anthropic/…` model (`haiku`, `sonnet`, `opus`, `claude`) errors `Not Found` in ~2 s at zero tokens, but the same model works via `openrouter/anthropic/…` | An inherited `ANTHROPIC_BASE_URL` missing its `/v1` path segment. The engine appends only `/messages`, so requests hit `https://api.anthropic.com/messages` → HTTP 404 with an empty body → the bare status text. A shell spawned by Claude Code sets the `/v1`-less form for you. The model id, alias, and key are all fine. | `export ANTHROPIC_BASE_URL=https://api.anthropic.com/v1`, or unset it entirely, or pass `--gateway openrouter`. In a council a dead seat **degrades the run instead of failing it** — smoke-test each seat with one throwaway `amicus start` before paying for a council. See [docs/troubleshooting.md](./docs/troubleshooting.md#every-direct-anthropic-model-fails-with-not-found). |
479
528
  | `Model 'X' is unverified against the direct catalog; attempting anyway` for a model that plainly exists | Not a claim the model is wrong — amicus **couldn't check**. That vendor's direct catalog fetch failed (usually a stale or truncated stored key), leaving its namespace empty, and an empty namespace never blocks a launch. The engine may still run the model from its own credential store, so a working model warns forever. | `amicus models --refresh` and watch for a provider that stays empty; re-save the good key with `amicus key <provider> <apikey>`. See [docs/troubleshooting.md](./docs/troubleshooting.md#model-x-is-unverified-against-the-direct-catalog-attempting-anyway). |
480
529
  | Session not found | No session matches the given ID | Run `amicus list`, or omit `--session-id` to use the most recent. |
package/bin/amicus.js CHANGED
@@ -22,6 +22,7 @@ const { handleStart, handleFanout, handleRead } = require('../src/cli-handlers-r
22
22
  const { handleResume, handleContinue } = require('../src/cli-handlers-resume-continue');
23
23
  const { isOneShotCommand, armExitWatchdog } = require('../src/utils/lifecycle');
24
24
  const { suggestCommand } = require('../src/utils/input-validators');
25
+ const { unknownFlags, getKnownFlags } = require('../src/utils/known-flags');
25
26
  const { logger } = require('../src/utils/logger');
26
27
 
27
28
  const VERSION = require('../package.json').version;
@@ -30,6 +31,28 @@ async function main() {
30
31
  const args = parseArgs(process.argv.slice(2));
31
32
  const command = args._[0];
32
33
 
34
+ // Reject unknown flags BEFORE anything can act on a half-understood command
35
+ // line. parseArgs accepts any `--token`, so an unrecognized one used to land
36
+ // on `args`, go unread, and let the command run as though it were never typed
37
+ // — `start … --headless` silently took the interactive path, ignored `--model`
38
+ // and left a session running. Same treatment as an unknown command: name it,
39
+ // suggest the nearest real flag, point at help, exit 1. See
40
+ // src/utils/known-flags.js for what counts as known and why.
41
+ const badFlags = unknownFlags(args);
42
+ if (badFlags.length > 0) {
43
+ for (const flag of badFlags) {
44
+ console.error(`Unknown option: --${flag}`);
45
+ const candidates = suggestCommand(flag, [...getKnownFlags()]);
46
+ if (candidates.length > 0) {
47
+ console.error(`Did you mean: ${candidates.map(c => `--${c}`).join(', ')}`);
48
+ }
49
+ }
50
+ console.error(command
51
+ ? `Run \`amicus ${command} --help\` to see valid options.`
52
+ : 'Run `amicus --help` to see valid options.');
53
+ process.exit(1);
54
+ }
55
+
33
56
  // Install crash handler for MCP-spawned processes (have --task-id)
34
57
  if (args['task-id'] && (command === 'start' || command === 'continue')) {
35
58
  const { installCrashHandler } = require('../src/sidecar/crash-handler');
@@ -137,6 +137,22 @@ Legacy `SIDECAR_IDLE_TIMEOUT*` names were removed in v2.0.0 — rename to the `A
137
137
 
138
138
  Set `AMICUS_IDLE_TIMEOUT=0` to disable self-termination entirely.
139
139
 
140
+ ### Server startup
141
+
142
+ | Variable | Purpose | Default |
143
+ |----------|---------|---------|
144
+ | `AMICUS_SERVER_START_TIMEOUT_MS` | How long to wait for OpenCode to report it is listening before treating the start as failed. | `30000` on Windows, `15000` elsewhere |
145
+
146
+ A start that exceeds this window is treated as **transient** and retried on the same bounded schedule as an OpenCode database lock race (5 attempts, 250/500/1000/2000 ms), because retrying costs nothing but the backoff while a failed start costs a whole review seat.
147
+
148
+ Raise it if you see `Timeout waiting for server to start` on a slow box — a project directory on a sync-backed volume (OneDrive, Dropbox) with an antivirus scanner attached can push a cold OpenCode/SQLite start well past the default. Values of `0` or below are ignored rather than honored, since a zero start timeout fails every start instantly.
149
+
150
+ To see how much headroom you actually have, run with `LOG_LEVEL=debug` and look for the `OpenCode server started` line — it reports both `startMs` (what the start took) and `timeoutMs` (the ceiling it ran against):
151
+
152
+ ```json
153
+ {"level":"debug","msg":"OpenCode server started","startMs":561,"timeoutMs":30000}
154
+ ```
155
+
140
156
  ### Shared server
141
157
 
142
158
  The shared-server mode (`AMICUS_SHARED_SERVER=1`, which is the default) lets multiple Amicus sessions reuse a single OpenCode Go binary process rather than spawning one per invocation, eliminating cold-start latency on the second and subsequent calls. Disable it with `AMICUS_SHARED_SERVER=0` if you need per-process isolation or are diagnosing a crash loop.
package/docs/usage.md CHANGED
@@ -443,7 +443,7 @@ $ amicus status demo123 --json
443
443
  "taskId": "demo123",
444
444
  "status": "complete",
445
445
  "elapsed": "5m 0s",
446
- "version": "4.5.1",
446
+ "version": "4.5.3",
447
447
  "model": "google/gemini-2.5-flash",
448
448
  "phase": "terminal"
449
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amicus",
3
- "version": "4.5.1",
3
+ "version": "4.5.3",
4
4
  "mcpName": "io.github.BourbonDog/amicus",
5
5
  "description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
6
6
  "keywords": [
@@ -77,6 +77,7 @@
77
77
  "@modelcontextprotocol/sdk": "^1.27.0",
78
78
  "@opencode-ai/sdk": "^1.1.36",
79
79
  "dotenv": "^17.2.3",
80
+ "extract-zip": "^2.0.1",
80
81
  "opencode-ai": "^1.2.20",
81
82
  "tiktoken": "^1.0.0",
82
83
  "update-notifier": "^7.3.1",
@@ -93,7 +94,7 @@
93
94
  "jest": "^29.0.0",
94
95
  "lint-staged": "^16.3.2",
95
96
  "puppeteer": "^24.36.0",
96
- "sharp": "^0.33.5",
97
+ "sharp": "^0.35.3",
97
98
  "ws": "^8.19.0"
98
99
  },
99
100
  "engines": {