amicus 4.4.0 → 4.4.1

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 (75) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +3 -1
  4. package/docs/DISTRIBUTION.md +234 -0
  5. package/docs/ROADMAP.md +200 -0
  6. package/docs/SHIMS.md +62 -0
  7. package/docs/architecture.md +104 -0
  8. package/docs/configuration.md +371 -0
  9. package/docs/council.md +911 -0
  10. package/docs/doc-system.md +92 -0
  11. package/docs/electron-testing.md +471 -0
  12. package/docs/jsdoc-setup.md +75 -0
  13. package/docs/opencode-integration.md +114 -0
  14. package/docs/publishing.md +60 -0
  15. package/docs/schemas.md +55 -0
  16. package/docs/testing.md +589 -0
  17. package/docs/troubleshooting.md +298 -0
  18. package/docs/usage.md +699 -0
  19. package/electron/fold.js +1 -1
  20. package/electron/main.js +4 -1
  21. package/electron/setup-ui-aliases.js +6 -6
  22. package/electron/workspace-ui/live-model.js +12 -1
  23. package/electron/workspace-ui/md-lite.js +52 -8
  24. package/electron/workspace-ui/workspace-matrix.js +46 -9
  25. package/electron/workspace-ui/workspace-panels.js +14 -3
  26. package/electron/workspace-ui/workspace-render.js +7 -1
  27. package/electron/workspace-ui/workspace-verbs.js +48 -2
  28. package/package.json +8 -3
  29. package/schemas/council-run.schema.json +20 -0
  30. package/schemas/progress.schema.json +12 -0
  31. package/schemas/spend.schema.json +52 -4
  32. package/src/cli-handlers-spend.js +20 -2
  33. package/src/cli-handlers-watch.js +11 -0
  34. package/src/cli.js +4 -2
  35. package/src/council/briefings-debate.js +27 -7
  36. package/src/council/briefings-stage2.js +155 -25
  37. package/src/council/briefings.js +24 -1
  38. package/src/council/findings.js +236 -9
  39. package/src/council/parse-stage2.js +10 -2
  40. package/src/council/report.js +19 -8
  41. package/src/council/run-assemble.js +42 -1
  42. package/src/council/run-budget.js +64 -11
  43. package/src/council/run-chair.js +4 -1
  44. package/src/council/run-debate.js +4 -2
  45. package/src/council/run-finalize.js +102 -0
  46. package/src/council/run-launch.js +29 -1
  47. package/src/council/run-server.js +248 -0
  48. package/src/council/run-stage2.js +118 -0
  49. package/src/council/run-stages.js +132 -111
  50. package/src/council/run-state.js +23 -1
  51. package/src/council/run.js +44 -46
  52. package/src/council/tally.js +10 -0
  53. package/src/headless.js +175 -6
  54. package/src/observe/council-legs.js +60 -3
  55. package/src/observe/live-doc.js +18 -1
  56. package/src/observe/watch-render.js +4 -1
  57. package/src/sidecar/child-sessions.js +1 -2
  58. package/src/sidecar/fanout-leg-fallback.js +69 -21
  59. package/src/sidecar/fanout-leg.js +6 -0
  60. package/src/sidecar/fanout-signals.js +61 -0
  61. package/src/sidecar/fanout-wave-io.js +75 -0
  62. package/src/sidecar/fanout.js +61 -70
  63. package/src/sidecar/progress-fields.js +26 -4
  64. package/src/sidecar/progress.js +8 -1
  65. package/src/sidecar/session-utils.js +23 -14
  66. package/src/spend-query.js +17 -5
  67. package/src/utils/lifecycle.js +37 -1
  68. package/src/utils/path-fence.js +39 -1
  69. package/src/utils/pricing.js +26 -10
  70. package/src/utils/server-setup.js +79 -1
  71. package/src/utils/spend-ledger.js +24 -3
  72. package/src/workspace/artifact-guard.js +22 -1
  73. package/src/workspace/fold-format.js +33 -4
  74. package/src/workspace/live-normalize.js +28 -15
  75. package/src/workspace/run-detail.js +7 -1
package/docs/usage.md ADDED
@@ -0,0 +1,699 @@
1
+ # CLI & MCP Usage Reference
2
+
3
+ ## CLI Commands
4
+
5
+ The `am` alias is interchangeable with `amicus` everywhere.
6
+
7
+ ```bash
8
+ # Core workflow
9
+ amicus start --model <model> --prompt "<task>"
10
+ amicus start --model <model> --prompt-file briefing.md --no-ui --json
11
+ amicus fanout --models "gemini,deepseek,gpt" --prompt "Review this" --json
12
+ amicus list [--status <filter>] [--all] [--json]
13
+ amicus resume <task_id> [--no-ui --json]
14
+ amicus continue <task_id> --prompt "Next step..." [--no-ui --json]
15
+ amicus read <task_id> [--conversation|--metadata|--json]
16
+ amicus status <task_id> [--json] # One-shot status for a session or wave
17
+ amicus watch <task_id> [--plain|--json] [--interval <sec>] [--ui] # Live-render a run from any terminal, or open it in the Council Workspace window
18
+ amicus abort <task_id> [--json]
19
+ amicus abort --all [--json]
20
+
21
+ # Setup & maintenance
22
+ amicus setup # Full wizard: keys, default model, aliases
23
+ amicus setup --api-keys # Open just the API-key step
24
+ amicus setup --add-alias fast=google/gemini-3.1-flash-lite-preview # bare canonical, direct-first
25
+ amicus models # List the live catalog
26
+ amicus models --search gemini # Filter by substring
27
+ amicus models --refresh # Force-fetch from provider APIs
28
+ amicus models --check # Audit aliases against catalog
29
+ amicus mcp # Start MCP server (stdio transport)
30
+ amicus update # Update to latest version
31
+ amicus doctor [--json] [--fix] # Diagnose setup; --fix self-heals (e.g. Electron)
32
+ amicus spend [--since 7d] [--wave <id>] [--group-by <dim>] [--rows] [--json] # Cost rollup + attribution query
33
+ amicus key <provider> <key> # Validate + save one API key (also: --remove / bare list)
34
+ amicus provider add|list|test|remove # Local / OpenAI-compatible servers ($0): Ollama, LM Studio, vLLM
35
+ amicus init [--claude] [--desktop] # Register skills + MCP on demand (postinstall re-run)
36
+ amicus council tally <input.json> --json # Deterministic tiers + street-cred (+ ledger append)
37
+ amicus council stats [--json] # Reviewer reliability from the ledger
38
+ amicus council report <verdict.json> [--md|--html] # Render the council run report
39
+ amicus council validate <file> [--json] # Validate a Stage-1 findings block (exit 0/2/1)
40
+ amicus council verdict <tally.json> [--decisions <d.json>] [-o <out.json>] [--render] # Build + write verdict.json
41
+ amicus council run --prompt-file <b.md> --models a,b,c --chair <m> [--json] # Headless engine: reviews, cross-review, tally, chair verdict
42
+ amicus council save <name> --models a,b,c # Save a named council preset (>=2 resolvable members)
43
+ amicus council list [--json] # List saved councils + built-ins (free/budget/frontier)
44
+ amicus council show <name> [--json] # Resolve a council (saved or built-in) and show its members
45
+ ```
46
+
47
+ ---
48
+
49
+ ## `amicus start` — Launch a Session
50
+
51
+ ```bash
52
+ amicus start --model gemini --prompt "Fact-check the auth approach"
53
+ amicus start --model opus --prompt-file briefing.md --no-ui --json
54
+ amicus start --model deepseek --prompt "Generate tests" --no-ui --timeout 30
55
+ ```
56
+
57
+ **All options:**
58
+
59
+ | Option | Description | Default |
60
+ |--------|-------------|---------|
61
+ | `--model <model>` | Alias, or a full id: bare `provider/model` (canonical, direct-first) or `openrouter/provider/model` (explicit force-OpenRouter). | config default |
62
+ | `--prompt <text>` | Task description. | *(required unless `--prompt-file`)* |
63
+ | `--prompt-file <path>` | Read the prompt from a UTF-8 file (XOR `--prompt`). | |
64
+ | `--agent <agent>` | OpenCode agent: `Chat`, `Build`, `Plan`. | `Chat` interactive / `Build` headless |
65
+ | `--no-ui` | Run headless (autonomous, no window). | off |
66
+ | `--json` | Emit the run result as stable JSON (requires `--no-ui`). | off |
67
+ | `--timeout <minutes>` | Headless timeout. | 15 |
68
+ | `--context-turns <N>` | Max conversation turns to include. | 50 |
69
+ | `--context-since <duration>` | Time filter (e.g. `2h`); overrides turns. | |
70
+ | `--context-max-tokens <N>` | Max context tokens. | 80000 |
71
+ | `--no-context` | Skip parent conversation history. | off |
72
+ | `--thinking <level>` | Reasoning effort: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`. | model default |
73
+ | `--summary-length <length>` | Fold summary verbosity: `brief`, `normal`, `verbose`. | `normal` |
74
+ | `--mcp <spec>` | Add an MCP server (`name=url` or `name=command`). | |
75
+ | `--mcp-config <path>` | Path to an `opencode.json` with MCP config. | |
76
+ | `--no-mcp` | Don't inherit MCP servers from the parent. | off |
77
+ | `--exclude-mcp <name>` | Exclude a specific inherited MCP server (repeatable). | |
78
+ | `--session-id <id\|current>` | Session to pull context from. | `current` |
79
+ | `--cwd <path>` | Project directory. | cwd |
80
+ | `--client <type>` | Client context: `code-local`, `code-web`, `cowork`. | `code-local` |
81
+ | `--position <pos>` | Window position: `right`, `left`, `center`. | `right` |
82
+ | `--fold-shortcut <key>` | Customize the fold keyboard shortcut. | `Cmd/Ctrl+Shift+F` |
83
+ | `--opencode-port <port>` | Port override for the OpenCode server. | |
84
+ | `--session-dir <path>` | Explicit session-data directory. | |
85
+ | `--setup` | Force-open configuration before launching. Does **not** relax the `--prompt`/`--prompt-file` requirement — `start --setup` still fails fast with "Error: --prompt or --prompt-file is required" if neither is given. | |
86
+ | `--no-validate-model` | Skip model-catalog validation before launch. | validation on |
87
+ | `--gateway <mode>` | Routing override for this launch: `auto` (direct-first), `direct` (require a direct provider key), or `openrouter` (force OpenRouter). Overrides `routing.prefer` for one call. | `auto` |
88
+
89
+ > Agents: **Chat** auto-approves reads and asks before writes/bash (interactive default); **Build** has full tool access (headless default); **Plan** is read-only analysis. `--agent Chat` is interactive-only and incompatible with `--no-ui`.
90
+
91
+ **Catalog validation.** For an explicit `--model`, the model is checked against the live catalog before launch — a typo'd name fails fast with same-vendor suggestions. For a model inherited from a previous session (`continue`/`resume` without `--model`), validation is **advisory**: a warning is printed but the session starts anyway. Skip with `--no-validate-model`.
92
+
93
+ **The fold handoff.** In interactive mode, clicking **FOLD** (or headless completion) is a one-way summary handoff, not a live handback. Mechanically: the model is asked for a structured summary, which is written to the sidecar process's stdout as a `[SIDECAR_FOLD]`-tagged block; the runner that spawned the sidecar captures that stdout and persists it to the session's `summary.md` under the session directory. Your orchestrating agent retrieves it on request — `amicus read <taskId>` (CLI) or the `amicus_read` MCP tool — and the result comes back wrapped in an `<untrusted_sidecar_output>` fence (it's another model's prose entering your context, treated as data, not instructions).
94
+
95
+ ---
96
+
97
+ ## `amicus fanout` — Same Prompt, Many Models
98
+
99
+ Fanout runs one headless wave: every leg receives the **same** prompt concurrently (this is the shared-prompt model that the council's review stages are built on). When all legs settle, Amicus emits a single JSON wave document on stdout.
100
+
101
+ ```bash
102
+ amicus fanout --models "gemini,deepseek,gpt" --prompt "Review this design" --json
103
+ amicus fanout --models "gemini,opus" --prompt-file briefing.md --json --wave-id my-wave-1
104
+ amicus fanout --council free --prompt "Review this design" --json
105
+ ```
106
+
107
+ **Key options:**
108
+
109
+ | Option | Description |
110
+ |--------|-------------|
111
+ | `--models <a,b,c>` | Comma-separated aliases or full model IDs (bare `provider/model` routes direct-first; `openrouter/provider/model` forces OpenRouter). Required unless `--council` is given; mutually exclusive with `--council`. |
112
+ | `--council <name>` | Run a saved council, or one of the built-in benches `free` \| `budget` \| `frontier`, instead of `--models`; mutually exclusive with `--models`. A saved council of the same name as a built-in always takes precedence (see `amicus council list`/`show`). |
113
+ | `--prompt <text>` | Shared briefing (mutually exclusive with `--prompt-file`). |
114
+ | `--prompt-file <path>` | Read the shared briefing from a file. Preferred for long briefs and required on Windows when content exceeds ~32 KB. |
115
+ | `--wave-id <id>` | Set the wave ID explicitly; leg IDs become `<wave-id>-1` … `<wave-id>-N`. |
116
+ | `--session-id <id\|"current">` | Session ID to pull shared context from (default `current`). Same semantics as on `start`. |
117
+ | `--json` | Emit the wave document on stdout. |
118
+ | `--max-cost <$>` | Refuse the wave if the estimated total exceeds `$` (soft ceiling). |
119
+ | `--no-cost-gate` | Disable the budget gate (per-$/Mtok threshold + ceiling) for this run. |
120
+ | `--no-validate-model` | Skip catalog validation. |
121
+ | `--gateway <mode>` | Routing override applied to every leg: `auto` (direct-first), `direct`, or `openrouter`. |
122
+
123
+ **Shared per-leg knobs.** Every leg in the wave also accepts the same per-leg options as `start`:
124
+ `--agent`, `--thinking`, `--timeout`, `--summary-length`, `--no-context`, `--context-*`, `--mcp*`,
125
+ `--no-validate-model`, `--gateway`, `--cwd`.
126
+
127
+ **Exit codes:** `0` all legs complete · `2` partial wave (at least one leg failed) · `1` none complete / hard failure · `130` SIGINT · `143` SIGTERM.
128
+
129
+ **Wave document shape:**
130
+
131
+ ```json
132
+ {
133
+ "schemaVersion": 1,
134
+ "waveId": "...",
135
+ "status": "complete",
136
+ "counts": { "total": 2, "complete": 2, "error": 0, "timeout": 0, "aborted": 0 },
137
+ "legs": [
138
+ {
139
+ "taskId": "...", "model": "...", "modelInput": "...", "agent": "...",
140
+ "status": "complete", "summary": "...", "error": null,
141
+ "createdAt": "...", "completedAt": "...", "durationMs": 0
142
+ }
143
+ ]
144
+ }
145
+ ```
146
+
147
+ `status` is `complete | partial | error | aborted`. Each leg's `summary` is that model's full response.
148
+ `legs[]` come in `--models` order (or preset-membership order for `--council`), not completion order.
149
+
150
+ **Fanout vs. N parallel starts.** Use `fanout` when every leg should receive the **same prompt** — this is what the council's independent review waves use. Use N separate `start` calls when each leg needs a **different prompt**.
151
+
152
+ ---
153
+
154
+ ## `amicus council run` — Headless Council Engine
155
+
156
+ Runs the **entire adjudicated council pipeline in one command with no Claude runtime** (v4.0):
157
+ Stage-1 independent reviews → anonymized peer cross-review (with bounded repair re-prompts) →
158
+ deterministic tally → non-Claude chair verdict → `verdict.json` + `report.html`, all written to a
159
+ durable run directory. Stage-4 accept/deny decisions stay human — the engine is report-only.
160
+
161
+ ```bash
162
+ amicus council run --prompt-file briefing.md --models gemini,glm --chair deepseek \
163
+ --out-dir council-run --json --max-cost 2.00 --timeout 10
164
+ ```
165
+
166
+ **Key options:**
167
+
168
+ | Option | Description |
169
+ |--------|-------------|
170
+ | `--prompt-file <path>` | The council briefing. **Required** — there is no inline `--prompt` for councils. |
171
+ | `--models <a,b,c>` \| `--council <name>` | The bench (≥2 seats); mutually exclusive, same semantics as `fanout`. |
172
+ | `--chair <model>` | Verdict synthesizer. Default `deepseek`; must **not** be a bench seat (pre-flight error). |
173
+ | `--critic <model>` | Optional adversarial seat; must **be** a bench seat. Mutually exclusive with `--lenses`. |
174
+ | `--lenses <s1,s2,...>` | Expert lenses, one per seat (count must equal seat count); forces `--no-ledger` semantics. |
175
+ | `--out-dir <dir>` | Run directory. Default `./council-<runId>/`. |
176
+ | `--json` | Emit the council-run document on stdout (error envelope + documented exit codes on failure). |
177
+ | `--max-cost <$>` | **Whole-run** ceiling on **known** spend, checked before each paid stage launch. A leg whose cost cannot be determined does not count against it and never halts the run; when the total is inexact and a ceiling is set, the run exits `2`. |
178
+ | `--timeout <min>` | **Per-leg** timeout (fanout semantics); bound the aggregate with your CI job timeout. |
179
+ | `--gateway <mode>` / `--no-validate-model` | Same routing/validation semantics as `start`/`fanout`. |
180
+ | `--debate` | Adds a Stage-2.5 rebuttal round (provisional tally → defense → re-vote → final tally) between cross-review and the final tally. |
181
+ | `--claude-review <file>` | Enters Claude's own review from a file as judged review N+1 — no leg is ever launched for it; `claude` is a reserved seat name and may not also appear in `--models`, `--chair`, or `--critic` (pre-flight error). |
182
+ | `--no-cost-gate` | Disable the per-leg price gate for the whole run (repairs + chair). |
183
+
184
+ **Exit codes:** `0` full run · `2` degraded but reportable (fewer than 2 judges, chair failure —
185
+ `overallVerdict: null` — a cost ceiling hit after the tally, or a `--max-cost` ceiling set over a
186
+ total the run knows is only a floor) · `1` quorum/pre-tally failure
187
+ (error doc) · `130`/`143` signals. `amicus status|abort <councilRunId>` work on council runs
188
+ via the sessions-dir pointer file. There is no CLI `wait` — to block until a council run
189
+ finishes, use the MCP `amicus_wait` tool instead.
190
+
191
+ Field-by-field run-directory contents, the degradation table, and `verdict.json`'s
192
+ `overallVerdict` are documented in **[docs/council.md](./council.md#amicus-council-run)**. This is
193
+ the command the repo's Council Review GitHub Action (v2) runs on labeled PRs.
194
+
195
+ ---
196
+
197
+ ## `amicus council save|list|show` — Council Presets
198
+
199
+ A council preset is a named list of `--models`-style members (aliases or full `provider/model` IDs) that `--council <name>` (on `fanout` and the `amicus_fanout` MCP tool) can run in one shot.
200
+
201
+ ```bash
202
+ amicus council save my-bench --models opus,gpt,deepseek # Save (or overwrite) a preset
203
+ amicus council list [--json] # Saved presets + built-ins
204
+ amicus council show my-bench [--json] # Members + resolution (resolved/dropped)
205
+ amicus council show budget [--json] # Works on built-ins too
206
+ ```
207
+
208
+ **Built-in benches.** Three names resolve even with no saved config — `resolveCouncilMembers` (the same function `--council` uses everywhere) checks user-saved councils first, and falls back to these only when the name isn't saved:
209
+
210
+ | Name | Members | Resolution |
211
+ |------|---------|------------|
212
+ | `free` | Zero-cost `:free`-suffixed OpenRouter models, one per vendor | Dynamic — resolved from the live catalog at use time (same logic as the setup wizard's free-council picker), with a small offline pinned fallback when the catalog is empty |
213
+ | `budget` | 3 cheap workhorse aliases across 3 distinct vendor families | Static — fixed aliases from the default alias table |
214
+ | `frontier` | 3 premium flagship aliases across 3 distinct vendor families | Static — fixed aliases from the default alias table |
215
+
216
+ **Precedence: user config always shadows a built-in of the same name.** If you `amicus setup` the wizard's free-OpenRouter-council flow, it seeds `councils.free` in your config — that saved list then wins over the built-in `free` bench (this is the pre-existing behavior, unchanged). The same shadowing applies if you `amicus council save budget --models ...`. `amicus council list` marks a built-in `shadowed: true` when a saved council of the same name exists.
217
+
218
+ ---
219
+
220
+ ## `amicus models` — The Model Catalog
221
+
222
+ Amicus does **not** ship a frozen table of model names. Aliases and validation resolve against a **live catalog** fetched from provider APIs and cached at `~/.config/amicus/model-catalog.json` (24-hour TTL; the fetch works without an API key).
223
+
224
+ ```bash
225
+ amicus models # List the catalog
226
+ amicus models --search gemini # Filter by substring over id and name
227
+ amicus models --refresh # Force-refresh from provider APIs
228
+ amicus models --check # Audit your aliases against the catalog
229
+ ```
230
+
231
+ `amicus models --check` exits with the **number of stale aliases** (capped at 100) and prints same-vendor replacement suggestions for each, so it drops cleanly into CI.
232
+
233
+ **Validation on launch.** `start` and `fanout` validate the model against the catalog before launching. For an explicit `--model` on `continue`/`resume` this is **blocking** (a typo'd model fails fast with suggestions); for a model *inherited* from a prior session it's **advisory**. Skip it any time with `--no-validate-model`, or fix the catalog with `amicus models --refresh`.
234
+
235
+ **Aliases are a curated seed, not a fixed list.** `amicus setup` seeds a curated set of short aliases (e.g. `gemini`, `gpt`, `opus`, `deepseek`), and you add or override them with `amicus setup --add-alias name=provider/model`. To see exactly what resolves on *your* machine, run `amicus models` — that is the source of truth.
236
+
237
+ **Full-id passthrough.** You can always bypass aliases and name a model directly. Bare `provider/model` is the canonical, policy-routed form; `openrouter/provider/model` is an explicit override. See [Routing](../README.md#routing) for the full explanation — summary:
238
+
239
+ | Format | Example | Routing | Credentials |
240
+ |--------|---------|---------|-------------|
241
+ | `provider/model` (bare, canonical) | `google/gemini-2.5-flash`, `openai/gpt-5`, `anthropic/claude-opus-4` (the `opus` alias resolves here by default) | Direct-first (`auto`) | That vendor's direct key if configured, else `OPENROUTER_API_KEY` |
242
+ | `openrouter/provider/model` | `openrouter/google/gemini-2.5-flash` | Always OpenRouter | `OPENROUTER_API_KEY` |
243
+
244
+ ---
245
+
246
+ ## Other Commands
247
+
248
+ ```bash
249
+ amicus list # Current project
250
+ amicus list --status running # Filter: running, complete, error, timed-out,
251
+ # aborted, crashed, idle-timeout
252
+ amicus list --all # All projects
253
+ amicus list --json # Machine-readable
254
+
255
+ amicus read <id> # Fold summary (default)
256
+ amicus read <id> --conversation # Full conversation
257
+ amicus read <id> --metadata # Session metadata
258
+ amicus read <id> --json # Stable JSON run or wave document
259
+
260
+ amicus status <id> # One-shot status for a session or wave
261
+ amicus status --wave <id> # Alternative spelling for a wave ID
262
+ amicus status <id> --json # Machine-readable output
263
+
264
+ amicus resume <id> # Reopen session with full history
265
+ amicus resume <id> --no-ui --json # Headless resume; stable run document on stdout
266
+ amicus continue <id> --prompt "..." # New session; previous one as read-only context
267
+ amicus continue <id> --prompt "..." --no-ui --json # Headless continue; run doc carries the NEW task id
268
+
269
+ amicus abort <id> # Stop one running session
270
+ amicus abort <id> --json # Machine-readable abort result
271
+ amicus abort --all # Stop all running sessions in this project
272
+ amicus abort --all --json # Machine-readable abort result (scope: "all")
273
+
274
+ amicus setup --api-keys # Open just the API-key window
275
+ amicus setup --add-alias fast=google/gemini-2.5-flash # Add/override one alias (bare canonical)
276
+ ```
277
+
278
+ **`amicus status <id>` output.** Human-readable:
279
+
280
+ ```
281
+ $ amicus status demo123
282
+ Task: demo123
283
+ Status: complete (terminal)
284
+ Elapsed: 5m 0s
285
+ Model: google/gemini-2.5-flash
286
+ ```
287
+
288
+ `--json`:
289
+
290
+ ```
291
+ $ amicus status demo123 --json
292
+ {
293
+ "taskId": "demo123",
294
+ "status": "complete",
295
+ "elapsed": "5m 0s",
296
+ "version": "4.4.1",
297
+ "model": "google/gemini-2.5-flash",
298
+ "phase": "terminal"
299
+ }
300
+ ```
301
+
302
+ A running session additionally reports `messages`, `lastActivity`/`latest`, and (if stalled) a `STALLED` line with recovery guidance in `--json`. A wave ID (`amicus status <waveId>` / `--wave <waveId>`) instead reports `legsComplete`/`legsTotal` and a per-leg breakdown.
303
+
304
+ ---
305
+
306
+ ## Keys, Health & Spend
307
+
308
+ Five commands for day-to-day account and cost hygiene: manage keys (cloud or local), check your setup, run local models at $0, and see what you've spent.
309
+
310
+ ### `amicus key`
311
+
312
+ ```bash
313
+ amicus key # List every configured provider (cloud + local)
314
+ amicus key openrouter <key> # Validate + save a cloud vendor key
315
+ amicus key openrouter --remove # Remove a saved cloud vendor key
316
+ amicus key my-ollama <token> # Save/validate a bearer for a LOCAL provider
317
+ amicus key my-ollama --remove # Remove a local provider's bearer
318
+ ```
319
+
320
+ Bare `amicus key` lists both kinds of provider:
321
+
322
+ - **Cloud vendors** (`openrouter`, `google`, `openai`, `anthropic`, `deepseek`) — `✓` with a masked key hint, or `✗ not set`.
323
+ - **Local providers** (anything added with `amicus provider add`, below) — `no key required` when the entry has no `apiKeyEnv`, else `✓` with a masked hint or `✗ not set`.
324
+
325
+ `amicus key <provider> <key>` behaves differently depending on which kind `<provider>` is:
326
+
327
+ | Provider kind | What happens |
328
+ |---|---|
329
+ | Cloud vendor (one of the 5 above) | `<key>` is validated live against the vendor's API, then saved to `~/.config/amicus/.env` (`0600`). A failed validation aborts the save. |
330
+ | Local provider (an id in `config.providers`) | `<key>` is a **bearer token**, not a vendor API key. Amicus probes the endpoint *with* the bearer attached (2s timeout) and saves it to `.env` either way — the probe result only changes the confirmation message, it never blocks the save. If the entry had no `apiKeyEnv` yet, one is derived and stamped onto `config.providers.<id>` so the router picks it up. |
331
+
332
+ After a successful **cloud**-vendor save (not a local-provider bearer save), Amicus offers the cost-aware default picker — a short list of that vendor's models, recommended one flagged, that becomes `aliases.<provider>` and optionally `config.default`. Non-interactively (`--json`, `--quiet`, or no TTY) it silently takes the recommended pick and prints a one-line summary instead of prompting.
333
+
334
+ `--remove` deletes a saved key/bearer; every subcommand supports `--json`.
335
+
336
+ ### `amicus doctor`
337
+
338
+ ```bash
339
+ amicus doctor # Human-readable checklist
340
+ amicus doctor --json # Machine-readable (versioned doc)
341
+ amicus doctor --fix # Self-heal what can be self-healed, then re-report
342
+ ```
343
+
344
+ Runs every check below, in order, and prints a ✓/⚠/✗ line for each plus a targeted fix hint for anything not `ok`:
345
+
346
+ | Check | What it verifies | Can fail as |
347
+ |---|---|---|
348
+ | `node` | Node.js ≥ 18 | error |
349
+ | `config-dir` | The resolved config directory | *(always ok)* |
350
+ | `keys` | At least one cloud-vendor key configured | error |
351
+ | `default-model` | Your default model alias resolves | error |
352
+ | `catalog` | Model-catalog cache present and within the 24h TTL | warn |
353
+ | `aliases` | Your configured aliases still resolve against the catalog | warn |
354
+ | `opencode-bin` | The OpenCode engine binary is on `PATH` | error |
355
+ | `engine-mcp` | The engine copy `npx -y amicus@latest mcp` would actually launch (catches a broken npx-cache copy a healthy local install would hide) | warn (error only if there's exactly one npx-cache copy and it's broken) |
356
+ | `electron` | Electron (the interactive GUI) is installed | warn — headless still works |
357
+ | `skills` | Both skills exist under `~/.claude/skills/` | warn |
358
+ | `mcp` | Amicus is registered as an MCP server in Claude Code | warn |
359
+ | `mcp-legacy` | No duplicate legacy `sidecar` MCP entry survives alongside `amicus` | warn |
360
+ | `sessions-index-tmp` | No orphaned `sessions-index.json.*.tmp` files | warn |
361
+ | `openrouter-credit` | Remaining OpenRouter credit (skipped — reports `ok` — when no OpenRouter key is set) | warn |
362
+ | `local-providers` **(v4.2)** | Every provider in `config.providers` is reachable | warn |
363
+ | `project-root` | Your cwd looks like a real project, not an app/install dir | warn |
364
+
365
+ **`local-providers`** probes every configured local provider (2s timeout each) the same way `amicus provider test` does, and reports per-id reachability in one line, e.g. `ollama: 3 models @ http://127.0.0.1:11434/v1; my-vllm: unreachable @ http://127.0.0.1:8000/v1`. No providers configured at all is a plain `ok` ("none configured") — this check can never fail your doctor run outright, only warn: a napping `ollama serve` isn't treated as broken setup.
366
+
367
+ `--fix` self-heals four of the checks above in place: reprovisions Electron, copies the OpenCode engine into a broken npx-cache install, removes a duplicate legacy MCP entry, and sweeps orphaned session-index tmp files (only ones older than 60s). It does **not** start a local server for you — `local-providers` stays a warning until you start the server yourself.
368
+
369
+ Exit code is `1` if anything is `error`, else `0` (same rule drives `--json`'s `ok` field).
370
+
371
+ ### `amicus spend`
372
+
373
+ ```bash
374
+ amicus spend # All-time rollup, human-readable
375
+ amicus spend --since 7d # Restrict to the last 7 days
376
+ amicus spend --json # Machine-readable (versioned doc)
377
+ ```
378
+
379
+ Reads `~/.config/amicus/spend-ledger.jsonl` (one row per completed run/leg) and prints a most-expensive-model-first table: runs, input/output tokens, cost, and a **source mix** `r<N>/e<N>/u<N>` — how many of that model's runs were `reported` (billed cost from the provider), `estimated` (tokens × cached catalog pricing), or `unknown` (neither available). A trailing total line sums everything, plus your remaining OpenRouter credit when a key is configured.
380
+
381
+ **Cost markers** — the shared convention behind every dollar figure Amicus prints, not just `spend`'s table: a bare `$1.23` (no `~`) is a provider-reported cost; `~$1.23` is estimated from tokens × cached pricing; `?`/`—` mark a cost Amicus has no data for at all. A model whose rows are *all* unpriced shows `?` in the cost column rather than a measured-looking `$0.0000`, and whenever any row is unpriced the table adds an explicit `N unpriced row(s) — cost unknown and NOT in the total; real spend is at least this much.` line under the total. Unknown is never silently folded into the number: `amicus spend --json` carries `unpricedRows` on `total`, each `byModel` entry, each `group`, and `wasted`.
382
+
383
+ **A priced row can still understate.** A leg whose own cost resolved perfectly but which spawned a subagent whose child-session spend could not be determined writes a **priced** row — it lands in the `r` bucket and contributes to the total, so `unpricedRows` cannot see it. Those rows carry `subtreeUnknown: true` in the ledger and are counted as `unattributedSubtreeRows` everywhere `unpricedRows` lives, and the table adds a second, differently-worded line: `N row(s) spawned a subagent whose CHILD session spend could NOT be determined — real spend is HIGHER than this total.` The distinction matters — "we could not see this leg at all" and "we saw this leg but not what it spawned" are different facts, and a row can be counted in both. This is the same money `council run` reports as `costExact: false` / `subtreeUnknownLegs`; before v4.4.1 the two surfaces disagreed about it.
384
+
385
+ **`unknown` means "we observed nothing we can price", not "it was free".** A run whose captured **input/output** token totals are both zero resolves to `{amount: null, source: 'unknown'}` — never to `$0` — because pricing a zero-token total as `0 × catalog` would assert a bill we cannot support and would silently under-count the `--max-cost` ceiling. That holds even when the run *did* report cache or reasoning tokens: the estimate prices input and output and nothing else, so a leg observed only in those currencies is genuinely unpriceable, and calling it $0 would be the same fabrication one corner over. (Pricing them properly needs catalog fields that may not exist; when they do, the predicate and the estimate widen together.) Councils surface the same distinction: `run.json`'s `usage` block carries `unknownLegs` and `costExact`, the human summary appends `+ N leg(s) unknown — real spend is at least this much`, and the workspace's budget gauge switches to an indeterminate (hatched) band with a `≥` readout instead of claiming a percentage it cannot know.
386
+
387
+ **Subagent (child-session) spend is attributed to the leg that spawned it.** A leg that calls the `task` tool spawns a *child* OpenCode session that OpenCode bills separately and does **not** roll into the parent session's cost. Amicus walks those sessions when the leg finishes and adds their measured spend to the run total, reporting how much came from them as `cost.subtreeCost` / `cost.subtreeSessions`. The child's price comes from OpenCode's own billing, never from a catalog estimate — the SDK's session record carries no model id, so an estimate would be a guess. Where a subtree cannot be fully accounted for (the walk failed, a bound was hit, or a child reported work with no cost), the run carries `subtreeUnknownLegs` and `costExact: false` instead of a number: `costExact: true` means "this is the whole bill", not merely "every leg reported tokens".
388
+
389
+ **Local provider runs are a real, explicit `$0` tier.** `amicus provider`'s default pricing is `{prompt: 0, completion: 0}`, and a local seat still reports real token counts — so it resolves to an *estimated* (not unknown) cost, renders as `~$0.0000`, is counted in `e`, and sits right alongside your paid runs in the same rollup. The `unknown` label above keys on observed **input/output tokens**, never on the price, precisely so this `$0` tier stays `$0` — a real local leg reports thousands of input tokens even when it bills nothing. (A local run that returned no such tokens at all did nothing, and reports `unknown` like any other — that is the honest reading.)
390
+
391
+ **Query & attribution flags (v4.3).** `continue`/`resume`/council rows are now recorded in the ledger too, not just `start`/`fanout` legs, and every row carries attribution — `op`, `status`, `waveId`, `councilRunId`/`councilName`, `project`, `gateway`, plus fallback/retry linkage when applicable. Slice and filter with:
392
+
393
+ | Flag | Filters to |
394
+ |---|---|
395
+ | `--wave <id>` | rows from one fan-out wave |
396
+ | `--council <runId\|name>` | rows from one council run — matches either the run id or a saved council name |
397
+ | `--project <path\|.>` | rows recorded against one project (`.` expands to cwd) |
398
+ | `--model <id-or-prefix>` | rows whose model id starts with the given string |
399
+ | `--op <op>` | rows for one operation (`start`, `leg`, `continue`, `resume`, …) |
400
+ | `--failed` | rows with an explicit non-`complete` status (see the caveat below) |
401
+ | `--group-by <dim>` | bucket totals by `model` (default) \| `wave` \| `council` \| `project` \| `op` \| `day` |
402
+ | `--rows` | also emit the raw filtered rows (capped at 1000; `--json` sets `rowsTruncated: true` past the cap) |
403
+
404
+ All filters compose, e.g. `amicus spend --project . --group-by model --since 7d`.
405
+
406
+ **`--failed` and the `--json` `wasted` block both exclude rows with no recorded status at all.** A ledger row written before v4.3 (or any row that never reached a terminal-status write) isn't counted as "wasted money" — it's just unattributed, and counting it would fabricate a failure that was never actually recorded, so it's dropped rather than bucketed either way.
407
+
408
+ The read-only `amicus_spend` MCP tool (see [MCP Server](#mcp-server)) mirrors every one of these flags for MCP-only hosts.
409
+
410
+ ### `amicus provider`
411
+
412
+ Configure a local, self-hosted, OpenAI-compatible server — LM Studio, Ollama, vLLM, or anything else that speaks the `/v1/models` + chat-completions shape — as a first-class model source. Local providers cost **$0** marginal: no cloud key, no per-token bill.
413
+
414
+ ```bash
415
+ amicus provider add lmstudio --preset lmstudio # LM Studio, default port
416
+ amicus provider add ollama --preset ollama # Ollama, default port
417
+ amicus provider add vllm --preset vllm # vLLM, default port
418
+ amicus provider add my-remote --url http://127.0.0.1:9000/v1 --bearer <token>
419
+ amicus provider list
420
+ amicus provider test lmstudio
421
+ amicus provider remove lmstudio
422
+ ```
423
+
424
+ | Option | Description |
425
+ |---|---|
426
+ | `provider add <id> --preset ollama\|lmstudio\|vllm` | Add from a built-in preset. |
427
+ | `provider add <id> --url <baseURL>` | Add a custom endpoint instead of (or overriding) a preset. |
428
+ | `--bearer-env <VAR>` | Point at an env var that already holds the bearer (never written by this command). |
429
+ | `--bearer <token>` | Save `<token>` immediately, under a derived env-var name (e.g. `vllm-lab` → `VLLM_LAB_API_KEY`). Mutually exclusive with `--bearer-env`. |
430
+ | `--pricing-in <$/tok> --pricing-out <$/tok>` | Override the default `$0`/`$0` pricing (e.g. a metered self-host you actually pay for). |
431
+ | `provider list` | List configured providers: id, base URL, flavor, whether a bearer is set. |
432
+ | `provider test <id>` | Re-probe one provider; exit `0` if reachable, `1` if not. |
433
+ | `provider remove <id>` | Delete the config entry and its bearer (kept if another provider shares the same `--bearer-env`). |
434
+ | `--json` | Every subcommand supports it. |
435
+
436
+ **Presets** (always `127.0.0.1`, never `localhost` — some resolvers try `::1` first, which most local servers don't bind):
437
+
438
+ | Preset | Default base URL |
439
+ |---|---|
440
+ | `lmstudio` | `http://127.0.0.1:1234/v1` |
441
+ | `ollama` | `http://127.0.0.1:11434/v1` |
442
+ | `vllm` | `http://127.0.0.1:8000/v1` |
443
+
444
+ **`add` never fails just because the server is offline.** It validates and saves the config entry (and the bearer, if given) first, then does a best-effort 2s reachability probe: reachable prints the model count and offers the cost-aware default picker (see `amicus key` above); unreachable just warns and points you at `amicus provider test <id>` — the entry is saved either way, so starting the server later and re-testing is enough to pick it up. A provider id may not be `openrouter`, `google`, `openai`, `anthropic`, or `deepseek` (reserved for the built-in vendors), and must match `^[a-z][a-z0-9_-]{1,31}$`. If you also pass a plain `http://` `--url` to a non-loopback host with a bearer, `add` warns that the token would cross the network in cleartext.
445
+
446
+ `amicus setup`'s interactive wizard (readline and Electron) also offers to add a local server as one step of the normal setup flow — `amicus provider add` is the same feature from the command line.
447
+
448
+ **Running local models.** Two things cloud models don't require:
449
+
450
+ - **Load the model with enough context.** Amicus's agent prompt is ~26k tokens; a model loaded
451
+ with too small a context window will reject it. LM Studio's default (~16k) is not enough — load
452
+ with a larger context first, e.g. `lms load <model> --context-length 32768`, or set it in the
453
+ GUI before use. Ollama: set the model's context via a Modelfile (`num_ctx`).
454
+ - **The first token is slow.** The model has to prefill that ~26k-token prompt before it can
455
+ respond — 30–90s to first token on a cold local model is normal, not a hang. Amicus's
456
+ per-request timeout for local providers is 5 minutes to give this room.
457
+
458
+ ### `amicus init`
459
+
460
+ ```bash
461
+ amicus init # Register both Claude Code and Claude Desktop
462
+ amicus init --claude # Claude Code only
463
+ amicus init --desktop # Claude Desktop only
464
+ amicus init --json # Per-step status as JSON
465
+ ```
466
+
467
+ Re-runs the **same registration core** `npm install`'s postinstall runs: install both skills (`sidecar`, `second-opinion`) into `~/.claude/skills/`, register the `amicus` MCP server in Claude Code and/or Claude Desktop, and clean up any leftover legacy `sidecar` MCP entry. Useful when:
468
+
469
+ - A **plugin-channel install** (or any `--ignore-scripts` npm install) never ran the postinstall in the first place.
470
+ - The postinstall failed partway through.
471
+ - You deleted `~/.claude` state and want it rebuilt without reinstalling.
472
+
473
+ It never touches API keys, your default model, or Electron/engine provisioning — that's `amicus setup` and `amicus doctor --fix`. Each step (`skills`, `claudeCode`, `claudeDesktop`, `legacyMigration`) reports its own status independently — a broken Claude Desktop registration doesn't stop the Claude Code one from completing — and the command ends with a compact doctor summary. Exit code is `1` if any step genuinely failed, `0` otherwise.
474
+
475
+ ---
476
+
477
+ ## Observability (v4.3)
478
+
479
+ Every fan-out wave, council run, and session already writes durable JSON to disk
480
+ (`metadata.json`/`progress.json`/`wave.json`/council `run.json` — all additively
481
+ extended, never a breaking rename). v4.3 adds two more file-based surfaces on top
482
+ of that — no push, no IPC, no `fs.watch` anywhere, just polling, so behavior is
483
+ identical on Windows/macOS/Linux and over a network mount:
484
+
485
+ - an append-only **`events.jsonl`** milestone stream, one per wave dir / council-run
486
+ dir (`wave-started`, `leg-started`, `leg-fallback`, `leg-terminal`,
487
+ `wave-terminal`, and the council equivalents `run-started`/`stage-started`/
488
+ `stage-terminal`/`run-terminal`);
489
+ - the composed **live doc** — the same `amicus_status` rollup, stamped
490
+ `view:'live'` with per-leg read-time `usage`, while the run is still going.
491
+
492
+ ### Watch a run live from any terminal
493
+
494
+ ```bash
495
+ amicus watch <waveId|councilRunId|sessionId> # in-place refresh table on a TTY
496
+ amicus watch <id> --plain # milestone log lines (pipes/CI)
497
+ amicus watch <id> --json # NDJSON events + composed doc on change
498
+ amicus watch <id> --interval 0.5 # faster refresh (floor 0.5s; default 2s)
499
+ amicus watch <id> --project <path> # id lives in a different project
500
+ ```
501
+
502
+ `watch` reads only the data layer above — no attach, works from any process, on a
503
+ live or already-finished run — and resolves `<id>` with the same canonical
504
+ resolution the rest of the CLI uses (council pointer file first, else session
505
+ metadata: `type:'wave'` → wave, else solo). Exit code maps the run's terminal
506
+ state: `complete`→`0`, `partial`→`2`, else `1` — so `amicus watch <id> &&
507
+ next-step` works as a poor-man's wait. `--ui` opens the Council Workspace window
508
+ instead of the terminal renderer (`--json` is rejected) — see the next section.
509
+
510
+ ### Watch a council in a window (v4.4)
511
+
512
+ ```bash
513
+ amicus watch 1a2b3c4d --ui # Council Workspace window for that run
514
+ amicus watch --ui # run list for the current project
515
+ ```
516
+
517
+ Interactive-only (no `--json`; the terminal renderer above keeps `--json`) —
518
+ passing both fails fast rather than silently falling back to the render loop.
519
+ The window's **Fold** button writes the chair verdict back to this terminal's
520
+ output exactly like an interactive sidecar fold; closing without folding exits
521
+ `0`. Field-by-field detail — the run list, the live Seats table, the
522
+ adjudication matrix, dissent drill-in, blind mode, and the two verbs (Abort /
523
+ Fold) — is documented in **[docs/council.md's Council Workspace
524
+ section](./council.md#council-workspace-gui)**.
525
+
526
+ ### Stream a launching run (`--follow`)
527
+
528
+ ```bash
529
+ amicus fanout --models a,b,c --prompt-file p.md --follow
530
+ amicus council run --models a,b,c --prompt-file p.md --follow
531
+ amicus fanout ... --json --follow 2>progress.ndjson # machine-consumable events
532
+ ```
533
+
534
+ Milestone events stream to **stderr** as they happen; stdout's existing
535
+ `--json`/human contracts are byte-identical to a non-`--follow` run. On `fanout`,
536
+ `--follow` streams every leg's own start/fallback/terminal events alongside the
537
+ wave's own. **On `council run`, `--follow` streams the run's own lifecycle
538
+ (`run-started`, each stage's `stage-started`/`stage-terminal`, `run-terminal`) but
539
+ not the per-leg events inside a stage's internal fan-out sub-wave** — Stage-1's
540
+ review wave and Stage-2's judge wave launch through the same fan-out transport
541
+ internally, but the council engine doesn't thread `--follow` down into those
542
+ calls, so you see stage boundaries during a council run, not individual leg
543
+ starts/finishes within a stage.
544
+
545
+ ### Run a command / notify when a run finishes (`--on-complete`)
546
+
547
+ ```bash
548
+ amicus fanout ... --on-complete "notify-send 'council done'" # CLI: exec
549
+ ```
550
+
551
+ The command is user-authored on this invocation's own command line — the same
552
+ trust level as typing it into your shell; Amicus never sources hook commands from
553
+ config, briefings, or model output. The payload rides via **environment only**,
554
+ and it's ids/paths — never model-generated text — exactly these 8 variables:
555
+
556
+ | Variable | Value |
557
+ |---|---|
558
+ | `AMICUS_TASK_ID` | the wave / council-run id |
559
+ | `AMICUS_TYPE` | `wave` or `council-run` |
560
+ | `AMICUS_STATUS` | the run's terminal status |
561
+ | `AMICUS_EXIT_CODE` | the run's exit code |
562
+ | `AMICUS_RESULT_FILE` | path to the durable `wave.json`/`run.json` |
563
+ | `AMICUS_EVENTS_FILE` | path to that run's `events.jsonl` |
564
+ | `AMICUS_COST` | formatted total cost |
565
+ | `AMICUS_PROJECT` | the project directory |
566
+
567
+ A hook that wants model text reads the result file itself. The hook can never
568
+ change the run's own exit code, docs, or events — a non-zero exit or a timeout
569
+ (60s default, `AMICUS_HOOK_TIMEOUT_MS`) is logged as a warning only, never
570
+ propagated. **Over MCP, only `onComplete: "mcp-notify"` is accepted** — a
571
+ best-effort advisory notification through the MCP connection; `exec` is not
572
+ exposed to MCP callers at all. `amicus_wait` remains the reliable way to block
573
+ until a run finishes over MCP.
574
+
575
+ ### Never waste a run
576
+
577
+ ```bash
578
+ amicus fanout --retry-failed <waveId> # relaunch only the dead legs
579
+ amicus fanout --retry-failed <waveId> --models qwen # only retry that model's leg(s)
580
+ amicus fanout ... --fallback # opt-in cheaper-model substitution
581
+ amicus fanout ... --no-fallback # force it off even if config enables it
582
+ ```
583
+
584
+ `--retry-failed <waveId>` relaunches only that wave's terminal, non-complete legs
585
+ (`error`/`timeout`/`crashed`/`aborted`/`idle-timeout`) as a **new, linked wave**,
586
+ replaying each failed leg's own saved initial context for a byte-identical retry.
587
+ The original `wave.json` is never touched — linkage lives in `metadata.json`
588
+ (`retryOf` on the new wave, `retriedBy` on the original). It refuses while the
589
+ original wave is still running; `--models` filters which failed legs get retried.
590
+
591
+ `--fallback` substitutes the next-cheaper model in the chain, but **only when a
592
+ leg fails on a capacity signal — rate-limit or overload — never on timeout or auth
593
+ failure** (a slow model isn't a capacity problem, and `--retry-failed` already
594
+ covers it; auth/validation failures never substitute). Off by default;
595
+ `--fallback`/`--no-fallback` override the config's `fallbacks.enabled` for one
596
+ run. Substitution is per-leg, capped (2 attempts by default), and never silent: a
597
+ `leg-fallback` event lands in `events.jsonl`, the leg's doc gains an `attempts[]`
598
+ array, and the final doc gains a `fallback: {from, reason, attempts}` block.
599
+
600
+ ### See where every dollar went
601
+
602
+ `amicus spend` grew a full query and attribution surface in v4.3 — `--wave`,
603
+ `--council`, `--project`, `--model`, `--op`, `--failed`, `--group-by <dim>`,
604
+ `--rows` — see [`amicus spend`](#amicus-spend) above for the full flag table and
605
+ the wasted-view caveat. The read-only `amicus_spend` MCP tool mirrors the same
606
+ flags for MCP-only hosts.
607
+
608
+ ---
609
+
610
+ ## MCP Server
611
+
612
+ ```bash
613
+ # Auto-registered on npm install. Manual registration:
614
+ claude mcp add-json amicus '{"command":"npx","args":["-y","amicus@latest","mcp"]}' --scope user
615
+ ```
616
+
617
+ MCP tools: `amicus_start`, `amicus_status`, `amicus_wait`, `amicus_read`, `amicus_list`, `amicus_resume`, `amicus_continue`, `amicus_abort`, `amicus_setup`, `amicus_guide`, `amicus_fanout`, `amicus_council_tally`, `amicus_council_stats`, `amicus_verdict`, `amicus_council_run`, `amicus_spend`
618
+
619
+ The async pattern is **start → status → read**: `amicus_start` (or `amicus_fanout`) returns immediately, you poll `amicus_status`, then call `amicus_read` once the status is terminal.
620
+
621
+ `amicus_spend` is the read-only exception to that pattern: it's synchronous, takes the same filters as the [`amicus spend`](#amicus-spend) CLI command (`since`, `wave`, `council`, `filterProject`, `model`, `op`, `failed`, `groupBy`, `rows`), and returns the same versioned spend doc — unfenced, since spend docs are ids/numbers/paths only, never model-generated text. `since` takes the same `<N>d` format as the CLI's `--since` (e.g. `'7d'`). `filterProject` (not `project`) names the ledger row filter, since `project` is reserved on every MCP tool for the working-directory selector and the spend ledger is global, not per-project. Unlike the CLI, this tool never fetches the OpenRouter credit footer (`credit` is always `null`) — that's the one network-bound piece of `amicus spend`, deliberately excluded so a read-only MCP query never waits on the network.
622
+
623
+ Session statuses: `running`, `complete`, `aborted`, `crashed`, `error`, `timed-out`, `idle-timeout`
624
+
625
+ > Legacy `sidecar_*` tool names were removed entirely in v2.0.0 — the tool surface is `amicus_*` only, always. `AMICUS_LEGACY_ALIASES=1` (the v1.8.0 opt-in switch that used to restore the `sidecar_*` twins) is now a no-op: setting it on the MCP server entry changes nothing. See [docs/SHIMS.md](./SHIMS.md) for the removal record.
626
+
627
+ > The MCP server auto-detects whether it's running under Claude Code or Claude Desktop/Cowork (from the MCP `initialize` handshake) and passes the right `--client` value downstream — this drives context inclusion, MCP discovery, and session-dir resolution. If detection ever picks the wrong one, force it with `"env": {"AMICUS_MCP_CLIENT": "code-local"}` (or `code-web` / `cowork`) on the MCP server entry.
628
+
629
+ ---
630
+
631
+ ## OpenCode Agent Types
632
+
633
+ The `--agent` option controls which OpenCode agent drives the session:
634
+
635
+ | Agent | Description | Tool Access |
636
+ |-------|-------------|-------------|
637
+ | **Chat** | Interactive conversation | Reads freely, asks before writes/bash |
638
+ | **Build** | Full-access primary agent (headless default) | Read, write, bash, task |
639
+ | **Plan** | Read-only analysis | Read-only |
640
+
641
+ `--agent Chat` is interactive-only and incompatible with `--no-ui`. Custom agents defined in `~/.config/opencode/agents/` or `.opencode/agents/` are also supported.
642
+
643
+ ---
644
+
645
+ ## Context Sharing
646
+
647
+ When you `start` or `fanout`, Amicus automatically includes your recent Claude Code conversation history as context. Tune it:
648
+
649
+ - `--context-turns <N>` — max conversation turns to include (default 50).
650
+ - `--context-since <duration>` — time window (e.g. `2h`); overrides turns.
651
+ - `--context-max-tokens <N>` — cap the context size (default 80000).
652
+ - `--no-context` — skip parent history entirely (useful for `fanout` with a self-contained briefing).
653
+
654
+ ---
655
+
656
+ ## Process Self-Termination
657
+
658
+ Amicus processes automatically shut down after a period of inactivity. Default idle timeouts:
659
+
660
+ - **Headless mode**: 15 minutes
661
+ - **Interactive mode**: 60 minutes
662
+ - **Shared server**: 30 minutes
663
+
664
+ Set `AMICUS_IDLE_TIMEOUT=0` to disable self-termination entirely. For per-mode control use `AMICUS_IDLE_TIMEOUT_HEADLESS`, `AMICUS_IDLE_TIMEOUT_INTERACTIVE`, or `AMICUS_IDLE_TIMEOUT_SERVER` (all in minutes). See [docs/configuration.md](configuration.md#process-lifecycle) for the full table.
665
+
666
+ Legacy `SIDECAR_IDLE_TIMEOUT*` names were removed in v2.0.0 — use the `AMICUS_IDLE_TIMEOUT*` names above. See [docs/SHIMS.md](./SHIMS.md).
667
+
668
+ ---
669
+
670
+ ## JSON Output
671
+
672
+ With `--json`, Amicus emits stable, versioned documents on stdout.
673
+
674
+ **Run document** (single session):
675
+
676
+ ```json
677
+ {
678
+ "taskId": "...", "model": "...", "modelInput": "...", "agent": "...",
679
+ "status": "complete", "summary": "...", "error": null,
680
+ "createdAt": "...", "completedAt": "...", "durationMs": 0
681
+ }
682
+ ```
683
+
684
+ `modelInput` is the alias you passed; `model` is the resolved id. `status` is one of `complete | error | timeout | aborted | crashed | idle-timeout`.
685
+
686
+ **Exit codes:** `0` success · `2` partial wave · `1` error / hard failure · `130` SIGINT · `143` SIGTERM.
687
+
688
+ ---
689
+
690
+ ## Agentic Evals
691
+
692
+ ```bash
693
+ node evals/run_eval.js --eval-id 1 # Single eval
694
+ node evals/run_eval.js --all # All evals
695
+ node evals/run_eval.js --all --dry-run # Print commands only
696
+ node evals/run_eval.js --eval-id 1 --model opus # Override model
697
+ ```
698
+
699
+ See [evals/README.md](../evals/README.md) for the full eval system documentation.