amicus 1.8.0 → 1.9.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 -2
- package/CHANGELOG.md +148 -0
- package/README.md +43 -6
- package/commands/council.md +22 -0
- package/electron/close-guard.js +140 -0
- package/electron/fold.js +39 -2
- package/electron/main.js +12 -2
- package/package.json +3 -1
- package/skills/second-opinion/MODEL-NOTES.md +47 -1
- package/skills/second-opinion/SKILL.md +51 -10
- package/skills/sidecar/SKILL.md +51 -38
- package/src/cli-handlers-doctor.js +1 -1
- package/src/headless.js +2 -0
- package/src/mcp-server.js +29 -33
- package/src/sidecar/interactive-process.js +99 -0
- package/src/sidecar/interactive.js +1 -87
- package/src/sidecar/read.js +13 -5
- package/src/sidecar/session-utils.js +7 -2
- package/src/sidecar/setup-window.js +1 -1
- package/src/sidecar/start.js +2 -1
- package/src/utils/client-detect.js +118 -0
- package/src/utils/untrusted-fence.js +38 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christian Wagner"
|
|
7
7
|
},
|
|
8
8
|
"homepage": "https://bourbondog.github.io/amicus/",
|
|
9
9
|
"repository": "https://github.com/BourbonDog/amicus",
|
|
10
|
-
"bugs": "https://github.com/BourbonDog/amicus/issues",
|
|
11
10
|
"license": "MIT",
|
|
12
11
|
"keywords": [
|
|
13
12
|
"claude-code",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,154 @@ All notable changes to Amicus are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.9.0] - 2026-07-03
|
|
9
|
+
|
|
10
|
+
Engine pull-forwards, release-rail hardening, docs sync, and a new Council Review GitHub Action.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`/amicus:council` slash command and a `/amicus:sidecar <model> <prompt…>` argument surface.** `commands/council.md`
|
|
14
|
+
wraps the `second-opinion` skill end-to-end via `$ARGUMENTS`; `skills/sidecar/SKILL.md` gained an
|
|
15
|
+
`argument-hint` and a slash-invocation section binding `$1` (model alias, falling back to gemini for
|
|
16
|
+
non-model-looking input) and `$ARGUMENTS` (full prompt). **Slash commands are plugin-channel-only:**
|
|
17
|
+
`commands/` ships in the npm tarball (via `package.json`'s `files` array) but the npm/`install.sh`/
|
|
18
|
+
`install.ps1` postinstall flow never copies it into a Claude Code commands directory — only
|
|
19
|
+
`skills/sidecar` and `skills/second-opinion` are installed that way. npm/postinstall users do not get
|
|
20
|
+
`/amicus:council` or `/amicus:sidecar`; only plugin installs (`claude plugin install`) do. This is a
|
|
21
|
+
known, accepted gap, not a bug — carried forward from the 9.1 review as a note that must keep
|
|
22
|
+
reappearing in release-facing docs so it doesn't get silently "fixed" into a false claim.
|
|
23
|
+
- **MCP Registry wiring.** `package.json` gained `mcpName: "io.github.BourbonDog/amicus"`; `server.json`
|
|
24
|
+
(repo root) describes the stdio launch (`npx amicus mcp`). `.github/workflows/publish.yml` now publishes
|
|
25
|
+
to `registry.modelcontextprotocol.io` via `mcp-publisher`, authenticated over the same GitHub OIDC token
|
|
26
|
+
used for npm Trusted Publishing — no registry secret required. This fires automatically on every `v*` tag
|
|
27
|
+
push, strictly after `npm publish` succeeds (npm-side ownership validation reads the published
|
|
28
|
+
`package.json`). See `docs/DISTRIBUTION.md` §3 for the full flow, the release-order dependency on the
|
|
29
|
+
Phase 4 tool-surface de-bloat, and the manual recovery path if the registry publish fails in CI.
|
|
30
|
+
- **Marketplace submission runbook and preflight guard.** `docs/DISTRIBUTION.md` documents the
|
|
31
|
+
`claude-community` submission process (individual-author Console form route), the preflight checklist
|
|
32
|
+
(`claude plugin validate . --strict`, `claude --plugin-dir .` smoke test, `npm test`), and what the
|
|
33
|
+
Anthropic review pipeline is expected to check.
|
|
34
|
+
- **Council Review GitHub Action (v1).** A new reusable, label-gated workflow (`.github/workflows/council-review.yml`)
|
|
35
|
+
runs an `amicus fanout` review wave (default cheap bench `deepseek,gemini,glm`, cost- and time-bounded) over a
|
|
36
|
+
pull request's diff and posts one sticky synthesis comment with the individual reviews collapsed underneath. v1 is
|
|
37
|
+
fanout-only — independent reviews plus a one-leg synthesis, no adjudicated verdict (that needs the skill-orchestrated
|
|
38
|
+
Stage-2 cross-review, which a code-only pipeline can't produce; deferred to v2). Fork-safe and no-checkout by
|
|
39
|
+
design: PR code is never checked out or executed, only its diff (capped, via `gh pr diff`) is read; the job soft-skips
|
|
40
|
+
with a notice when `OPENROUTER_API_KEY` is unavailable (e.g. a fork PR without repo secrets) rather than failing the
|
|
41
|
+
check; every use of PR-controlled text (title/body) reaches the shell only through `env:` indirection, never inlined
|
|
42
|
+
into a `run:` script. Untrusted model output is neutralized before it enters the PR comment — case-insensitive,
|
|
43
|
+
whitespace-tolerant rules strip anything that could forge the sticky-comment marker (and hijack the next run's
|
|
44
|
+
update), forge the "not an adjudicated verdict" footer disclosure, or break out of the comment's own `<details>`
|
|
45
|
+
wrapper — and the real footer is echoed last, after all model text, so its position can't be forged. The label
|
|
46
|
+
gate (`council-review`) is enforced with a string-safe comparison (`format('{0}', inputs.require_label) == 'false'`)
|
|
47
|
+
to avoid a loose-equality bug where GitHub coerces an empty `pull_request`-event input to falsy and would otherwise
|
|
48
|
+
bypass the gate on every same-repo PR. **Inert by default:** the workflow only runs once a repo both adds the
|
|
49
|
+
`OPENROUTER_API_KEY` Actions secret and applies the `council-review` label to a PR — installing it does nothing on
|
|
50
|
+
its own. Locked by `tests/scripts/council-review-workflow.test.js`.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- **Every prose channel that returns another model's output is now wrapped in the
|
|
54
|
+
`<untrusted_sidecar_output>` fence** (`amicus_status`/`amicus_list` previews remain sanitized-and-truncated
|
|
55
|
+
instead, by design — `sanitizePreview()` in `src/sidecar/progress-fields.js` defangs fence/tag characters
|
|
56
|
+
and caps length so the full untrusted text is only ever reachable through the fenced `amicus_read` path),
|
|
57
|
+
extending the protection `amicus_read` summaries already had: MCP wave and conversation reads, CLI
|
|
58
|
+
`amicus read` summary/conversation/wave output, and the foreground summary echo after
|
|
59
|
+
`start`/`continue`/`resume`. This is visible in CLI output. JSON output (`--json`), metadata mode, and
|
|
60
|
+
on-disk artifacts (`wave.json`, `summary.md`, `conversation.jsonl`) are byte-identical to before — the
|
|
61
|
+
fence is applied only at output time, never at write time.
|
|
62
|
+
- Internal: `interactive.js`'s Electron process helpers extracted to `src/sidecar/interactive-process.js`
|
|
63
|
+
(size-gate headroom; no behavior change).
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
- **`plugin.json`'s unrecognized `bugs` field removed.** `claude plugin validate . --strict` now passes
|
|
67
|
+
clean (exit 0); it previously reported an unknown-field warning that `--strict` promotes to an error.
|
|
68
|
+
- **The Fold handoff is now documented operationally** (README + usage.md): the `[SIDECAR_FOLD]` stdout
|
|
69
|
+
block, where the summary lands (`summary.md`), and how the orchestrator reads it back (fenced, via
|
|
70
|
+
`amicus read`/`amicus_read`).
|
|
71
|
+
- **README↔usage.md drift corrected against the binary:** `amicus fanout` documents `--council`
|
|
72
|
+
(mutually exclusive with `--models`, exactly one required) in both files; `amicus list --status`
|
|
73
|
+
documents the full 7-value set (`running, complete, error, timed-out, aborted, crashed, idle-timeout`)
|
|
74
|
+
— note the `--json` schema's distinct `timeout` vocabulary is deliberately unchanged; fanout
|
|
75
|
+
`--session-id` support documented; `amicus status` gained real human and `--json` output examples;
|
|
76
|
+
`start --setup` documented as NOT relaxing the `--prompt`/`--prompt-file` requirement (with the exact
|
|
77
|
+
error string users see).
|
|
78
|
+
- **OpenRouter 402 recovery** added to the README troubleshooting table and docs/troubleshooting.md:
|
|
79
|
+
key save/validation never checks account balance, so the first council review / `start` / `fanout` call
|
|
80
|
+
can 402 (the `amicus council` subcommand itself is deterministic math and never calls a model) — recovery
|
|
81
|
+
via openrouter.ai/credits, `:free` models, and the non-blocking `amicus doctor` credit probe.
|
|
82
|
+
- docs/DISTRIBUTION.md's stale `/v0.1/` registry API path synced to `/v0/`.
|
|
83
|
+
- All of the above locked by `tests/docs-quick-sync.test.js` (17 pins).
|
|
84
|
+
- **Closing the GUI window no longer loses the session summary.** Closing without folding previously
|
|
85
|
+
destroyed the window immediately — the session finalized as `complete` with a placeholder summary, and
|
|
86
|
+
closing during an in-flight fold discarded the summary about to land. The window close is now intercepted
|
|
87
|
+
by a close guard (`electron/close-guard.js`): a close with no fold auto-triggers the same fold flow
|
|
88
|
+
(overlay + summary + `[SIDECAR_FOLD]` handoff) and then closes; a close during an in-flight fold lets it
|
|
89
|
+
finish — regardless of whether the fold was close-initiated or started from the toolbar/shortcut — instead
|
|
90
|
+
of falling through and destroying the window mid-summary; a failed or timed-out fold still closes the
|
|
91
|
+
window (the user is never trapped). This relies on `electron/fold.js` exposing a finer-grained
|
|
92
|
+
`isFolding()`/`hasCompleted()` split (a fold is "in flight" from the moment `triggerFold` is entered until
|
|
93
|
+
its `[SIDECAR_FOLD]` stdout write actually succeeds) alongside the original `hasFolded()`, so the guard can
|
|
94
|
+
tell "still running" apart from "actually done" — and a fold that settles without completing (including a
|
|
95
|
+
synchronous throw from the post-write nudge-overlay update, which the old code's `.catch()` couldn't
|
|
96
|
+
observe) still safely falls back to closing the window rather than leaving it permanently stuck open.
|
|
97
|
+
External abort remains immediate and never waits on a fold.
|
|
98
|
+
- **The MCP server no longer hardcodes `--client cowork`.** Under Claude Code — the primary caller — that
|
|
99
|
+
hardcode silently broke `includeContext:true` (empty context), parent-MCP discovery, and session-dir
|
|
100
|
+
resolution. The server now detects its caller from the MCP handshake's `clientInfo` (claude-code →
|
|
101
|
+
`code-local`; Claude Desktop/Cowork → `cowork`; unknown callers keep today's `cowork` behavior with a
|
|
102
|
+
one-time stderr notice) and threads the detected client through every spawn path and the in-process
|
|
103
|
+
shared-server path. A new `AMICUS_MCP_CLIENT` env var (set it in the MCP registration's `env` block)
|
|
104
|
+
explicitly overrides detection. One consequence: MCP-spawned GUI chat sessions under Claude Code now keep
|
|
105
|
+
the default SE-focused base prompt — `opencode-client.js`'s Cowork-specific general-purpose prompt swap
|
|
106
|
+
(`buildCoworkAgentPrompt()`) only fires when `options.client === 'cowork'`, which no longer matches a
|
|
107
|
+
Claude Code caller now that it's correctly tagged `code-local`.
|
|
108
|
+
- **Release-workflow re-runs now recover a half-published release instead of dead-ending.** A `publish.yml`
|
|
109
|
+
re-run after a post-`npm publish` failure previously died on `EPUBLISHCONFLICT` before ever reaching the
|
|
110
|
+
step that failed. Now the npm publish is skipped (loudly) when `amicus@<version>` is already live (E404
|
|
111
|
+
means not-published and proceeds; any other `npm view` error fails loud rather than skipping), a
|
|
112
|
+
tag↔`package.json` lockstep check fails fast before anything publishes, the MCP Registry publish is
|
|
113
|
+
skipped when the version is already registered (pre-check tolerates transport-level failures and falls
|
|
114
|
+
through to publishing), `mcp-publisher login github-oidc` gained the same 5×20s retry the publish call
|
|
115
|
+
already had, and `gh release create` is guarded by an existence check. `docs/DISTRIBUTION.md` §3 now
|
|
116
|
+
documents re-run as the primary recovery path, with the manual path as fallback. Locked by
|
|
117
|
+
`tests/scripts/publish-workflow.test.js`.
|
|
118
|
+
- **The `second-opinion` skill's frontmatter description no longer exceeds Claude Code's 1024-char cap.**
|
|
119
|
+
It was 1441 chars, so the router silently truncated the tail — which was the NOT-clause routing quick
|
|
120
|
+
single-model asks ("ask Gemini…", "what does DeepSeek think") to the `sidecar` skill. Rewritten to
|
|
121
|
+
988 chars with every trigger phrase and the NOT boundary intact (same fix pattern as the sidecar skill's
|
|
122
|
+
1.8.1 overhaul); locked by `tests/skill-second-opinion-docs.test.js`. Existing installs pick the fix up
|
|
123
|
+
when postinstall refreshes skill copies on the next upgrade.
|
|
124
|
+
|
|
125
|
+
## [1.8.1] - 2026-07-02
|
|
126
|
+
|
|
127
|
+
Docs & skills accuracy sprint from the Phase-8 whole-branch review — no engine changes. Every item fixed a claim
|
|
128
|
+
that actively misdirected Claude or users, plus one headless completion-state bugfix.
|
|
129
|
+
|
|
130
|
+
### Changed
|
|
131
|
+
- **`report.html` is now the default final council artifact**, and an inline verdict summary in chat is
|
|
132
|
+
MANDATORY at Stage 5 of the second-opinion skill.
|
|
133
|
+
- **The `sidecar` skill's frontmatter dropped the "second opinion from another model" trigger** — those requests
|
|
134
|
+
now route to the `second-opinion` skill instead.
|
|
135
|
+
- **MODEL-NOTES seed updated** with durable lessons from council runs 4-7 (new Grok/Kimi/Mistral/Claude-in-council
|
|
136
|
+
sections; shipped/local split defined). Existing installs: the machine-local copy is installed only-if-missing —
|
|
137
|
+
merge/refresh manually by pointing at the shipped file.
|
|
138
|
+
- **Council mechanics hardened:** mandatory no-tools preamble for judges and chair (plus scratch-cwd advice);
|
|
139
|
+
`--max-cost` / `--no-cost-gate` pass-through documented for repair and chair calls (the false solo-start
|
|
140
|
+
cost-gate exemption was removed); `--models` lists quoted in every example; current-date injection rule for
|
|
141
|
+
time-sensitive artifacts.
|
|
142
|
+
|
|
143
|
+
### Fixed
|
|
144
|
+
- **Plugin quick-start now states the truth:** plugin installs do not put `amicus` on `PATH`; use
|
|
145
|
+
`npx -y amicus@latest <cmd>`. Both skills gained an npx-fallback/transport rule.
|
|
146
|
+
- **README/usage now document `doctor`, `key`, and `council`;** troubleshooting leads with `amicus doctor`; the
|
|
147
|
+
false "`amicus list` shows active servers" claim is replaced with real `netstat`/`lsof` guidance.
|
|
148
|
+
- **Headless runs that finish via idle detection no longer write `status:"error"` / `reason:"Incomplete"` to
|
|
149
|
+
`metadata.json`.** The poll loop's two genuine idle-completion exits — the SDK-authoritative `session.status`
|
|
150
|
+
idle signal and the stable-poll activity heuristic (both gated on real output, F1 #16) — broke out of the loop
|
|
151
|
+
without setting `completed`, so `resolveTerminalState` fell through to error and poisoned `amicus_list` /
|
|
152
|
+
`amicus_status` / wave rollups for successful runs, while the stdout `--json` doc correctly said
|
|
153
|
+
`status:"complete"`. Both exits now mark the run completed, matching the fold-marker branch. Dead-server
|
|
154
|
+
classification is unchanged: the consecutive-poll-failure fast-exit (F4) and crash paths still report an error.
|
|
155
|
+
|
|
8
156
|
## [1.8.0] - 2026-07-02
|
|
9
157
|
|
|
10
158
|
### Added
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ Claude is the orchestrator. The council and chat skills run *on top of* the engi
|
|
|
57
57
|
|
|
58
58
|
## Quick start
|
|
59
59
|
|
|
60
|
-
**Install** — pick whichever fits
|
|
60
|
+
**Install** — pick whichever fits. Every path delivers the MCP server and both skills; the `amicus`/`am` CLI lands on your PATH with the **npm and install-script paths** (the plugin path runs the CLI on demand via `npx -y amicus@latest <command>`):
|
|
61
61
|
|
|
62
62
|
**As a Claude Code plugin** — the most native path if you use Claude Code:
|
|
63
63
|
|
|
@@ -67,7 +67,7 @@ Claude is the orchestrator. The council and chat skills run *on top of* the engi
|
|
|
67
67
|
/reload-plugins
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Claude Code registers the MCP server and both skills for you — nothing to configure. (The standalone Electron window is npm-only
|
|
70
|
+
Claude Code registers the MCP server and both skills for you — nothing to configure. (The plugin does not put `amicus` on your PATH — CLI calls go through `npx -y amicus@latest <command>`; the standalone Electron window is npm-only; and the first council/sidecar call downloads the OpenCode engine.)
|
|
71
71
|
|
|
72
72
|
**With the install script** — macOS, Linux, or Windows (needs [Node.js](https://nodejs.org) ≥ 18):
|
|
73
73
|
|
|
@@ -96,6 +96,8 @@ For the **npm** and **install-script** paths, a postinstall auto-configures ever
|
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
98
|
amicus setup
|
|
99
|
+
# plugin-only install (no CLI on PATH):
|
|
100
|
+
npx -y amicus@latest setup
|
|
99
101
|
```
|
|
100
102
|
|
|
101
103
|
This opens a graphical wizard:
|
|
@@ -113,7 +115,7 @@ This opens a graphical wizard:
|
|
|
113
115
|
|
|
114
116
|
> *council review this*
|
|
115
117
|
|
|
116
|
-
Claude prepares the material, recommends a bench of models, discloses the run shape and cost, and orchestrates the rest. You make the accept/deny calls at the end. (The `second-opinion` skill
|
|
118
|
+
Claude prepares the material, recommends a bench of models, discloses the run shape and cost, and orchestrates the rest. You make the accept/deny calls at the end. (The `second-opinion` skill is what teaches Claude to recognize this — if nothing happens, run `amicus doctor` (or `npx -y amicus@latest doctor`). npm/install-script installs place the skill at `~/.claude/skills/second-opinion/`; plugin installs keep it inside the plugin itself — check `/plugin` in Claude Code to confirm amicus is enabled.)
|
|
117
119
|
|
|
118
120
|
**Your first sidecar.** The sidecar is the lower-level path — you can invoke it by phrase through Claude too, but the CLI gives you the flags directly:
|
|
119
121
|
|
|
@@ -224,6 +226,8 @@ When you don't need a full council — just one other model's take — fork a co
|
|
|
224
226
|
|
|
225
227
|
**Headless (`--no-ui`):** the agent works autonomously and emits the fold summary when it finishes — ideal for bulk work like test generation or documentation. Pair with `--json` for a machine-readable run document.
|
|
226
228
|
|
|
229
|
+
**How the fold handoff actually works.** A fold is a summary handoff, not a live handback — once you fold, the sidecar model's context is gone. Mechanically: clicking **FOLD** asks the model for a structured summary, then writes it to the Electron process's stdout as a `[SIDECAR_FOLD]`-tagged block; the amicus runner that spawned Electron captures that stdout and persists it as the session's `summary.md` (under the session directory, alongside `metadata.json` and `conversation.jsonl`). Nothing pushes the result back to you automatically — your orchestrating agent picks it up on request, via `amicus read <taskId>` (CLI) or the `amicus_read` MCP tool. Either path returns the summary wrapped in an `<untrusted_sidecar_output>` fence, since it's prose from another model entering your context and should be treated as data, not instructions.
|
|
230
|
+
|
|
227
231
|
**Context sharing.** Your conversation history is passed automatically. Tune it:
|
|
228
232
|
|
|
229
233
|
- `--context-turns <N>` — max conversation turns to include (default 50).
|
|
@@ -261,6 +265,9 @@ amicus update
|
|
|
261
265
|
| `amicus read` | Output a session's summary / conversation / metadata. |
|
|
262
266
|
| `amicus status <id>` | One-shot status for a session or fan-out wave (human or `--json`; `--wave <id>` alternative spelling). |
|
|
263
267
|
| `amicus models` | List, search, refresh the catalog, or audit aliases. |
|
|
268
|
+
| `amicus doctor` | Diagnose your setup — keys, default model, catalog, aliases, OpenCode binary, Electron, skills, MCP registration, OpenRouter credit (`--json`; `--fix` self-heals what it can). |
|
|
269
|
+
| `amicus key` | Manage API keys non-interactively: `amicus key <provider> <key>` saves after live validation; `--remove`; bare `amicus key` lists providers. |
|
|
270
|
+
| `amicus council` | Council math: `tally <input.json>` (deterministic tiers + ledger append), `stats` (reviewer reliability), `report <verdict.json> [--md\|--html]`. |
|
|
264
271
|
| `amicus abort` | Abort a running session (or `--all`). |
|
|
265
272
|
| `amicus setup` | Configure default model, API keys, and aliases. |
|
|
266
273
|
| `amicus update` | Update to the latest version. |
|
|
@@ -296,7 +303,7 @@ The `am` alias is interchangeable with `amicus` everywhere.
|
|
|
296
303
|
| `--fold-shortcut <key>` | Customize the fold keyboard shortcut. | `Cmd/Ctrl+Shift+F` |
|
|
297
304
|
| `--opencode-port <port>` | Port override for the OpenCode server. | |
|
|
298
305
|
| `--session-dir <path>` | Explicit session-data directory. | |
|
|
299
|
-
| `--setup` | Force-open configuration before launching. | |
|
|
306
|
+
| `--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. | |
|
|
300
307
|
| `--no-validate-model` | Skip model-catalog validation before launch. | validation on |
|
|
301
308
|
|
|
302
309
|
> 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`.
|
|
@@ -304,7 +311,7 @@ The `am` alias is interchangeable with `amicus` everywhere.
|
|
|
304
311
|
### `amicus fanout` — same prompt, many models
|
|
305
312
|
|
|
306
313
|
```bash
|
|
307
|
-
amicus fanout --models gemini,deepseek,gpt --prompt "Review this design" --json
|
|
314
|
+
amicus fanout --models "gemini,deepseek,gpt" --prompt "Review this design" --json
|
|
308
315
|
```
|
|
309
316
|
|
|
310
317
|
Fanout runs one **headless wave**: every leg gets the **same** prompt (this is the shared-prompt model the council's review stages are built on). When all legs are terminal it prints **one** JSON wave document on stdout.
|
|
@@ -314,6 +321,7 @@ Fanout runs one **headless wave**: every leg gets the **same** prompt (this is t
|
|
|
314
321
|
- `--prompt <text>` / `--prompt-file <path>` — the shared briefing. `--prompt-file` avoids the ~32 KB Windows argument cap and is mutually exclusive with `--prompt`.
|
|
315
322
|
- `--wave-id <id>` — set the wave ID explicitly (leg IDs become `<id>-1..N`).
|
|
316
323
|
- `--json` — emit the wave document.
|
|
324
|
+
- `--session-id <id|current>` — session to pull shared context from (same as `start`; supported on `fanout` too).
|
|
317
325
|
- Shared per-leg knobs: `--agent`, `--thinking`, `--timeout`, `--summary-length`, `--no-context`, the `--context-*` flags, the `--mcp*` flags, `--no-validate-model`, `--cwd`.
|
|
318
326
|
- **Exit codes:** `0` all legs complete, `2` partial wave, `1` none complete / hard failure.
|
|
319
327
|
|
|
@@ -321,7 +329,8 @@ Fanout runs one **headless wave**: every leg gets the **same** prompt (this is t
|
|
|
321
329
|
|
|
322
330
|
```bash
|
|
323
331
|
amicus list # current project
|
|
324
|
-
amicus list --status running # filter by status
|
|
332
|
+
amicus list --status running # filter by status: running, complete, error,
|
|
333
|
+
# timed-out, aborted, crashed, idle-timeout
|
|
325
334
|
amicus list --all # all projects
|
|
326
335
|
amicus list --json # machine-readable
|
|
327
336
|
|
|
@@ -344,6 +353,32 @@ amicus setup --api-keys # open just the API-key window
|
|
|
344
353
|
amicus setup --add-alias fast=openrouter/google/gemini-2.5-flash # add/override one alias
|
|
345
354
|
```
|
|
346
355
|
|
|
356
|
+
**`amicus status <id>` output.** Human-readable:
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
$ amicus status demo123
|
|
360
|
+
Task: demo123
|
|
361
|
+
Status: complete (terminal)
|
|
362
|
+
Elapsed: 5m 0s
|
|
363
|
+
Model: google/gemini-2.5-flash
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
`--json`:
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
$ amicus status demo123 --json
|
|
370
|
+
{
|
|
371
|
+
"taskId": "demo123",
|
|
372
|
+
"status": "complete",
|
|
373
|
+
"elapsed": "5m 0s",
|
|
374
|
+
"version": "1.9.0",
|
|
375
|
+
"model": "google/gemini-2.5-flash",
|
|
376
|
+
"phase": "terminal"
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
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.
|
|
381
|
+
|
|
347
382
|
---
|
|
348
383
|
|
|
349
384
|
## Models
|
|
@@ -429,6 +464,7 @@ claude mcp add-json amicus '{"command":"npx","args":["-y","amicus@latest","mcp"]
|
|
|
429
464
|
| `AMICUS_CONFIG_DIR` | Override the config directory (keys, catalog, sessions). | `~/.config/amicus` |
|
|
430
465
|
| `AMICUS_FANOUT_MAX_LEGS` | Cap the number of legs in a single fanout wave; non-positive values fall back to 10. | `10` |
|
|
431
466
|
| `AMICUS_SHARED_SERVER` | When `1`, multiple MCP sessions share a single OpenCode Go process, eliminating cold-start latency. Set to `0` for per-process isolation or to diagnose a crash loop. | `1` |
|
|
467
|
+
| `AMICUS_MCP_CLIENT` | Override the auto-detected MCP caller (`code-local`, `code-web`, or `cowork`). Amicus detects Claude Code vs. Claude Desktop/Cowork from the MCP `initialize` handshake; set this only if detection picks the wrong one. Note: `code-web` requires an explicit `--session-dir` and is not usable for MCP-spawned sessions. | auto-detected |
|
|
432
468
|
|
|
433
469
|
**Headless poller tuning** (advanced — rarely needed)
|
|
434
470
|
|
|
@@ -505,6 +541,7 @@ Most Claude-adjacent tooling assumes macOS/Linux; Amicus doesn't.
|
|
|
505
541
|
| `npm install -g amicus` fails with `EEXIST: … claude-sidecar` | The old upstream `claude-sidecar` package is still installed globally; npm won't overwrite another package's bin shims | `npm uninstall -g claude-sidecar`, then `npm install -g amicus`. Your config and sessions carry over (legacy paths are still read). |
|
|
506
542
|
| Install fails partway, or `amicus doctor` reports the OpenCode binary "not found" | A **transient** error during the OpenCode engine's own postinstall (a spawn `ENOENT`, or an antivirus file-lock while it lays down its 11 per-platform binaries) can roll back the whole atomic install — retrying usually succeeds | Just re-run `npm install -g amicus`. If it still fails, clear the cache first: `npm cache clean --force && npm install -g amicus`. |
|
|
507
543
|
| `401` / auth error | API key missing, or the model prefix doesn't match the key you have | Run `amicus setup`; make sure the prefix (`openrouter/…` vs `google/…` vs `openai/…` vs `anthropic/…`) matches the credentials you configured. |
|
|
544
|
+
| `402` / "Payment Required" on first council review / `start` / `fanout` call | Your OpenRouter key is real but has no credit. Key save (`amicus key openrouter <key>` or the setup wizard's key step) only checks that the key **authenticates** — it doesn't check balance, so a zero-credit key saves cleanly and only fails later, on the first real model call. (The `amicus council` subcommand itself is deterministic math and never calls a model.) | Add credit at [openrouter.ai/credits](https://openrouter.ai/credits), **or** switch to a zero-cost council: `amicus setup` → option 2 (Free OpenRouter council) builds one from live `:free`-suffixed models and saves it as `councils.free` — then run `amicus fanout --council free …`. See "Free council (zero-cost)" under [The Council](#the-council) above. |
|
|
508
545
|
| Session not found | No session matches the given ID | Run `amicus list`, or omit `--session-id` to use the most recent. |
|
|
509
546
|
| No conversation history found | Project-path encoding | Check `~/.claude/projects/`; `/` and `_` in the project path are encoded as `-` in the directory name. |
|
|
510
547
|
| Headless run never finishes | Task is bigger than the default timeout | Raise it: `--timeout 30`. |
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run a structured multi-model LLM council review of the given material — wraps the second-opinion skill (independent reviews → anonymous cross-review → non-Claude chair verdict → accept/deny decisions).
|
|
3
|
+
argument-hint: [material, path, or URL] [analysis request + criteria]
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Run a full council review by invoking the `second-opinion` skill shipped in this
|
|
8
|
+
plugin (listed as `amicus:second-opinion`). Do not synthesize a verdict yourself —
|
|
9
|
+
the skill's chair model does that; you orchestrate.
|
|
10
|
+
|
|
11
|
+
Treat everything the user typed after the command as the review request:
|
|
12
|
+
|
|
13
|
+
$ARGUMENTS
|
|
14
|
+
|
|
15
|
+
Interpret it as three inputs: the **material** (inline text, a file path, or a URL),
|
|
16
|
+
the **analysis request**, and the **criteria**. If any of the three is missing or
|
|
17
|
+
ambiguous, ask for it before launching any model (the skill's Stage 0 covers this —
|
|
18
|
+
don't re-ask for what is already present).
|
|
19
|
+
|
|
20
|
+
Then follow the second-opinion skill end to end: Stage 0 intake/prep and run-folder
|
|
21
|
+
setup, council selection with a cost estimate and explicit user confirmation, the
|
|
22
|
+
three review waves, `amicus council tally`, and the accept/deny decision pass.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Close Guard — auto-fold on window close (backlog B01)
|
|
3
|
+
*
|
|
4
|
+
* `mainWindow.on('close', ...)` used to destroy the window immediately
|
|
5
|
+
* whenever no fold had run yet, silently discarding the session summary on
|
|
6
|
+
* the most common "I'm done, close the window" path (red X / Cmd|Ctrl+Q /
|
|
7
|
+
* window-all-closed). createCloseGuard() centralizes the decision so it can
|
|
8
|
+
* be unit-tested without booting Electron:
|
|
9
|
+
*
|
|
10
|
+
* - No fold attempted yet: prevent the destroy, invoke the SAME triggerFold
|
|
11
|
+
* closure (fold.js's own `folded` flag guards re-entrancy — this module
|
|
12
|
+
* never constructs a second fold handler). triggerFold's existing success
|
|
13
|
+
* path already closes the window itself; its failure path resets
|
|
14
|
+
* folded=false.
|
|
15
|
+
* - Fold already IN FLIGHT: prevent the destroy and let the in-flight fold
|
|
16
|
+
* finish and close the window on its own — regardless of who started it
|
|
17
|
+
* (a second user close during an auto-fold, or a close during a
|
|
18
|
+
* toolbar/shortcut-initiated fold, must both be blocked; a single
|
|
19
|
+
* `hasFolded()`-style flag that goes true at triggerFold ENTRY cannot
|
|
20
|
+
* distinguish in-flight from done, so this relies on fold.js's finer
|
|
21
|
+
* `isFolding()`/`hasCompleted()` split instead).
|
|
22
|
+
* - Fold already COMPLETED (the `[SIDECAR_FOLD]` stdout write actually
|
|
23
|
+
* succeeded): proceed exactly like the pre-existing behavior (no
|
|
24
|
+
* interception).
|
|
25
|
+
* - A close-initiated fold SETTLES WITHOUT COMPLETION — whether triggerFold
|
|
26
|
+
* rejects, or its outer catch swallows the error and resolves (e.g. the
|
|
27
|
+
* nudge-overlay executeJavaScript call throws synchronously on a
|
|
28
|
+
* destroyed webContents; fold.js's trailing `.catch()` there only guards
|
|
29
|
+
* promise rejection, not a sync throw) — fall back to the pre-existing
|
|
30
|
+
* destroy path so the user is never trapped with a window that can no
|
|
31
|
+
* longer be closed. A latch ensures the fallback destroy is not
|
|
32
|
+
* re-intercepted by this same guard, and a second rapid close click while
|
|
33
|
+
* a fold is in flight does not spawn another fold.
|
|
34
|
+
*
|
|
35
|
+
* This module is intentionally ignorant of external abort — `amicus abort` /
|
|
36
|
+
* MCP amicus_abort SIGTERMs the Electron child process directly
|
|
37
|
+
* (src/sidecar/interactive-abort.js killElectron), which is an OS-level kill
|
|
38
|
+
* of the whole process, not a call into any renderer 'close' handler. Node
|
|
39
|
+
* terminates immediately on SIGTERM unless the process itself installs a
|
|
40
|
+
* 'SIGTERM' listener; main.js installs none, so that teardown can never route
|
|
41
|
+
* through this guard.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {object} deps
|
|
46
|
+
* @param {() => boolean} deps.hasFolded - fold.js's original entry-to-settle
|
|
47
|
+
* flag (kept for API-compatibility with other hasFolded() consumers; not
|
|
48
|
+
* used to gate the close decision here since it can't distinguish
|
|
49
|
+
* in-flight from done).
|
|
50
|
+
* @param {() => boolean} [deps.isFolding] - Whether a fold is currently IN
|
|
51
|
+
* FLIGHT (entered but not yet completed). Falls back to `hasFolded()` when
|
|
52
|
+
* omitted, preserving the pre-fix (buggy) behavior for any caller that
|
|
53
|
+
* hasn't upgraded — but main.js always passes fold.js's real isFolding().
|
|
54
|
+
* @param {() => boolean} [deps.hasCompleted] - Whether the fold's
|
|
55
|
+
* `[SIDECAR_FOLD]` stdout write has actually succeeded. Falls back to
|
|
56
|
+
* `hasFolded()` when omitted.
|
|
57
|
+
* @param {(mainWindow: object, contentView: object) => Promise<void>} deps.triggerFold
|
|
58
|
+
* - The SAME fold.js closure used by the shortcut/toolbar/IPC paths.
|
|
59
|
+
* @returns {{ handleClose: (event: object, mainWindow: object, contentView: object) => void }}
|
|
60
|
+
*/
|
|
61
|
+
function createCloseGuard({ hasFolded, isFolding, hasCompleted, triggerFold }) {
|
|
62
|
+
const checkIsFolding = isFolding || hasFolded;
|
|
63
|
+
const checkHasCompleted = hasCompleted || hasFolded;
|
|
64
|
+
|
|
65
|
+
// Tracks whether THIS guard has already kicked off a close-initiated fold
|
|
66
|
+
// attempt (in flight or settled-but-not-yet-fallen-back). Separate from
|
|
67
|
+
// fold.js's own `folded` re-entrancy flag: this latch additionally prevents
|
|
68
|
+
// a second fold attempt or a preventDefault loop around the fallback
|
|
69
|
+
// destroy once the close-initiated fold has failed.
|
|
70
|
+
let closeFoldAttempted = false;
|
|
71
|
+
let fallbackFired = false;
|
|
72
|
+
|
|
73
|
+
function destroyIfPossible(mainWindow) {
|
|
74
|
+
if (fallbackFired) { return; }
|
|
75
|
+
fallbackFired = true;
|
|
76
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
77
|
+
mainWindow.destroy();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleClose(event, mainWindow, contentView) {
|
|
82
|
+
if (checkHasCompleted()) {
|
|
83
|
+
// Fold already completed — proceed exactly like the pre-existing
|
|
84
|
+
// behavior (no interception, no destroy call from the guard itself;
|
|
85
|
+
// Electron's default close-then-'closed' sequence runs normally).
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (fallbackFired) {
|
|
90
|
+
// The close-initiated fold already failed/timed out and the fallback
|
|
91
|
+
// destroy has already fired (or is in flight). Do not re-intercept —
|
|
92
|
+
// let this close event proceed so an already-invoked destroy()
|
|
93
|
+
// re-entering 'close', or a straggling user click, cannot loop.
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Not completed yet — either no fold has been attempted, or one is
|
|
98
|
+
// in flight (close-initiated or toolbar/shortcut-initiated). Either way
|
|
99
|
+
// the destroy must be prevented so an in-flight summary is never
|
|
100
|
+
// discarded out from under it.
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
|
|
103
|
+
if (checkIsFolding() || closeFoldAttempted) {
|
|
104
|
+
// A fold is already in flight (from this guard's own close-initiated
|
|
105
|
+
// attempt, or from the toolbar/shortcut path) — do not spawn a second
|
|
106
|
+
// one. Let it finish; its own success path closes the window, and if
|
|
107
|
+
// it settles without completing, the .then() below falls back to
|
|
108
|
+
// destroy so the window never gets permanently stuck open.
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
closeFoldAttempted = true;
|
|
112
|
+
|
|
113
|
+
Promise.resolve(triggerFold(mainWindow, contentView)).then(() => {
|
|
114
|
+
// triggerFold can RESOLVE without ever calling mainWindow.close() —
|
|
115
|
+
// its outer catch swallows failures (including a synchronous throw
|
|
116
|
+
// from the post-write nudge-overlay executeJavaScript call, which can
|
|
117
|
+
// land AFTER the stdout write already flipped completed=true) and
|
|
118
|
+
// returns normally instead of rejecting. fold.js's catch always resets
|
|
119
|
+
// its `folded` flag back to false in that path, and only in that path
|
|
120
|
+
// — the success path that actually reaches mainWindow.close() leaves
|
|
121
|
+
// `folded` true. So hasFolded() being false here is the reliable
|
|
122
|
+
// "settled without the window actually closing" signal, independent
|
|
123
|
+
// of hasCompleted() (the summary can be safely on stdout while the
|
|
124
|
+
// window itself never got closed). Skipping this check and trusting
|
|
125
|
+
// completion alone would leave the window with no further close
|
|
126
|
+
// handler able to fire — permanently open.
|
|
127
|
+
if (!hasFolded()) {
|
|
128
|
+
destroyIfPossible(mainWindow);
|
|
129
|
+
}
|
|
130
|
+
}, () => {
|
|
131
|
+
// Close-initiated fold rejected outright. Never trap the user: fall
|
|
132
|
+
// back to the original destroy path.
|
|
133
|
+
destroyIfPossible(mainWindow);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { handleClose };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { createCloseGuard };
|
package/electron/fold.js
CHANGED
|
@@ -19,14 +19,23 @@ const { tokenCss } = require('../src/design/tokens');
|
|
|
19
19
|
* @param {string} state.sessionId - OpenCode session ID
|
|
20
20
|
* @param {string} state.taskId - Sidecar task ID
|
|
21
21
|
* @param {number} state.port - OpenCode server port
|
|
22
|
-
* @returns {{ triggerFold: Function, hasFolded: Function }}
|
|
22
|
+
* @returns {{ triggerFold: Function, hasFolded: Function, isFolding: Function, hasCompleted: Function }}
|
|
23
23
|
*/
|
|
24
24
|
function createFoldHandler(state) {
|
|
25
|
+
// `folded` is set synchronously at triggerFold ENTRY and covers both
|
|
26
|
+
// "in flight" and "done" — this is `hasFolded()`'s existing external
|
|
27
|
+
// contract (main.js wires it straight into createCloseGuard's `hasFolded`
|
|
28
|
+
// dep) and must not change. `completed` is the finer-grained signal: it
|
|
29
|
+
// only flips true AFTER the `[SIDECAR_FOLD]` stdout write actually
|
|
30
|
+
// succeeds, so callers that need to distinguish "still running" from
|
|
31
|
+
// "actually done" (close-guard.js) use isFolding()/hasCompleted() instead.
|
|
25
32
|
let folded = false;
|
|
33
|
+
let completed = false;
|
|
26
34
|
|
|
27
35
|
async function triggerFold(mainWindow, contentView) {
|
|
28
36
|
if (folded) { return; }
|
|
29
37
|
folded = true;
|
|
38
|
+
completed = false;
|
|
30
39
|
|
|
31
40
|
// Show fold progress in toolbar and content overlay
|
|
32
41
|
showFoldOverlay(mainWindow, contentView);
|
|
@@ -54,6 +63,7 @@ function createFoldHandler(state) {
|
|
|
54
63
|
].join('\n');
|
|
55
64
|
|
|
56
65
|
process.stdout.write(output + '\n');
|
|
66
|
+
completed = true;
|
|
57
67
|
logger.info('Fold completed', { taskId: state.taskId });
|
|
58
68
|
|
|
59
69
|
// Show nudge overlay before closing
|
|
@@ -76,6 +86,17 @@ function createFoldHandler(state) {
|
|
|
76
86
|
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
77
87
|
} catch (err) {
|
|
78
88
|
logger.error('Fold failed', { error: err.message });
|
|
89
|
+
// Reset re-entrancy so a later close/shortcut/toolbar click can retry.
|
|
90
|
+
// NOTE: this can fire AFTER the `[SIDECAR_FOLD]` stdout write already
|
|
91
|
+
// succeeded (completed = true, set above) if the post-write nudge-overlay
|
|
92
|
+
// executeJavaScript call throws SYNCHRONOUSLY on a destroyed webContents
|
|
93
|
+
// (the trailing .catch() on that call only guards promise rejection, not
|
|
94
|
+
// a synchronous throw) — the summary is safely on stdout in that case,
|
|
95
|
+
// but the window was never closed by this run, so folded must still
|
|
96
|
+
// reset to let a fallback close proceed. completed is left as-is:
|
|
97
|
+
// close-guard.js checks it independently to decide whether a pending
|
|
98
|
+
// close can simply proceed (summary already landed) or must fall back
|
|
99
|
+
// to destroy (summary never made it out).
|
|
79
100
|
folded = false;
|
|
80
101
|
return;
|
|
81
102
|
}
|
|
@@ -93,7 +114,23 @@ function createFoldHandler(state) {
|
|
|
93
114
|
return folded;
|
|
94
115
|
}
|
|
95
116
|
|
|
96
|
-
|
|
117
|
+
// isFolding(): true only while a fold is IN FLIGHT (entered but not yet
|
|
118
|
+
// completed) — the signal close-guard.js needs to keep a close blocked
|
|
119
|
+
// regardless of who initiated the fold (close-initiated or
|
|
120
|
+
// toolbar/shortcut-initiated), since hasFolded() alone can't distinguish
|
|
121
|
+
// in-flight from done.
|
|
122
|
+
function isFolding() {
|
|
123
|
+
return folded && !completed;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// hasCompleted(): true once (and only once) the `[SIDECAR_FOLD]` stdout
|
|
127
|
+
// write has actually succeeded — the signal close-guard.js needs to know
|
|
128
|
+
// the summary is safely handed off and a close may proceed.
|
|
129
|
+
function hasCompleted() {
|
|
130
|
+
return completed;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { triggerFold, hasFolded, isFolding, hasCompleted };
|
|
97
134
|
}
|
|
98
135
|
|
|
99
136
|
/**
|
package/electron/main.js
CHANGED
|
@@ -18,6 +18,7 @@ const { logger } = require('../src/utils/logger');
|
|
|
18
18
|
const { getCompatEnv } = require('../src/utils/env-compat');
|
|
19
19
|
const { buildToolbarHTML, TOOLBAR_H, getBrandName } = require('./toolbar');
|
|
20
20
|
const { createFoldHandler } = require('./fold');
|
|
21
|
+
const { createCloseGuard } = require('./close-guard');
|
|
21
22
|
const { registerSetupHandlers } = require('./ipc-setup');
|
|
22
23
|
const { computeWindowPosition } = require('./window-position');
|
|
23
24
|
const { attachLoadFailsafe, buildLoadErrorHTML } = require('./load-failsafe');
|
|
@@ -90,6 +91,15 @@ const foldHandler = createFoldHandler({
|
|
|
90
91
|
taskId: TASK_ID,
|
|
91
92
|
port: OPENCODE_PORT
|
|
92
93
|
});
|
|
94
|
+
// Auto-fold on close (backlog B01): a user-initiated window close with no
|
|
95
|
+
// fold yet run must not silently discard the session summary. See
|
|
96
|
+
// close-guard.js for the full design (latch, fallback, abort isolation).
|
|
97
|
+
const closeGuard = createCloseGuard({
|
|
98
|
+
hasFolded: foldHandler.hasFolded,
|
|
99
|
+
isFolding: foldHandler.isFolding,
|
|
100
|
+
hasCompleted: foldHandler.hasCompleted,
|
|
101
|
+
triggerFold: foldHandler.triggerFold,
|
|
102
|
+
});
|
|
93
103
|
|
|
94
104
|
// ============================================================================
|
|
95
105
|
// Amicus Window (OpenCode + Toolbar)
|
|
@@ -284,8 +294,8 @@ function createAmicusWindow() {
|
|
|
284
294
|
}, 500);
|
|
285
295
|
}
|
|
286
296
|
|
|
287
|
-
mainWindow.on('close', () => {
|
|
288
|
-
|
|
297
|
+
mainWindow.on('close', (event) => {
|
|
298
|
+
closeGuard.handleClose(event, mainWindow, contentView);
|
|
289
299
|
});
|
|
290
300
|
mainWindow.on('closed', () => {
|
|
291
301
|
mainWindow = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
|
+
"mcpName": "io.github.BourbonDog/amicus",
|
|
4
5
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"claude",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"src/",
|
|
41
42
|
"electron/",
|
|
42
43
|
"skills/",
|
|
44
|
+
"commands/",
|
|
43
45
|
".claude-plugin/",
|
|
44
46
|
"CHANGELOG.md",
|
|
45
47
|
"scripts/postinstall.js",
|