amicus 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +162 -0
- package/README.md +17 -2
- package/bin/amicus.js +10 -0
- package/docs/DISTRIBUTION.md +234 -0
- package/docs/ROADMAP.md +226 -0
- package/docs/SHIMS.md +62 -0
- package/docs/architecture.md +104 -0
- package/docs/configuration.md +395 -0
- package/docs/council.md +970 -0
- package/docs/doc-system.md +92 -0
- package/docs/electron-testing.md +471 -0
- package/docs/jsdoc-setup.md +75 -0
- package/docs/opencode-integration.md +114 -0
- package/docs/publishing.md +60 -0
- package/docs/schemas.md +56 -0
- package/docs/testing.md +589 -0
- package/docs/troubleshooting.md +298 -0
- package/docs/usage.md +849 -0
- package/electron/fold.js +1 -1
- package/electron/main.js +4 -1
- package/electron/setup-ui-aliases.js +6 -6
- package/electron/workspace-ui/live-model.js +12 -1
- package/electron/workspace-ui/md-lite.js +52 -8
- package/electron/workspace-ui/workspace-app.js +39 -17
- package/electron/workspace-ui/workspace-matrix.js +46 -9
- package/electron/workspace-ui/workspace-panels.js +88 -19
- package/electron/workspace-ui/workspace-render.js +17 -1
- package/electron/workspace-ui/workspace-verbs.js +48 -2
- package/package.json +8 -3
- package/schemas/council-run-live.schema.json +1 -1
- package/schemas/council-run.schema.json +34 -0
- package/schemas/error.schema.json +1 -1
- package/schemas/event.schema.json +1 -1
- package/schemas/pack.schema.json +30 -0
- package/schemas/progress.schema.json +13 -1
- package/schemas/run-live.schema.json +1 -1
- package/schemas/run.schema.json +2 -1
- package/schemas/spend.schema.json +52 -4
- package/schemas/wave-live.schema.json +1 -1
- package/schemas/wave.schema.json +2 -1
- package/skills/second-opinion/SKILL.md +5 -0
- package/src/cli-handlers-council-run.js +51 -8
- package/src/cli-handlers-pack.js +238 -0
- package/src/cli-handlers-run.js +36 -8
- package/src/cli-handlers-spend.js +20 -2
- package/src/cli-handlers-template.js +53 -0
- package/src/cli-handlers-watch.js +11 -0
- package/src/cli.js +68 -5
- package/src/council/briefings-debate.js +27 -7
- package/src/council/briefings-stage2.js +155 -25
- package/src/council/briefings.js +24 -1
- package/src/council/findings.js +199 -9
- package/src/council/parse-stage2.js +10 -2
- package/src/council/presets-cli.js +23 -11
- package/src/council/report.js +19 -8
- package/src/council/run-assemble.js +42 -1
- package/src/council/run-budget.js +64 -11
- package/src/council/run-chair.js +4 -1
- package/src/council/run-debate.js +4 -2
- package/src/council/run-finalize.js +102 -0
- package/src/council/run-launch.js +29 -1
- package/src/council/run-server.js +248 -0
- package/src/council/run-stage2.js +118 -0
- package/src/council/run-stages.js +134 -110
- package/src/council/run-state.js +40 -1
- package/src/council/run.js +45 -47
- package/src/council/tally.js +10 -0
- package/src/headless.js +180 -7
- package/src/mcp-council-run.js +108 -4
- package/src/mcp-server.js +203 -7
- package/src/mcp-tools.js +15 -5
- package/src/observe/council-legs.js +60 -3
- package/src/observe/live-doc.js +18 -1
- package/src/observe/watch-render.js +4 -1
- package/src/pack/pack-cli.js +38 -0
- package/src/pack/pack-forward.js +96 -0
- package/src/pack/pack-resolve.js +297 -0
- package/src/pack/pack-store.js +130 -0
- package/src/pack/pack-validate.js +113 -0
- package/src/sidecar/child-sessions.js +1 -2
- package/src/sidecar/fanout-leg-fallback.js +69 -21
- package/src/sidecar/fanout-leg.js +6 -0
- package/src/sidecar/fanout-signals.js +61 -0
- package/src/sidecar/fanout-wave-io.js +75 -0
- package/src/sidecar/fanout.js +82 -74
- package/src/sidecar/progress-fields.js +26 -4
- package/src/sidecar/progress.js +42 -1
- package/src/sidecar/session-utils.js +23 -14
- package/src/sidecar/start.js +5 -4
- package/src/sidecar/workspace-auto-open.js +69 -0
- package/src/sidecar/workspace-window.js +46 -1
- package/src/spend-query.js +17 -5
- package/src/template/apply.js +88 -0
- package/src/template/render.js +86 -0
- package/src/template/store.js +106 -0
- package/src/utils/config.js +65 -25
- package/src/utils/error-doc.js +5 -0
- package/src/utils/lifecycle.js +37 -1
- package/src/utils/path-fence.js +39 -1
- package/src/utils/pricing.js +26 -10
- package/src/utils/result-schema-rebuild.js +1 -0
- package/src/utils/result-schema.js +8 -2
- package/src/utils/server-setup.js +79 -1
- package/src/utils/spend-ledger.js +24 -3
- package/src/workspace/artifact-guard.js +66 -7
- package/src/workspace/fold-format.js +33 -4
- package/src/workspace/live-normalize.js +28 -15
- package/src/workspace/run-detail.js +13 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Configuration Reference
|
|
2
|
+
|
|
3
|
+
`amicus setup` is the recommended way to configure Amicus. It opens a graphical wizard that validates your API keys live, lets you pick a default model from the live catalog, and saves everything to `~/.config/amicus/.env` (permissions `0600`). The environment variables below are for overrides and advanced tuning — most users only need the API keys section.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## API Keys
|
|
8
|
+
|
|
9
|
+
Amicus reads API keys from `~/.config/amicus/.env` and from `process.env`. Environment variables already set in your shell win; the `.env` file fills in anything unset; `auth.json` is the last fallback. Keys written by `amicus setup` are never silently overridden by something with higher priority.
|
|
10
|
+
|
|
11
|
+
| Variable | Purpose |
|
|
12
|
+
|----------|---------|
|
|
13
|
+
| `OPENROUTER_API_KEY` | OpenRouter — routes to any provider from a single key. |
|
|
14
|
+
| `GOOGLE_GENERATIVE_AI_API_KEY` | Direct Google Gemini access (bypasses OpenRouter). |
|
|
15
|
+
| `OPENAI_API_KEY` | Direct OpenAI access. |
|
|
16
|
+
| `ANTHROPIC_API_KEY` | Direct Anthropic access. |
|
|
17
|
+
| `DEEPSEEK_API_KEY` | Direct DeepSeek access. |
|
|
18
|
+
|
|
19
|
+
**Running models locally?** None of the above are required — `amicus provider` configures Ollama, LM Studio, vLLM, or any other OpenAI-compatible endpoint as an additional provider at $0 marginal cost, no entry in this table needed. See [docs/usage.md § `amicus provider`](./usage.md#amicus-provider) and the `providers` key under [Config file format](#config-file-format) below. Local models also need to be loaded with enough context to fit Amicus's ~26k-token agent prompt (~32k is a safe target) — see **Running local models** in the same `amicus provider` section for the exact commands.
|
|
20
|
+
|
|
21
|
+
**Bare `provider/model` is the canonical, policy-routed form.** Amicus routes it **direct-first**:
|
|
22
|
+
your direct provider key when one is configured, falling back to `OPENROUTER_API_KEY`
|
|
23
|
+
automatically when it isn't. `openrouter/provider/model` is an **explicit override** that always
|
|
24
|
+
forces OpenRouter, even when a direct key exists — reach for it deliberately, or for gateway-only
|
|
25
|
+
vendors with no direct integration (Qwen, Grok, Mistral, GLM, …), which require this form. See
|
|
26
|
+
[Routing](#routing) below for the full picture (`routing.prefer`, `--gateway`, the migration
|
|
27
|
+
notice).
|
|
28
|
+
|
|
29
|
+
| Model prefix | Credential consumed |
|
|
30
|
+
|-------------|-------------------|
|
|
31
|
+
| `provider/model` (bare, canonical) | Direct key for that vendor if configured, else `OPENROUTER_API_KEY` |
|
|
32
|
+
| `openrouter/provider/model` | `OPENROUTER_API_KEY`, always |
|
|
33
|
+
|
|
34
|
+
Per vendor, the bare form's direct key is: `google/...` → `GOOGLE_GENERATIVE_AI_API_KEY`,
|
|
35
|
+
`openai/...` → `OPENAI_API_KEY`, `anthropic/...` → `ANTHROPIC_API_KEY`, `deepseek/...` →
|
|
36
|
+
`DEEPSEEK_API_KEY`.
|
|
37
|
+
|
|
38
|
+
**Inherited provider base URLs.** Amicus does not define or read `*_BASE_URL` variables for the
|
|
39
|
+
hosted vendors above, but it does pass the whole environment through to the OpenCode engine, which
|
|
40
|
+
hands them to the underlying provider SDK. `ANTHROPIC_BASE_URL` is the one that bites: the SDK
|
|
41
|
+
appends only `/messages` to it, so it must include the `/v1` path segment. A value of
|
|
42
|
+
`https://api.anthropic.com` (**no** `/v1`) makes every direct `anthropic/…` model fail with a bare
|
|
43
|
+
`Not Found` at zero tokens, while the same models still work through OpenRouter. Some hosts set
|
|
44
|
+
this for you — a shell spawned by Claude Code inherits the `/v1`-less form. Either export
|
|
45
|
+
`https://api.anthropic.com/v1` or unset the variable. See
|
|
46
|
+
[troubleshooting § Every Direct Anthropic Model Fails with `"Not Found"`](./troubleshooting.md#every-direct-anthropic-model-fails-with-not-found).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Routing
|
|
51
|
+
|
|
52
|
+
`routing.prefer` in `config.json` sets the global default gateway policy; `--gateway` (CLI) or the
|
|
53
|
+
MCP `gateway` param overrides it per call.
|
|
54
|
+
|
|
55
|
+
| Setting | Values | Default | Effect |
|
|
56
|
+
|---------|--------|---------|--------|
|
|
57
|
+
| `routing.prefer` (config.json) | `"direct"` \| `"openrouter"` | `"direct"` | Global default: prefer the direct provider key when one exists, or always prefer OpenRouter. |
|
|
58
|
+
| `--gateway <mode>` (CLI, all commands that resolve a model) | `auto` \| `direct` \| `openrouter` | `auto` | Per-call override. `auto` means direct-first (honors `routing.prefer`); `direct`/`openrouter` force a specific gateway for this call and error if the required key is missing. |
|
|
59
|
+
| `gateway` (MCP: `amicus_start`, `amicus_continue`, `amicus_fanout`) | `"auto"` \| `"direct"` \| `"openrouter"` | `"auto"` | Same semantics as `--gateway`, for MCP callers. |
|
|
60
|
+
|
|
61
|
+
There is no `amicus setup` wizard step for `routing.prefer` yet — set it by hand-editing
|
|
62
|
+
`~/.config/amicus/config.json`'s top-level `routing.prefer` field (see the config.json example
|
|
63
|
+
below).
|
|
64
|
+
|
|
65
|
+
**One-time migration notice.** If you hold both an OpenRouter key and a direct key for a vendor,
|
|
66
|
+
the first launch that resolves a bare canonical id to that vendor via direct-first auto-routing
|
|
67
|
+
prints a one-time notice, e.g.:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Routing openai via direct API (previously OpenRouter).
|
|
71
|
+
Set routing.prefer: "openrouter" (or use --gateway openrouter) to restore.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The notice fires once per vendor (tracked in `config.json`'s `routing.migration_notified` map) —
|
|
75
|
+
not on every launch, and not when you explicitly chose the gateway with `--gateway`. Set
|
|
76
|
+
`routing.prefer: "openrouter"` (or pass `--gateway openrouter` per call) to keep routing everything
|
|
77
|
+
through OpenRouter as before.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Behavior
|
|
82
|
+
|
|
83
|
+
| Variable | Purpose | Default |
|
|
84
|
+
|----------|---------|---------|
|
|
85
|
+
| `LOG_LEVEL` | Log verbosity: `error` \| `warn` \| `info` \| `debug`. Keep `error` (the default) for clean LLM consumption; use `debug` to diagnose poll issues. | `error` |
|
|
86
|
+
| `AMICUS_CONFIG_DIR` | Override the entire config directory — keys, model catalog, session index. Useful for isolated test environments. | `~/.config/amicus` |
|
|
87
|
+
| `AMICUS_ENV_DIR` | Override just the `.env` file directory (keys only). The legacy `SIDECAR_ENV_DIR` name was removed in v2.0.0 — only `AMICUS_ENV_DIR` is read now. | `~/.config/amicus` (the config dir) |
|
|
88
|
+
| `AMICUS_FANOUT_MAX_LEGS` | Cap the number of concurrent legs in a single fanout wave. Protects against accidental runaway costs when `--models` is a long list. Non-positive or non-integer values fall back to the default. | `10` |
|
|
89
|
+
| `AMICUS_MCP_CLIENT` | Force the MCP server's `--client` value (`code-local`, `code-web`, or `cowork`) instead of auto-detecting it from the caller's MCP `initialize` handshake (`clientInfo.name`). Invalid values are ignored (with a warning) and detection proceeds normally. Note: `code-web` requires an explicit `--session-dir` and is not usable for MCP-spawned sessions. | auto-detected |
|
|
90
|
+
| `AMICUS_MAX_SESSIONS` | Maximum number of concurrent sessions the shared OpenCode server (`src/utils/shared-server.js`) will track before rejecting new ones. Renamed from `SIDECAR_MAX_SESSIONS` in v2.0.0. | `20` |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Headless Poller Tuning
|
|
95
|
+
|
|
96
|
+
These variables control the polling loop that drives headless sessions. The defaults are conservative and work for almost all workloads. You only need them if you are running against unusually slow or fast model endpoints, or if you are building tooling on top of Amicus and need tighter completion detection.
|
|
97
|
+
|
|
98
|
+
**Which of these accept `0`, and which ignore it.** The four `SETTLE` knobs in this table (`AMICUS_USAGE_SETTLE_POLLS`, `AMICUS_USAGE_SETTLE_INTERVAL_MS`, `AMICUS_USAGE_SETTLE_CALL_TIMEOUT_MS`, `AMICUS_TOOL_SETTLE_GRACE_MS`) read through `envNumber()` (`src/utils/env-num.js`), which honours an explicit, finite `0` — for those, `0` is a documented escape hatch and each row below says what it switches off. **Every other variable in this table reads through `Number(env) || default`, so `0` is falsy and silently falls back to the default** — there is no way to set them to zero, and that is deliberate: a `0` poll interval would busy-loop and a `0` stall threshold would kill every leg on its first poll. In both families a blank, missing or non-finite value falls back to the default.
|
|
99
|
+
|
|
100
|
+
| Variable | Purpose | Default |
|
|
101
|
+
|----------|---------|---------|
|
|
102
|
+
| `AMICUS_POLL_INTERVAL_MS` | Delay between poll cycles in milliseconds. Lower values detect completion faster but add more API calls; raise it if you see rate-limit warnings from the OpenCode server. | `2000` |
|
|
103
|
+
| `AMICUS_POLL_CALL_TIMEOUT_MS` | Per-poll `getMessages` call timeout in milliseconds. If a poll call hangs longer than this, it is abandoned and counted as a consecutive failure. | `30000` |
|
|
104
|
+
| `AMICUS_STABLE_FINISHED_POLLS` | Number of consecutive idle polls required after the SDK reports the session as `completed` before the headless runner exits. A small number (2) guards against a race where the assistant message is flagged complete but trailing content is still streaming. | `2` |
|
|
105
|
+
| `AMICUS_STABLE_IDLE_POLLS` | Number of consecutive idle polls required when no explicit completion signal is received (approximately 60 s at the 2 s default). This is the fallback heuristic for models or SDK versions that don't emit a clean completion event. | `30` |
|
|
106
|
+
| `AMICUS_MAX_CONSECUTIVE_POLL_FAILURES` | Consecutive poll failures before the headless runner bails. At the 2 s interval this is approximately 30 s. Prevents a dead server from burning the full session timeout on futile polls. | `15` |
|
|
107
|
+
| `AMICUS_TOOL_CALL_STALL_MS` | How long a tool call may sit pending with **no** result and no output growth before the leg is failed with `Tool call stalled: <tool>` and its OpenCode session aborted. This is the wedge guard: it targets a leg producing nothing at all, and it is skipped while a tool-settle deferral is active (`AMICUS_TOOL_SETTLE_GRACE_MS` owns that decision instead, and ends in a completion rather than a failure). **`0` is ignored** — it falls back to the default rather than disabling the guard, because a `0` threshold would kill every leg on its first poll. There is no way to switch this off; raise it if you legitimately run very long single tool calls. | `180000` |
|
|
108
|
+
| `AMICUS_USAGE_SETTLE_POLLS` | How many extra `getMessages` reads run **after** a leg has already finished, to catch provider usage/cost that lands milliseconds after the completion signal (measured: real paid legs losing their cost by 29 ms and 155 ms). The loop breaks early as soon as every assistant message carries usage, so the common case is one extra read. **Set to `0` to disable the reconciliation entirely** — legs then report whatever usage was present at completion, which can be `$0` on a leg that really did cost money. | `3` |
|
|
109
|
+
| `AMICUS_USAGE_SETTLE_INTERVAL_MS` | Delay between those settle reads. **`0` is honoured and means no delay** — the reads run back to back. It does **not** disable the reconciliation (that is `AMICUS_USAGE_SETTLE_POLLS=0`); it only removes the gap between attempts. | `400` |
|
|
110
|
+
| `AMICUS_USAGE_SETTLE_CALL_TIMEOUT_MS` | Per-call deadline for a settle read and for the child-session (subagent) spend walk. Deliberately much tighter than `AMICUS_POLL_CALL_TIMEOUT_MS`: the leg is already finished, so a hung read must not add 30 s × 3 to a run's wall time. The effective value is the **smaller** of this and `AMICUS_POLL_CALL_TIMEOUT_MS`, so raising it above that has no effect. **`0` is honoured and means no timer is armed at all** — a hung settle read or subtree walk would then wait indefinitely. | `5000` |
|
|
111
|
+
| `AMICUS_TOOL_SETTLE_GRACE_MS` | How long a completion signal may be deferred while a tool call has not reached a terminal status, so a leg is not declared complete while its session is still working and billing. On exceeding the grace the leg **completes anyway** — its partial output is kept and it is never failed — carrying `toolSettleTimedOut` on its result, its `metadata.json` and its terminal `progress.json`; its OpenCode session is then **aborted** so it stops billing for output nobody will read, and whether that abort landed is recorded as `toolSettleAborted`. Set to `0` to disable the deferral entirely (and with it the abort) — pre-v4.4 behaviour. | `300000` |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## GUI and Debug
|
|
116
|
+
|
|
117
|
+
| Variable | Purpose | Default |
|
|
118
|
+
|----------|---------|---------|
|
|
119
|
+
| `AMICUS_GUI_LOAD_TIMEOUT_MS` | Maximum wait in milliseconds for the Electron UI to load before the load-failsafe fires. If the OpenCode web UI fails to respond within this window, Amicus shows a load-error page instead of hanging invisibly. | `15000` |
|
|
120
|
+
| `AMICUS_DEBUG_PORT` | Chrome DevTools Protocol port for the Electron window. Increment (e.g. `9223`) to avoid conflicts with a running Chrome or another Amicus window. | `9222` |
|
|
121
|
+
| `AMICUS_MOCK_UPDATE` | Mock the update-notification state for UI development. Values: `available` \| `updating` \| `success` \| `error`. Has no effect outside development. | *(unset)* |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Process Lifecycle
|
|
126
|
+
|
|
127
|
+
Amicus processes self-terminate after a configurable idle period. The idle watchdog is active in all modes — headless, interactive, and shared-server.
|
|
128
|
+
|
|
129
|
+
| Variable | Purpose | Default |
|
|
130
|
+
|----------|---------|---------|
|
|
131
|
+
| `AMICUS_IDLE_TIMEOUT` | Blanket override for all modes (minutes; `0` = disabled). | *(mode default)* |
|
|
132
|
+
| `AMICUS_IDLE_TIMEOUT_HEADLESS` | Per-mode override for headless sessions (minutes). | `15` |
|
|
133
|
+
| `AMICUS_IDLE_TIMEOUT_INTERACTIVE` | Per-mode override for interactive sessions (minutes). | `60` |
|
|
134
|
+
| `AMICUS_IDLE_TIMEOUT_SERVER` | Per-mode override for shared-server sessions (minutes). | `30` |
|
|
135
|
+
|
|
136
|
+
Legacy `SIDECAR_IDLE_TIMEOUT*` names were removed in v2.0.0 — rename to the `AMICUS_IDLE_TIMEOUT*` forms above. See [docs/SHIMS.md](./SHIMS.md).
|
|
137
|
+
|
|
138
|
+
Set `AMICUS_IDLE_TIMEOUT=0` to disable self-termination entirely.
|
|
139
|
+
|
|
140
|
+
### Shared server
|
|
141
|
+
|
|
142
|
+
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.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Model Names Reference
|
|
147
|
+
|
|
148
|
+
Amicus does **not** ship a frozen model list. Aliases and validation resolve against a live catalog fetched from provider APIs and cached at `~/.config/amicus/model-catalog.json`.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
amicus models # list the full catalog
|
|
152
|
+
amicus models --search gemini # filter by substring (id + display name)
|
|
153
|
+
amicus models --refresh # force-fetch from provider APIs (bypasses the 24h TTL)
|
|
154
|
+
amicus models --check # audit your configured aliases against the catalog
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Catalog mechanics:**
|
|
158
|
+
|
|
159
|
+
- **24-hour TTL.** The catalog is fetched at most once per 24 hours. `amicus models --refresh` bypasses the TTL immediately.
|
|
160
|
+
- **Keyless fetch.** The initial catalog fetch works without an API key — provider model lists are public. Keys are only needed when you actually launch a session.
|
|
161
|
+
- **Floor-only refresh guard.** A background or offline `--refresh` can never clobber a good cache with an empty or truncated response. If the fresh fetch returns fewer models than the cached catalog, Amicus keeps the existing cache and logs a warning. This protects against transient network errors.
|
|
162
|
+
- **Catalog location.** `~/.config/amicus/model-catalog.json` — human-readable JSON; safe to inspect or delete (it rebuilds on next use).
|
|
163
|
+
|
|
164
|
+
**Spend ledger.** Every completed run/leg appends one row to `~/.config/amicus/spend-ledger.jsonl` (tokens + resolved cost). `amicus spend` reads it for a cross-run rollup; safe to delete (starts fresh, loses history only).
|
|
165
|
+
|
|
166
|
+
**Validation on launch.** `amicus start` and `amicus fanout` validate the model against the catalog before launching. For an explicit `--model` this is **blocking** (a typo'd model name fails fast with same-vendor suggestions); for a model inherited from a previous session via `continue`/`resume` it is **advisory** (a warning is printed but the session still starts). Skip catalog validation with `--no-validate-model`, or refresh the catalog with `amicus models --refresh`.
|
|
167
|
+
|
|
168
|
+
**`amicus models --check` in CI.** The command exits with the **number of stale aliases** (capped at 100) and prints replacement suggestions for each, so it integrates cleanly into a CI gate:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
amicus models --check && echo "aliases ok"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Model Aliases
|
|
177
|
+
|
|
178
|
+
Aliases are short names that resolve to full provider-prefixed model IDs. `amicus setup` seeds a curated default set (e.g. `gemini`, `gpt`, `opus`, `deepseek`) to the bare canonical form for direct-capable vendors. You add or override aliases with:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
amicus setup --add-alias fast=google/gemini-3.1-flash-lite-preview
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Aliases are stored in `~/.config/amicus/config.json`. The source of truth for what resolves on your machine is `amicus models`, not this document.
|
|
185
|
+
|
|
186
|
+
**Full-id passthrough.** You can always bypass aliases and specify a model by its full ID — bare `provider/model` (canonical, direct-first) or `openrouter/provider/model` (explicit force-OpenRouter override):
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
amicus start --model google/gemini-3-pro-preview --prompt "..." # bare canonical, direct-first
|
|
190
|
+
amicus start --model anthropic/claude-opus-4 --prompt "..." # bare canonical, direct-first
|
|
191
|
+
amicus start --model openrouter/google/gemini-3.1-flash-lite-preview --prompt "..." # explicit override
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Where things live
|
|
197
|
+
|
|
198
|
+
New to Amicus's disk footprint? This section maps everything it reads and writes, verified against
|
|
199
|
+
the source that actually writes it — not aspirational. If a claim here and the code ever disagree,
|
|
200
|
+
the code wins; file an issue.
|
|
201
|
+
|
|
202
|
+
### The config tree
|
|
203
|
+
|
|
204
|
+
Everything lives under `~/.config/amicus/` (`getConfigDir()` in `src/utils/config.js`):
|
|
205
|
+
|
|
206
|
+
- **Override:** set `AMICUS_CONFIG_DIR` to relocate the entire tree — keys, catalog, session index,
|
|
207
|
+
both ledgers. Useful for isolated test environments.
|
|
208
|
+
- **Legacy fallback — removed in v2.0.0:** `getConfigDir()` no longer falls back to
|
|
209
|
+
`~/.config/sidecar/` at all. Through v1.x, config data was auto-migrated forward on every run
|
|
210
|
+
(a one-time, non-destructive copy into `~/.config/amicus/` the first time it didn't exist yet),
|
|
211
|
+
so most installs already have everything in the new location. If you jumped straight from a
|
|
212
|
+
pre-rebrand install to v2.0.0 without ever running a v1.x build, copy `~/.config/sidecar/` to
|
|
213
|
+
`~/.config/amicus/` by hand. See [docs/SHIMS.md](./SHIMS.md).
|
|
214
|
+
|
|
215
|
+
| File | Written by | Contains |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `config.json` | `amicus setup` / `saveConfig()` (`src/utils/config.js`) | Top-level keys: `default` (your default model alias), `aliases` (your alias → `provider/model` map), `councils` (saved council presets, e.g. `councils.free`), `providers` (user-defined local / OpenAI-compatible providers added via `amicus provider add`, or by hand — id → `{type, baseURL, flavor, name?, apiKeyEnv?, pricing}`; see [`amicus provider`](./usage.md#amicus-provider)), `routing` (`prefer`: `"direct"` \| `"openrouter"`; `migration_notified`: per-vendor flags for the one-time direct-migration notice — see [Routing](#routing)). `0600` permissions. |
|
|
218
|
+
| `.env` | `amicus setup` / `amicus key` | API keys (`OPENROUTER_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `DEEPSEEK_API_KEY`). `0600` permissions. |
|
|
219
|
+
| `model-catalog.json` | `refreshCatalog()` (`src/utils/model-catalog.js`) | The cached provider model list, schema-versioned, with a **24-hour TTL**. Also carries refresh-outcome fields — `lastRefreshAttempt` and `lastRefreshError` — stamped on a *failed* refresh without touching the last-good `models`/`fetchedAt` (a bad fetch never clobbers a good cache). Human-readable JSON; safe to delete, it rebuilds on next use. |
|
|
220
|
+
| `sessions-index.json` | `session-index.js` (`recordSession`, written at session start) | A **global** map of `taskId → project path`, consulted only when a per-project session lookup misses (e.g. an MCP server whose cwd differs from where the session was created). Navigation aid only, never authoritative — a corrupt index degrades to "no entry," never a crash. |
|
|
221
|
+
| `council-ledger.jsonl` | `src/council/ledger.js` (`appendRun`), on every `council tally` | One row per council model per run — findings raised, severity breakdown, street-cred, conformance. Read back by `amicus council stats`. |
|
|
222
|
+
| `spend-ledger.jsonl` | `src/utils/spend-ledger.js` (`appendSpend`), new in Phase 16 | One row per completed run/leg — tokens + resolved cost. Read back by `amicus spend` for the cross-run rollup. Append is best-effort and can never fail the run it's recording; safe to delete (starts fresh, loses history only). |
|
|
223
|
+
| `packs/<name>.json` (v4.5) | `amicus pack save` (`src/pack/pack-store.js`) | One JSON file per saved policy pack — bench/model, chair/critic/lenses, options, and a briefing-template *reference*. Peer directory of `templates/` below. Safe to inspect, hand-edit, or delete individually; see [Policy packs](./usage.md#policy-packs). |
|
|
224
|
+
| `templates/<name>.md` (v4.5) | You, by hand (your editor is the manager) | User-authored briefing templates; a file here shadows a built-in of the same name. Amicus itself never writes into this directory — there is no `template save`/`rm`. See [Briefing templates](./usage.md#briefing-templates). |
|
|
225
|
+
|
|
226
|
+
**Tmp-file pattern.** Several writers (`model-catalog.json`, `sessions-index.json`, session
|
|
227
|
+
metadata) use an atomic write: a temp file named `.<target>.<pid>.<random>.tmp` is written
|
|
228
|
+
alongside the target, then renamed into place. A process killed between the write and the rename
|
|
229
|
+
leaves an orphaned `.tmp` file behind forever — harmless, but it accumulates. `amicus doctor --fix`
|
|
230
|
+
sweeps orphaned `sessions-index.json.*.tmp` files (only ones older than 60 seconds, so a live
|
|
231
|
+
writer's in-flight tmp file is never touched); `amicus doctor` (without `--fix`) just reports the
|
|
232
|
+
count.
|
|
233
|
+
|
|
234
|
+
### Session storage
|
|
235
|
+
|
|
236
|
+
Session data is split across two different roots — don't confuse them:
|
|
237
|
+
|
|
238
|
+
**1. The session root** (`getSessionRoot()` in `src/environment.js`) — resolved per client, mostly
|
|
239
|
+
relevant for how Claude Code/Cowork discovers *your current conversation's* context to share:
|
|
240
|
+
|
|
241
|
+
| Client | Root |
|
|
242
|
+
|---|---|
|
|
243
|
+
| `code-local` (default on macOS, or when a `DISPLAY`/`WAYLAND_DISPLAY` is present) | `~/.claude/projects/<encoded-cwd>` — the cwd is encoded by replacing `/`, `\`, the drive-letter colon, and `_` with `-` (matches Claude Code's own scheme). |
|
|
244
|
+
| `cowork` | Platform-specific: macOS `~/Library/Application Support/Claude/local-agent-mode-sessions`, Windows `%APPDATA%\Claude\local-agent-mode-sessions`, Linux `~/.config/Claude/local-agent-mode-sessions`. |
|
|
245
|
+
| `code-web` | No default — `--session-dir` is **required**; there's nothing to resolve. |
|
|
246
|
+
|
|
247
|
+
**2. Amicus's own per-session directories** — where the actual session data (metadata, conversation,
|
|
248
|
+
summaries) is written. These live **project-scoped**, under `.claude/amicus_sessions/<taskId>/` in
|
|
249
|
+
the project directory (`SESSIONS_DIR` in `src/session-manager.js`; not under the session root
|
|
250
|
+
above). The legacy `.claude/sidecar_sessions/` dual-read was removed in v2.0.0 — that directory is
|
|
251
|
+
no longer read at all. If you have session history there, rename `.claude/sidecar_sessions/` to
|
|
252
|
+
`.claude/amicus_sessions/` to make it visible to `amicus list`/`amicus read` again. See
|
|
253
|
+
[docs/SHIMS.md](./SHIMS.md).
|
|
254
|
+
|
|
255
|
+
Per-session directory contents:
|
|
256
|
+
|
|
257
|
+
| File | Written by | Notes |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| `metadata.json` | `createSession()` | Model, project, briefing, mode, thinking level, status. Atomic write. |
|
|
260
|
+
| `conversation.jsonl` | Appended as the session runs | One JSON line per message. |
|
|
261
|
+
| `progress.json` | Headless polling | Live progress snapshot (message count, latest activity, stage) — read by `amicus status` and the fanout wave heartbeat. |
|
|
262
|
+
| `summary.md` | `saveSummary()`, on fold/completion | The fold output — what `amicus read <id>` returns by default. |
|
|
263
|
+
| `subagents/<subagentId>/` | Sub-agent sessions | Same shape as a top-level session (its own `metadata.json` + `conversation.jsonl`). |
|
|
264
|
+
|
|
265
|
+
**Fanout waves.** A wave (`amicus fanout`) gets its own session dir at `<waveId>` (same
|
|
266
|
+
`amicus_sessions/` root); each leg is a full sibling session dir named `<waveId>-1` through
|
|
267
|
+
`<waveId>-N` (`deriveLegIds()` in `src/sidecar/fanout.js`). The wave-heartbeat display reads each
|
|
268
|
+
leg's `progress.json`/`conversation.jsonl` directly — nothing wave-specific is stored beyond the
|
|
269
|
+
per-leg session dirs themselves plus the wave's own `metadata.json` (type `wave`, `legs: [...]`).
|
|
270
|
+
|
|
271
|
+
### Log location + LOG_LEVEL
|
|
272
|
+
|
|
273
|
+
**Logs go to stderr only — there is no log file, anywhere, ever.** `src/utils/logger.js` writes every
|
|
274
|
+
entry as one JSON line via `console.error(...)`; stdout is reserved for command output (summaries,
|
|
275
|
+
`--json` documents). Setting `LOG_LEVEL=debug` does **not** create a file or a new destination — it
|
|
276
|
+
only lowers the filter threshold so `debug`-level entries (which are dropped by default) start
|
|
277
|
+
printing to the same stderr stream. If you want debug output captured, redirect it yourself:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
LOG_LEVEL=debug amicus start --model gemini --prompt "test" --no-ui 2> debug.log
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Levels, in order of decreasing verbosity: `debug` > `info` > `warn` > `error` (the default). Each
|
|
284
|
+
level includes everything above it.
|
|
285
|
+
|
|
286
|
+
### Config file format
|
|
287
|
+
|
|
288
|
+
`config.json`, commented (comments added for illustration — real JSON has none):
|
|
289
|
+
|
|
290
|
+
```jsonc
|
|
291
|
+
{
|
|
292
|
+
// Default alias, resolved via `aliases` below when --model is omitted.
|
|
293
|
+
"default": "gemini",
|
|
294
|
+
|
|
295
|
+
// Short name -> full model id. Bare `provider/model` (canonical) routes direct-first;
|
|
296
|
+
// `amicus setup` seeds direct-capable vendors this way automatically.
|
|
297
|
+
// `amicus setup --add-alias name=provider/model` adds more.
|
|
298
|
+
"aliases": {
|
|
299
|
+
"gemini": "google/gemini-3-pro-preview",
|
|
300
|
+
"gpt": "openai/gpt-5",
|
|
301
|
+
"opus": "anthropic/claude-opus-4",
|
|
302
|
+
"deepseek": "deepseek/deepseek-v3"
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// Named council member lists, e.g. seeded by the Free OpenRouter council
|
|
306
|
+
// wizard step. Run with `amicus fanout --council <name>`.
|
|
307
|
+
"councils": {
|
|
308
|
+
"free": ["free-gemini", "free-deepseek", "free-llama"]
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
// Gateway routing policy (see Routing above). `prefer` defaults to "direct"
|
|
312
|
+
// when this key is absent entirely. `migration_notified` is written
|
|
313
|
+
// automatically the first time the one-time direct-migration notice fires
|
|
314
|
+
// for a vendor — don't hand-edit it.
|
|
315
|
+
"routing": {
|
|
316
|
+
"prefer": "direct",
|
|
317
|
+
"migration_notified": { "openai": true }
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
// User-defined local / OpenAI-compatible providers (v4.2) — written by
|
|
321
|
+
// `amicus provider add`, or hand-edited. id -> normalized entry; `pricing`
|
|
322
|
+
// defaults to {prompt: 0, completion: 0} (the $0 tier) when omitted.
|
|
323
|
+
"providers": {
|
|
324
|
+
"lmstudio": { "type": "openai-compatible", "baseURL": "http://127.0.0.1:1234/v1", "flavor": "lmstudio" },
|
|
325
|
+
"ollama": { "type": "openai-compatible", "baseURL": "http://127.0.0.1:11434/v1", "flavor": "ollama" },
|
|
326
|
+
"vllm-lab": {
|
|
327
|
+
"type": "openai-compatible",
|
|
328
|
+
"baseURL": "http://127.0.0.1:8000/v1",
|
|
329
|
+
"flavor": "vllm",
|
|
330
|
+
"apiKeyEnv": "VLLM_LAB_API_KEY",
|
|
331
|
+
"pricing": { "prompt": 0.0000005, "completion": 0.0000015 }
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
// v4.5: opt out of auto-opening the Council Workspace window on an
|
|
336
|
+
// MCP-invoked `amicus_council_run` from Claude Code (local). Absent, null,
|
|
337
|
+
// or anything other than a literal `false` leaves auto-open ON — only an
|
|
338
|
+
// explicit `false` here disables it. See docs/council.md's Council
|
|
339
|
+
// Workspace section for the full decision order (the `ui` MCP param and
|
|
340
|
+
// the hard guards both take precedence over this key either way).
|
|
341
|
+
"workspace": {
|
|
342
|
+
"autoOpen": false
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
A provider id may not shadow one of the five built-in vendors — `openrouter`, `google`, `openai`, `anthropic`, `deepseek` are rejected. `apiKeyEnv` names an env var — the token itself is never written to `config.json`, only to `.env` (`0600`), by `amicus provider add --bearer` or `amicus key <id> <token>`.
|
|
348
|
+
|
|
349
|
+
An alias whose value is missing, `null`, or not a string is stripped on the next `saveConfig()`
|
|
350
|
+
call, with a notice printed to stderr — `config.json` never accumulates dead aliases silently.
|
|
351
|
+
|
|
352
|
+
**Policy pack precedence.** A saved [policy pack](./usage.md#policy-packs) sits between your flags
|
|
353
|
+
and whatever this repo's existing default logic already was for a given knob — the resolution
|
|
354
|
+
order everywhere a pack applies is **flag > pack > config default > built-in default.** A pack only
|
|
355
|
+
fills in a value you did not type explicitly on the command line; everything below that layer is
|
|
356
|
+
unchanged from before packs existed. `--gateway` is the clearest example with all four tiers live
|
|
357
|
+
today: an explicit `--gateway` wins, then a pack's `options.gateway`, then `routing.prefer` in
|
|
358
|
+
`config.json` (this section, above), then the hard-coded `"auto"` fallback. Most other pack-fillable
|
|
359
|
+
options (`timeout`, `maxCost`, `agent`, `thinking`, …) have no `config.json`-level default yet — just
|
|
360
|
+
a hard-coded built-in (`DEFAULTS` in `src/cli.js`) — so for those the chain is effectively **flag >
|
|
361
|
+
pack > built-in** today. See [Policy packs](./usage.md#policy-packs) for the full per-kind field
|
|
362
|
+
reference.
|
|
363
|
+
|
|
364
|
+
### Uninstall instructions
|
|
365
|
+
|
|
366
|
+
`npm uninstall -g amicus` removes the package and its bin shims. It does **not** clean up everything
|
|
367
|
+
`amicus` and its postinstall left behind — remove these by hand if you want a full uninstall:
|
|
368
|
+
|
|
369
|
+
| What | Where | Left behind because |
|
|
370
|
+
|---|---|---|
|
|
371
|
+
| MCP registration in Claude Code | `~/.claude.json` → `mcpServers.amicus` | Written by `scripts/postinstall.js`'s `registerClaudeCode()`; npm has no hook into another app's config file. Remove the `amicus` key under `mcpServers`, or run `claude mcp remove amicus`. |
|
|
372
|
+
| MCP registration in Claude Desktop / Cowork | `claude_desktop_config.json` → `mcpServers.amicus` (macOS: `~/Library/Application Support/Claude/`; Windows: `%APPDATA%\Claude\`; Linux: `~/.config/claude/`) | Written by `registerClaudeDesktop()`, same reasoning. Remove the `amicus` key under `mcpServers` by hand. |
|
|
373
|
+
| The chat skill | `~/.claude/skills/sidecar/` | Copied by `installSkill()`. Delete the directory. |
|
|
374
|
+
| The council skill | `~/.claude/skills/second-opinion/` | Copied by `installCouncilSkill()`. Delete the directory — note `MODEL-NOTES.md` inside it is **your** reviewer-reliability data (seeded once, never overwritten by updates), so back it up first if you want to keep it. |
|
|
375
|
+
| The entire config tree | `~/.config/amicus/` (keys, config, catalog cache, both ledgers, session index) | Never touched by npm at all — it's outside the package's install footprint by design (so an uninstall doesn't silently delete your API keys or history). Delete the directory yourself: `rm -rf ~/.config/amicus` (macOS/Linux) or `Remove-Item -Recurse -Force $HOME\.config\amicus` (PowerShell). |
|
|
376
|
+
| Per-project session data | `<project>/.claude/amicus_sessions/` in every project you ran Amicus from | Also outside npm's footprint — it lives inside *your* project directories, not the package. Delete per-project if you want it gone. |
|
|
377
|
+
|
|
378
|
+
If you plan to reinstall later, leaving `~/.config/amicus/` in place is the point — your keys,
|
|
379
|
+
aliases, and council presets carry over untouched.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Dependencies
|
|
384
|
+
|
|
385
|
+
| Package | Purpose |
|
|
386
|
+
|---------|---------|
|
|
387
|
+
| `electron` ^43.1.1 | Interactive Amicus window |
|
|
388
|
+
| `tiktoken` ^1.0.0 | Declared for future exact tokenization; **currently unused** — token sizing uses a length/4 heuristic (see `src/context.js`, `src/context-compression.js`). |
|
|
389
|
+
| `jest` ^29.0.0 | Testing framework |
|
|
390
|
+
| `eslint` ^8.0.0 | Code linting |
|
|
391
|
+
| `lint-staged` ^16.3.2 | Run linters on staged files |
|
|
392
|
+
|
|
393
|
+
`opencode-ai` (>=1.0.0) is the bundled LLM conversation engine — it is installed automatically as a postinstall step and does not need a separate `npm install`.
|
|
394
|
+
|
|
395
|
+
> **Legacy names.** Pre-rebrand `SIDECAR_*` environment variables were removed entirely in v2.0.0 — they are no longer read, with no warning. Rename to the `AMICUS_*` equivalents documented above. See [docs/SHIMS.md](./SHIMS.md) for the full removal record and rename table.
|