amicus 4.6.3 → 4.7.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.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +349 -0
- package/README.md +19 -7
- package/bin/amicus.js +31 -0
- package/docs/ROADMAP.md +143 -36
- package/docs/configuration.md +52 -4
- package/docs/council.md +65 -12
- package/docs/doc-system.md +8 -7
- package/docs/schemas.md +10 -1
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +33 -5
- package/docs/usage.md +64 -14
- package/electron/workspace-ui/index.html +3 -0
- package/electron/workspace-ui/live-model.js +52 -14
- package/electron/workspace-ui/workspace-app.js +14 -3
- package/electron/workspace-ui/workspace-lazy.js +233 -0
- package/electron/workspace-ui/workspace-matrix.js +12 -1
- package/electron/workspace-ui/workspace-panels.js +24 -171
- package/electron/workspace-ui/workspace-render.js +6 -2
- package/electron/workspace-ui/workspace-seats.js +68 -0
- package/electron/workspace-ui/workspace.css +6 -0
- package/package.json +8 -4
- package/schemas/council-run.schema.json +1 -0
- package/schemas/council-stats.schema.json +9 -1
- package/schemas/run.schema.json +2 -1
- package/schemas/spend.schema.json +1 -1
- package/schemas/wave.schema.json +2 -1
- package/scripts/postinstall.js +6 -3
- package/scripts/setup-hooks.js +49 -3
- package/skills/second-opinion/MANUAL-ORCHESTRATION.md +12 -0
- package/skills/second-opinion/MODEL-NOTES.md +5 -4
- package/skills/sidecar/SKILL.md +9 -2
- package/src/cli-council-run-bench.js +86 -0
- package/src/cli-handlers-council-run.js +65 -81
- package/src/cli-handlers-council.js +17 -5
- package/src/cli-handlers-fanout.js +179 -0
- package/src/cli-handlers-pack.js +24 -10
- package/src/cli-handlers-resume-continue.js +20 -0
- package/src/cli-handlers-run.js +19 -161
- package/src/cli-template-args.js +48 -0
- package/src/cli.js +39 -46
- package/src/council/debate.js +89 -10
- package/src/council/ledger.js +72 -11
- package/src/council/report.js +17 -6
- package/src/council/run-assemble.js +15 -3
- package/src/council/run-budget.js +2 -2
- package/src/council/run-chair.js +61 -5
- package/src/council/run-debate.js +51 -67
- package/src/council/run-launch.js +20 -2
- package/src/council/run-retry.js +17 -2
- package/src/council/run-stage1-launch.js +94 -0
- package/src/council/run-stage2.js +25 -4
- package/src/council/run-stages.js +79 -86
- package/src/council/run-state.js +10 -2
- package/src/council/run.js +26 -2
- package/src/council/tally.js +6 -2
- package/src/headless.js +69 -6
- package/src/mcp-council-awareness.js +1 -0
- package/src/mcp-council-bench.js +4 -0
- package/src/mcp-council-run.js +10 -0
- package/src/mcp-server.js +114 -54
- package/src/mcp-tools.js +12 -5
- package/src/pack/pack-cli.js +1 -1
- package/src/pack/pack-forward.js +12 -4
- package/src/pack/pack-resolve.js +3 -0
- package/src/pack/pack-store.js +20 -3
- package/src/pack/pack-validate.js +5 -1
- package/src/sidecar/budget.js +38 -4
- package/src/sidecar/continue.js +8 -23
- package/src/sidecar/fanout-budget.js +1 -2
- package/src/sidecar/fanout-leg-fallback.js +7 -3
- package/src/sidecar/fanout-retry.js +15 -3
- package/src/sidecar/fanout-wave-io.js +13 -1
- package/src/sidecar/fanout.js +11 -9
- package/src/sidecar/list-limit.js +50 -0
- package/src/sidecar/list-search.js +69 -0
- package/src/sidecar/read.js +90 -5
- package/src/sidecar/reopen-spend.js +32 -0
- package/src/sidecar/resume.js +1 -1
- package/src/sidecar/start-metadata.js +58 -0
- package/src/sidecar/start.js +8 -43
- package/src/sidecar/workspace-auto-open.js +2 -2
- package/src/spend-query.js +2 -1
- package/src/template/apply.js +7 -4
- package/src/template/render.js +6 -2
- package/src/template/store.js +1 -1
- package/src/utils/cli-preflight.js +27 -1
- package/src/utils/config.js +15 -0
- package/src/utils/doctor-engine-check.js +32 -0
- package/src/utils/engine-install-scan.js +98 -15
- package/src/utils/engine-repair.js +96 -2
- package/src/utils/remediation-hints.js +29 -0
- package/src/utils/result-schema-rebuild.js +1 -0
- package/src/utils/result-schema.js +6 -1
- package/src/utils/session-index-tmp-sweep.js +18 -3
- package/src/utils/session-index.js +1 -0
- package/src/utils/session-metadata-tmp-sweep.js +24 -4
- package/src/utils/spend-ledger.js +11 -4
- package/src/utils/validators.js +16 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christian Wagner"
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,355 @@ All notable changes to Amicus are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [4.7.1] - 2026-08-09
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`continue`, `resume`, and `--retry-failed` now inherit the parent session's/wave's tag,
|
|
13
|
+
instead of dropping it.** `amicus list` now shows a TAG for continuations (they no longer group
|
|
14
|
+
under `(unattributed)` for `--group-by tag`), and `--tag` is now **rejected** on `continue` and
|
|
15
|
+
`resume` — the tag can only come from the parent, it cannot be overridden. This deletes a
|
|
16
|
+
documented limitation, not a bug: `docs/usage.md` carried a paragraph headed "Known limitation: a
|
|
17
|
+
tag is not inherited", and v4.7.0's own release notes called the gap "future work, not
|
|
18
|
+
oversights." That limitation is now gone.
|
|
19
|
+
- **`opencode-ai` and `@opencode-ai/sdk` are pinned to exactly `1.18.15`** (previously `^1.2.20` /
|
|
20
|
+
`^1.1.36`). Dev and CI now run on the same engine version end users resolve, so this is the
|
|
21
|
+
first release whose test suite actually ran against the engine users get. Honest scope: the pin
|
|
22
|
+
makes the *resolved* engine a pure function of the amicus version — it does nothing for a user
|
|
23
|
+
whose installed amicus version hasn't moved, and it does not force any existing npx cache to
|
|
24
|
+
re-resolve (that mechanism was investigated for #133 and found not to exist).
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **The no-output backstop message now reports what it observed, not what it guessed.** The old
|
|
29
|
+
three-line message asserted "likely a listed-but-not-serving model or a dead endpoint" with no
|
|
30
|
+
evidence behind it; it now names only what the deadline mechanism actually saw, and names
|
|
31
|
+
`AMICUS_NO_OUTPUT_BACKSTOP_MS` as the live governing window only when it is one, calling it out
|
|
32
|
+
as an overridden default otherwise.
|
|
33
|
+
- **The no-output backstop window now doubles on a Stage-1 retry, clamped to the leg timeout, so
|
|
34
|
+
retries can now heal a class of no-output failure that previously required a manual rerun.**
|
|
35
|
+
This is not a fix for the backstop class itself — only Stage-1 bench/critic/lens units retry;
|
|
36
|
+
judges, chair, chair-repair, and debate legs do not, and can still hit the backstop with no
|
|
37
|
+
automatic recovery.
|
|
38
|
+
- **`doctor` now reports engine version skew between installs instead of only grading on
|
|
39
|
+
presence.** A stale npx-cached copy of `opencode-ai` next to a newer global install now surfaces
|
|
40
|
+
as a WARN.
|
|
41
|
+
- **`npm root -g` now resolves on Windows** (it previously threw `ENOENT`/`EINVAL`, making the
|
|
42
|
+
global amicus install invisible to `doctor`). This also un-blinds `doctor --fix`'s donor
|
|
43
|
+
selection, which depends on seeing that global install to pick a healthy engine to copy from.
|
|
44
|
+
- **`npm test` no longer fails intermittently with `ENOENT: … src\__sizecheck_tmp__.js`.**
|
|
45
|
+
`tests/scripts/check-file-sizes.test.js` writes a real `src/__sizecheck_tmp__.js` and unlinks it a
|
|
46
|
+
few milliseconds later. It cannot move that fixture to a tmpdir: `checkAllTracked()` filters its
|
|
47
|
+
input through the anchored `src/**/*.js` include glob and only then resolves against
|
|
48
|
+
`process.cwd()`, so a tmpdir path is dropped before the read and the test would assert on an empty
|
|
49
|
+
violation list. Three suites walk `src/` in parallel jest workers, and a directory listing taken
|
|
50
|
+
while that file existed followed by a read issued after the unlink threw ENOENT — killing
|
|
51
|
+
`no-phantom-dependencies.test.js` during collection ("Test suite failed to run") on roughly 1 full
|
|
52
|
+
run in 2, with `cli-template-args.test.js` and `council/degrade-invariant.test.js` exposed
|
|
53
|
+
identically (both confirmed failing under a reproduction that widens the collision window). All
|
|
54
|
+
three now read through the new `tests/helpers/read-if-present.js`, which skips a file that vanished
|
|
55
|
+
mid-walk and still throws on any non-ENOENT failure, so an unreadable source file stays loud.
|
|
56
|
+
Pre-existing — reproduces at 0fe6128, unrelated to the graphify integration.
|
|
57
|
+
|
|
58
|
+
### Internal
|
|
59
|
+
|
|
60
|
+
- **`finalizeSpendForReopen` extracted to `src/sidecar/reopen-spend.js`**, out of
|
|
61
|
+
`sidecar/continue.js`, to keep that file under the 300-line gate; `resume.js` was already
|
|
62
|
+
reaching across into `continue.js` for it, so the shared home is the honest one.
|
|
63
|
+
- **Deleted three unreachable helpers from `scripts/validate-docs.js`** (dead code with no call
|
|
64
|
+
site remaining after earlier `--check` work landed).
|
|
65
|
+
- **Three doc/marker-freshness gates, all jest-enforced — one genuinely new, two hardened.**
|
|
66
|
+
`tests/docs-command-coverage.test.js` and the anchor-link gate
|
|
67
|
+
(`tests/docs-council-toc-anchors.test.js`) both already existed and already ran in CI; this
|
|
68
|
+
branch hardens them (the former now derives its command list from `bin/amicus.js`'s switch
|
|
69
|
+
instead of hardcoding five entries, the latter generalizes from one file to sixteen). Only the
|
|
70
|
+
CLAUDE.md AUTO-marker/cross-link freshness gate in this changelog entry's own commit is genuinely
|
|
71
|
+
new — `generate-docs --check` had never run in CI before — so a stale `CLAUDE.md` can no longer
|
|
72
|
+
pass CI silently.
|
|
73
|
+
|
|
74
|
+
## [4.7.0] - 2026-08-08
|
|
75
|
+
|
|
76
|
+
**"The count is the count."** Every number Amicus shows you is the number — what a council cost,
|
|
77
|
+
which legs ran, and which model earned the credit. Shipped across ten `v4.7-*` PRs — PR0
|
|
78
|
+
(extractions) through PR7, plus the PR3 riders and a closing documentation pass: the `runStats`
|
|
79
|
+
completeness half of CA-4, the GOA-7 ledger prerequisite, F8 session/wave tagging with `--search`,
|
|
80
|
+
and four correction sweeps.
|
|
81
|
+
|
|
82
|
+
### CI
|
|
83
|
+
|
|
84
|
+
- **The macOS/node-24 unit leg's `--workerIdleMemoryLimit` drops from 1GB to 512MB.** That leg
|
|
85
|
+
segfaulted a jest worker again on 2026-08-07 (PR #126) — killing
|
|
86
|
+
`tests/utils/gateway-route-audit.test.js` while 6791 tests passed and 0 failed, with a green
|
|
87
|
+
rerun — which was the first such hit *with* the 1GB ceiling in force, so 1GB does not bound the
|
|
88
|
+
growth on this runner. Halving recycles a worker sooner, at a small wall-clock cost on a leg
|
|
89
|
+
already mid-pack (~3m59s vs ubuntu's ~3m58s). Three pins in
|
|
90
|
+
`tests/scripts/ci-workflow.test.js` now guard the mitigation, which previously had none: it
|
|
91
|
+
produces no assertion failure when it works and none when it is deleted, so nothing else in the
|
|
92
|
+
repo would notice its loss. If a fifth hit lands at 512MB, the next lever is `--maxWorkers` on
|
|
93
|
+
that leg — fewer concurrent heaps — not a smaller idle ceiling.
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
|
|
97
|
+
- **`runStats` gains a row for every paid launch, not just one per requested seat** (v4.7 CA-4,
|
|
98
|
+
the row-per-launch design — closes the chair-cost accounting gap). Three new roles cover legs
|
|
99
|
+
that were billed but never rowed: `chair-attempt` (a failed ch1–ch3 chair launch), `repair`
|
|
100
|
+
(a Stage-1 `-p`, Stage-2 `-q`, chair-ch4, or debate-born `-d<N>r`/`-rv-…r` solo — a failed
|
|
101
|
+
defense or re-vote repair), and `superseded` (a first leg a later attempt replaced — an SL-2
|
|
102
|
+
retry or a debate repair); `wasChair` is always `false` on these. Every dead seat/critic/lens
|
|
103
|
+
with no recovery, and a chair walk that gives up entirely, now get an honest primary error row
|
|
104
|
+
too, extending the #83 judge treatment to every seat.
|
|
105
|
+
- **`runStats[].waveId`** (emit-only-when-set): every row backed by a real billed leg now names
|
|
106
|
+
the exact wave/leg it came from; e.g. the synthetic `claude` row, a give-up chair's error row,
|
|
107
|
+
and a leg-less dead-seat/critic/lens primary error row (the two SL-2 retry note-classes that
|
|
108
|
+
never produced a real leg for the seat at all) carry none.
|
|
109
|
+
- **The run-cost bijection invariant suite** (`tests/council/run-cost-bijection.test.js`): every
|
|
110
|
+
terminal run now proves Σ(`runStats` legged-row usage) equals `run.json`'s `usage` block —
|
|
111
|
+
cost, and the reported/estimated/unpriced/subtreeUnknown leg counts — across clean, repair,
|
|
112
|
+
chair-walk-failure, debate-repair, retry-healed, and retry-failed scenarios.
|
|
113
|
+
- **`runStats[].resolvedModel`** (v4.7 GOA-7, emit-only-when-set): every row built from a served
|
|
114
|
+
leg now records the executable id that actually served (post-fallback-substitution), never the
|
|
115
|
+
alias; carried verbatim through `tally.json`/`verdict.json` and onto `council-ledger.jsonl` rows.
|
|
116
|
+
- **`council stats` rows gain `aliases[]`** (every alias observed for the group, most recent
|
|
117
|
+
first — `aliases[0]` is the launch-preferred name) **and a `legacy` mark** on groups whose rows
|
|
118
|
+
all lack `resolvedModel`; the human table sizes the model column to the longest key (16-char
|
|
119
|
+
floor) and marks `legacy` in the notes column beside `low-N`.
|
|
120
|
+
- **`--tag <t>` on `start`/`fanout`/`council run`** (CLI + MCP, v4.7 F8 D13): labels a session
|
|
121
|
+
for `list`/`--search`/`spend --group-by tag`. Reject-style validation
|
|
122
|
+
(`^[A-Za-z0-9_-]{1,64}$`) — an invalid tag fails fast rather than being silently truncated or
|
|
123
|
+
charset-stripped the way `sanitizeCouncilName` cleans, since a stored tag is a search key.
|
|
124
|
+
Stored absent-not-null on `metadata.json`/`wave.json`/`run.json` and every result doc; the MCP
|
|
125
|
+
shared-server's in-process start path stamps it too. `--tag` is rejected alongside
|
|
126
|
+
`--retry-failed` (`BAD_ARGS`).
|
|
127
|
+
Council sub-waves (Stage-1, critic/lens solos, Stage-2, chair, debate) all carry the run's tag
|
|
128
|
+
on their own wave metadata. Riders: `continue`/`resume` don't yet inherit the parent session's
|
|
129
|
+
tag (their rows group under `(unattributed)` for `--group-by tag`), and `--retry-failed` doesn't
|
|
130
|
+
yet inherit the original wave's tag either — both are future work, not oversights.
|
|
131
|
+
- **`amicus list --search <q>` / MCP `amicus_list {search}`** (F8 D15, errata E-PR3-5):
|
|
132
|
+
case-insensitive substring filter over `id`, `tag`, and briefing material. Fan-out wave rows
|
|
133
|
+
match against the full `briefing.md` text (falling back to the row's 200-char excerpt when
|
|
134
|
+
unreadable); council-run rows match `briefing.md` written at MCP launch, or the
|
|
135
|
+
post-`--- MATERIAL / BRIEFING ---` portion of `briefing-stage1.md` for CLI-launched runs; leg
|
|
136
|
+
rows (spawned by a wave) match `id`/`tag` only, so a wave's briefing never surfaces once per
|
|
137
|
+
leg it spawned. A bare `--search` with no value is a usage error.
|
|
138
|
+
- **`amicus spend --group-by tag`** (F8 D16): spend-ledger rows now carry `tag` (leg and solo
|
|
139
|
+
rows alike); untagged history groups under `(unattributed)`, matching every other dimension's
|
|
140
|
+
convention. `SPEND_LEDGER_SCHEMA_VERSION` stays at `1` — this is an additive field, not the kind
|
|
141
|
+
of change that forced the council-ledger's `LEDGER_SCHEMA_VERSION` 1 → 2 bump above, which
|
|
142
|
+
existed to segment history by resolved-model id, a different need.
|
|
143
|
+
|
|
144
|
+
### Changed
|
|
145
|
+
|
|
146
|
+
- **Tally/report/GUI cost totals now include legs that used to be silently dropped** — repairs,
|
|
147
|
+
failed chair attempts, and superseded/replaced legs all get a row now. Totals read HIGHER than
|
|
148
|
+
v4.6.x for an identical run, intentionally: this is the fix for the two-numbers-disagree
|
|
149
|
+
symptom (`council tally`'s sum vs. `run.json`'s total quietly diverging by the omitted spend).
|
|
150
|
+
- **The council-ledger join is now an explicit allowlist, not a skip-set**: `seat`, `critic`,
|
|
151
|
+
`lens:*`, `chair`, `claude`, `council`, `redteam`. Fail-closed by design — consumers keying
|
|
152
|
+
`runStats` by model must now exclude every role outside that set, the v4.7 analogue of v4.6's
|
|
153
|
+
"must exclude `role: 'judge'`" rule: a custom/free-form `role` label (e.g. a skill-authored tag)
|
|
154
|
+
still renders in the tally/report artifact but no longer contributes role/wasChair/conformance
|
|
155
|
+
to `amicus council stats` reliability numbers.
|
|
156
|
+
- **The Workspace seats panel no longer renders the three new non-seat launch rows**
|
|
157
|
+
(`chair-attempt`, `repair`, `superseded`) — they still appear in the report/tally cost tables,
|
|
158
|
+
just not as a seat.
|
|
159
|
+
- **`LEDGER_SCHEMA_VERSION` 1 → 2** (v4.7 GOA-7). Legacy-read, no migration: rows without
|
|
160
|
+
`resolvedModel` (all pre-v4.7 history, plus leg-less rows whose resolution is unknowable)
|
|
161
|
+
aggregate under their alias; a group is marked `legacy` only when every row in it lacks
|
|
162
|
+
`resolvedModel`.
|
|
163
|
+
- **`council stats` groups reliability by resolved model id** (`resolvedModel || model`) instead
|
|
164
|
+
of by alias alone — history splits honestly at the bump; a retargeted alias's new rows start
|
|
165
|
+
a fresh `low-N` group.
|
|
166
|
+
- **Chair fallback promotion (`pickFallbackChair`) excludes candidates by their full name set**
|
|
167
|
+
(group key + `aliases[]`) and launches the group's most-recent alias (`aliases[0]`) — a bench
|
|
168
|
+
seat's resolved-keyed group can no longer be promoted as its own chair.
|
|
169
|
+
- **`amicus list` and the MCP `amicus_list` tool now share one enumeration**
|
|
170
|
+
(`src/sidecar/read.js`, F8 D14): MCP rows gain `type`/`parentWave`/`legCount`, CLI rows gain
|
|
171
|
+
`mode`, and both gain `tag`. The MCP `status` input relaxed from a 3-value enum
|
|
172
|
+
(`'all' | 'running' | 'complete'`) to a free string, so `error`/`aborted`/`crashed`/
|
|
173
|
+
`timed-out`/`idle-timeout` — always real statuses, previously rejected outright by MCP's schema
|
|
174
|
+
even though the CLI already accepted them — now filter correctly there too. The CLI table
|
|
175
|
+
gained a `TAG` column.
|
|
176
|
+
- Test/comment/docs sweep (v4.7 PR4, theme a): ~30 census nits dispositioned — no behavior
|
|
177
|
+
changes.
|
|
178
|
+
- **`--out`/`-o` no longer accepts a flag-shaped value as a filename.** `--out -x` and
|
|
179
|
+
`--out=-x` previously wrote a file literally named `-x` (the dash-leading value was accepted
|
|
180
|
+
at face value); both forms now fail fast with `BAD_ARGS` ("-o/--out cannot start with '-':
|
|
181
|
+
got '-x'") instead. `-o -x` was already rejected before this change, via a separate internal
|
|
182
|
+
check — it still fails with the same code and exit status, now carrying the more accurate
|
|
183
|
+
message above in place of "-o/--out requires a value".
|
|
184
|
+
- **`{{project}}` template variable is now path-resolved** (absolute, normalized via
|
|
185
|
+
`path.resolve`), matching `{{artifact_path}}`'s existing behavior — it previously rendered the
|
|
186
|
+
raw `--cwd`/`process.cwd()` string verbatim. All three `TEMPLATE_RENDER` error paths also gain a
|
|
187
|
+
real, followable `hint` instead of `hint: null`.
|
|
188
|
+
- **MCP-launched council runs now record `run.template` provenance**, closing the one remaining
|
|
189
|
+
gap after Wave 1's D1 forwarded it on fanout/start — `mcp-council-run.js`'s template path no
|
|
190
|
+
longer discards `promptMeta`, so `run.json` carries `{name,hash}` the same way the CLI path
|
|
191
|
+
already did.
|
|
192
|
+
|
|
193
|
+
### Fixed
|
|
194
|
+
|
|
195
|
+
- **`amicus list --all` now actually lists every project.** The flag has been documented since
|
|
196
|
+
before v4.7 (`--all` / "Show all projects" in `amicus list`'s help text) but `listSidecars`
|
|
197
|
+
never read it, so it silently behaved exactly like a bare `amicus list`. It now enumerates
|
|
198
|
+
every project the global sessions-index knows about (an advisory navigation aid, not
|
|
199
|
+
authoritative — a stale entry pointing at a missing or unreadable project is skipped, not
|
|
200
|
+
surfaced as an error), deduped by canonical project identity.
|
|
201
|
+
- **`amicus list --search` was accepted and silently ignored.** The generic CLI arg parser took
|
|
202
|
+
any `--search <value>`, but nothing downstream read it, so the flag — whose sibling convention,
|
|
203
|
+
`amicus models --search`, was already a repo-wide pattern — quietly did nothing. It's now
|
|
204
|
+
implemented; see Added, above.
|
|
205
|
+
- **`amicus fanout --quiet` was accepted and silently ignored.** `quiet` is a repo-wide known
|
|
206
|
+
flag, so the command parsed and exited 0 — but `handleFanout` never forwarded it into
|
|
207
|
+
`runFanout`, which printed the launch banner and per-leg lines anyway. Same
|
|
208
|
+
accepted-but-ignored shape as `list --search` above.
|
|
209
|
+
- **`amicus pack save --version <semver>` was accepted and silently ignored — it wrote no pack at
|
|
210
|
+
all.** `version` is a global boolean flag, so `parseArgs` set `args.version = true`, stranded the
|
|
211
|
+
semver in positionals, and `bin/amicus.js` printed the amicus version banner *before* command
|
|
212
|
+
dispatch — `handlePack` never ran, and the command exited 0 having saved nothing.
|
|
213
|
+
`--version=2.0.0` failed identically. Meanwhile the handler read `args.version` for a value it
|
|
214
|
+
could never receive, and both the help text and `docs/usage.md` documented `--version <semver>`
|
|
215
|
+
as a real option. The pack's own version is now spelled **`--pack-version <semver>`** (honored on
|
|
216
|
+
both the flags and `--from-run` paths), and `pack save --version` fails fast with `BAD_ARGS`
|
|
217
|
+
naming the right spelling. Every other `--version` still prints the banner. Third instance of the
|
|
218
|
+
accepted-but-ignored shape, after `list --search` and `fanout --quiet` above.
|
|
219
|
+
- **A fanout whose server fails to start no longer drops its pack.** `errorWave` — the third
|
|
220
|
+
`buildWaveResult` call site — inherited a pre-seeded `tag` from `metadata.json` but not a
|
|
221
|
+
pre-seeded `pack`, so an MCP-spawned wave that died at server start persisted a `wave.json`
|
|
222
|
+
missing the pack it was launched with (and `amicus read --json` prefers `wave.json`). The two
|
|
223
|
+
other call sites already inherited both; this was the lone holdout.
|
|
224
|
+
- **The test suite no longer writes session directories outside its sandbox.** Several suites
|
|
225
|
+
passed the literal `'/tmp'` as a project cwd — on Windows that resolves to `C:\tmp`, a real
|
|
226
|
+
directory — so every run leaked real session dirs onto the developer's filesystem and into the
|
|
227
|
+
global sessions-index. They now sandbox under `os.tmpdir()`, pinned by
|
|
228
|
+
`tests/hermetic-tmp-guard.test.js`. This residue was what made `--all` an 8-second, 21k-row
|
|
229
|
+
dump on a developer machine.
|
|
230
|
+
- **`pack list` warnings now print to stderr, not stdout.** They previously shared stdout with
|
|
231
|
+
`pack list`'s data output — unlike `pack save`, which already used stderr for the same
|
|
232
|
+
"Warning:" text — so `pack list | grep` mixed diagnostics into data. `--json` output is
|
|
233
|
+
unaffected.
|
|
234
|
+
- **A council pack combining `critic` and `lenses` with a by-name (string) bench is now rejected
|
|
235
|
+
at validate-time (`PACK_INVALID`), matching the array-bench form.** It previously survived
|
|
236
|
+
run-mode validation and could later surface a mutual-exclusion error naming a flag the user
|
|
237
|
+
never typed. One consequence: a string-bench pack naming both `critic` and `lenses` — even
|
|
238
|
+
invoked with an explicit flag — now hard-fails `PACK_INVALID` instead of running; array-bench
|
|
239
|
+
packs already behaved this way, so this closes a bench-shape-dependent inconsistency rather
|
|
240
|
+
than introducing a new restriction.
|
|
241
|
+
- **The `{{var.}}` (empty variable key) template error now has a followable message.** It
|
|
242
|
+
previously told the user to run `--var =<value>` — a form the CLI's own `--var` parser rejects,
|
|
243
|
+
making the remedy unfollowable by construction; it now correctly explains that
|
|
244
|
+
`{{var.<key>}}` requires a key and lists the known variables.
|
|
245
|
+
- **`doctor`'s tmp-sweep checks no longer report a name-shaped directory as an unremovable
|
|
246
|
+
orphan.** A directory whose name happened to match the tmp-file pattern (e.g.
|
|
247
|
+
`.metadata.json.<pid>.<rand>.tmp`) was picked up by the orphan scan, then permanently failed to
|
|
248
|
+
unlink — parking the check at `warn` forever ("swept 0, N remaining (too fresh or
|
|
249
|
+
unremovable)") even after every real orphan was cleared. Directories are now excluded from
|
|
250
|
+
both sweeps (the metadata sweep, which never follows symlinks, also excludes
|
|
251
|
+
symlinks/sockets/FIFOs) before the scan ever returns them, so the check reports `ok` once
|
|
252
|
+
nothing real is left.
|
|
253
|
+
|
|
254
|
+
### Added (follow-up)
|
|
255
|
+
|
|
256
|
+
- **`amicus list --limit <n>`**: show only the *n* newest rows (`0` = unlimited, and absent
|
|
257
|
+
behaves exactly as before). When the cap elides rows it says so, naming the real total; in
|
|
258
|
+
`--json` mode that notice goes to stderr so stdout stays a single parseable document. Caps
|
|
259
|
+
output only — `--all` still enumerates every project, since capping the walk would rank rows it
|
|
260
|
+
never saw.
|
|
261
|
+
|
|
262
|
+
### CI
|
|
263
|
+
|
|
264
|
+
- **The MCP-Registry skip-check's version match is no longer a BRE with unescaped dots.**
|
|
265
|
+
`$VERSION` (a semver like `4.7.0`) was interpolated directly into the grep pattern, where an
|
|
266
|
+
unescaped `.` matches any character; the fail direction is fail-toward-skip (a false match
|
|
267
|
+
would silently drop a publish), so the dots are now backslash-escaped before matching. Not
|
|
268
|
+
exploitable in practice, but tightens the same idempotency check the v4.6.3 CI fix below
|
|
269
|
+
hardened.
|
|
270
|
+
|
|
271
|
+
### Fixed
|
|
272
|
+
|
|
273
|
+
- A pack file whose JSON body is not an object (`null`, an array, a bare number or string) used to
|
|
274
|
+
read as a *successful* load and then crash with an uncaught `TypeError` — `council run --pack` and
|
|
275
|
+
`pack show` both died, and `pack show --json` exited 0 with `"pack": null`. It is now a clean
|
|
276
|
+
`PACK_NOT_FOUND`.
|
|
277
|
+
- **The MCP `amicus_start` shared-server path skipped the budget gate entirely unless a pack set
|
|
278
|
+
`maxCost`.** It now gates unconditionally with the same `maxCost`/`maxCostPerMtok` config fallback
|
|
279
|
+
the CLI has always used. Runs that previously proceeded may now be refused — that is the fix.
|
|
280
|
+
- Budget refusal text is now surface-aware on the MCP `amicus_start` (shared-server) path: it no
|
|
281
|
+
longer names `--max-cost`/`--no-cost-gate`, flags that do not exist there. Other MCP-visible
|
|
282
|
+
refusals — the `amicus_start` spawn fallback and the fanout/council reservation hint recorded in
|
|
283
|
+
run docs — still carry CLI-flavoured text; that remainder is filed, not fixed here. On BOTH
|
|
284
|
+
surfaces the remedy now names only levers that can actually clear the branch that fired: raising
|
|
285
|
+
the ceiling was previously suggested for a per-$/Mtok refusal, which it could never clear.
|
|
286
|
+
- A council pack could set `options.timeout` to `true` or to a non-numeric string and have it reach
|
|
287
|
+
the engine — `validatePack` checks option key names, never value types, and the old post-merge
|
|
288
|
+
check was `timeout <= 0`, which `true` passes by coercing to `1`. Such a pack now exits 1.
|
|
289
|
+
- `--cwd` typed without a value parsed as boolean `true` and reached 16 consumer sites — crashing
|
|
290
|
+
`council run` with a raw `TypeError` and silently resolving templates against `<cwd>/true`. It now
|
|
291
|
+
exits 1 at the entry point. Same treatment for `council run`'s `--out-dir`, `--claude-review`,
|
|
292
|
+
`--run-id`, and `--timeout` (which also accepted `NaN`).
|
|
293
|
+
- **`council run --out-dir` could write outside the project.** The MCP path has been fenced since
|
|
294
|
+
v4.5; the CLI now applies the same containment check.
|
|
295
|
+
- A council member whose model name collides with an `Object.prototype` key (`toString`,
|
|
296
|
+
`constructor`, …) crashed the Workspace seats repaint and every live tick after it.
|
|
297
|
+
|
|
298
|
+
### Added (v4.7 PR7)
|
|
299
|
+
|
|
300
|
+
- **The seats table's trailing flag column marks a seat that was retried and stayed failed**
|
|
301
|
+
(PR1F-4): `↻ retried once`, on the errored seat's own primary cost row. Previously that
|
|
302
|
+
information only existed on a dead-seat row the v4.7 CA-4 convergence now suppresses, so it had
|
|
303
|
+
no home at all; a status-cell suffix was rejected because a dead seat's row can legitimately
|
|
304
|
+
carry `status: 'complete'`, and `complete — retried once` reads as "it finished, twice." Terminal
|
|
305
|
+
path only (`seatsFromRunStats`) — a seat retried while its run is still live shows no marker
|
|
306
|
+
until the run finishes.
|
|
307
|
+
|
|
308
|
+
### Fixed (v4.7 PR7)
|
|
309
|
+
|
|
310
|
+
- **Four stale-paint paths in the workspace prose panels, plus three unhandled-rejection sites,
|
|
311
|
+
closed together (T19-m1/T19-m2).** A panel could repaint stale content after a
|
|
312
|
+
close/flip-blind/reopen sequence, after two loads landed out of order, after the same run's
|
|
313
|
+
artifact manifest grew mid-flight, or after being collapsed mid-flight and reopened; separately,
|
|
314
|
+
three fire-and-forget panel-load promises could reject uncaught. Fixed behind a new
|
|
315
|
+
`workspace-lazy.js` extraction: an unconditional panel-cache drop on every issue, a monotonic
|
|
316
|
+
per-panel issue token so only the newest in-flight load can paint, two-argument
|
|
317
|
+
`.then(ok, fail)` termination on every load promise (with an announced eviction and a self-check
|
|
318
|
+
against a stale cache write), and a sync-safe wrap around the matrix drill-in call so a
|
|
319
|
+
synchronous throw still surfaces instead of escaping uncaught. Worth recording honestly: the
|
|
320
|
+
collapse-mid-flight path was not closed by the cache-drop half — that half *converted* it from a
|
|
321
|
+
deterministic stale paint into a race, by orphaning a still-in-flight load that no longer had a
|
|
322
|
+
cache entry to fence it. The issue token is what actually closes it. Both land in the same commit
|
|
323
|
+
series, so no released state ever carried the race.
|
|
324
|
+
- **`amicus_fanout`'s wave briefing is now the rendered prompt, not the raw one** (W1-M4),
|
|
325
|
+
matching the parity the `amicus_start` in-process path already had. Previously a spawned child
|
|
326
|
+
that aborted before rendering its own copy left `briefing.md` — the file
|
|
327
|
+
`amicus list --search`/`amicus read` treat as the wave's search corpus — permanently holding
|
|
328
|
+
unrendered template markup. **Behaviour change:** a wave launched from a pack that forwards a
|
|
329
|
+
`template` now writes a second file into the wave dir, `briefing-input.md` (the raw prompt handed
|
|
330
|
+
to the spawned child so its own render still drives `promptMeta.template` provenance), and
|
|
331
|
+
`--search` now matches the rendered text even for a wave whose MCP-spawned child aborted early —
|
|
332
|
+
previously it matched nothing meaningful in that case; the same rendered excerpt (first 200
|
|
333
|
+
characters) is also seeded into `metadata.json` at creation for every MCP fan-out wave, pack or
|
|
334
|
+
not, so `amicus list`'s BRIEFING column is populated for an aborted wave too, not just a
|
|
335
|
+
completed one. `amicus_start`'s identical divergence
|
|
336
|
+
(`mcp-server.js:669`) is unchanged; nobody has driven that path end to end yet, so it is filed in
|
|
337
|
+
`BACKLOG.md`, not fixed here.
|
|
338
|
+
- **`amicus_fanout` now rejects an empty prompt or a non-positive timeout before creating anything
|
|
339
|
+
on disk**, closing the one remaining pid-less `status: 'running'` orphan-wave class the schema
|
|
340
|
+
didn't already cover. **Behaviour change:** a request shaped like `{prompt: '', ...}` or
|
|
341
|
+
`{timeout: -1, ...}` previously created a wave directory and spawned a child that then failed on
|
|
342
|
+
its own terms; it now returns a plain-text error and creates nothing. The Zod schema closes this
|
|
343
|
+
for one entry point (`prompt: z.string().min(1, …)`, `timeout: z.number().positive(…)`); a second,
|
|
344
|
+
identical check lives in the handler itself, after pack merge, because a pack can push the same
|
|
345
|
+
invalid values through a door the schema never sees (`validatePack` checks option key names, not
|
|
346
|
+
value types).
|
|
347
|
+
|
|
348
|
+
### Changed (v4.7 PR7)
|
|
349
|
+
|
|
350
|
+
- **The budget-refusal text recorded in a run/wave doc's degrade record is now surface-neutral**
|
|
351
|
+
(PR6F-1), since that doc is read by both the CLI and MCP: "the $N cost ceiling for this run
|
|
352
|
+
refused it" / "Raise this run's cost ceiling, or turn the cost gate off, to seat them," in place
|
|
353
|
+
of CLI-flavoured `--max-cost`/`--no-cost-gate` wording that named flags an MCP caller cannot type.
|
|
354
|
+
A separate, structurally-unreachable copy of the same CLI-flavoured trailer (nothing ever renders
|
|
355
|
+
its `hint` on that path) was removed rather than reworded.
|
|
356
|
+
|
|
8
357
|
## [4.6.3] - 2026-08-05
|
|
9
358
|
|
|
10
359
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**A multi-model LLM Council for Claude — with a parallel AI window underneath.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<p align="center"><img src="./docs/cards/council-hero.svg" width="560" alt="The Amicus council ritual across three convenings: independent review spokes, anonymous cross-review, and a non-Claude chair synthesizing the verdict — the third convening seats a local Qwen2.5-Coder on-device alongside cloud peers"></p>
|
|
8
8
|
|
|
9
9
|
Hand Claude a plan, a design, a diff, an architecture decision, a manuscript — anything — and say *council review this*: Amicus routes it through several models from different families, has them anonymously cross-review each other, and a non-Claude chair synthesizes a verdict you turn into accept/deny edits. Or skip the ceremony and **fork** a single conversation to Gemini, GPT, DeepSeek, or any other model — it works in parallel with full context, and you **fold** the result back when you're ready. Claude orchestrates throughout; you stay in your editor.
|
|
10
10
|
|
|
@@ -62,7 +62,7 @@ One install delivers six things that work together:
|
|
|
62
62
|
|
|
63
63
|
Claude is the orchestrator. The council and chat skills run *on top of* the engine; you talk to Claude, and Claude drives Amicus.
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
<p align="center"><img src="./docs/cards/what-is-amicus.svg" alt="One install: the second-opinion council and sidecar skills riding the amicus CLI + MCP engine, with the live model catalog, watch/spend, and Council Workspace fed off it"></p>
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
@@ -89,6 +89,8 @@ flowchart LR
|
|
|
89
89
|
E --> F["Reviewed copy<br/>+ run folder"]
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
<p align="center"><img src="./docs/cards/blind-ballots.svg" width="520" alt="Anonymized cross-review: reviews are relabeled A, B, C before the bench ranks them — one model disputes its own review"></p>
|
|
93
|
+
|
|
92
94
|
**What a run produces** (in `output/<stem>-council/`):
|
|
93
95
|
|
|
94
96
|
- `review-<model>.md` × N — each model's independent review.
|
|
@@ -106,6 +108,8 @@ flowchart LR
|
|
|
106
108
|
- **Debate mode** — after cross-review, every Contested or Disputed finding goes back to its raiser to **defend, amend, or withdraw**, and the disputing judges re-vote. Exactly one rebuttal round, then the final tally.
|
|
107
109
|
- **Claude in the council** — Claude adds its own fresh review to the bundle so the bench ranks and adjudicates it. Claude is *judged* but never votes or chairs, so the verdict stays independent.
|
|
108
110
|
|
|
111
|
+
<p align="center"><img src="./docs/cards/dial-it-up.svg" alt="The four opt-in council elements as toggles — critic seat and debate on, expert lenses and Claude-in-the-council off — with the up-front run-shape disclosure"></p>
|
|
112
|
+
|
|
109
113
|
The critic and lens methodologies are adapted from the `/critic` and `/debate` agents in [John Renaldi's product-kit](https://github.com/jrenaldi79/plugin-marketplace) (MIT); the briefing boilerplate lives in [`skills/second-opinion/SEAT-BRIEFS.md`](./skills/second-opinion/SEAT-BRIEFS.md).
|
|
110
114
|
|
|
111
115
|
**Cost is disclosed up front.** Before any model launches, you see the run shape — including any enabled optional elements — for example:
|
|
@@ -114,6 +118,8 @@ The critic and lens methodologies are adapted from the `/critic` and `/debate` a
|
|
|
114
118
|
|
|
115
119
|
Then the council waits for your confirmation.
|
|
116
120
|
|
|
121
|
+
<p align="center"><img src="./docs/cards/priced-up-front.svg" width="520" alt="The budget gate: a $0.42 run passes, a $31.80 bench is blocked, and the receipt matches the estimate"></p>
|
|
122
|
+
|
|
117
123
|
The skill lives at **[`skills/second-opinion/SKILL.md`](./skills/second-opinion/SKILL.md)**; the design spec behind it is **[`skills/second-opinion/COUNCIL-DESIGN.md`](./skills/second-opinion/COUNCIL-DESIGN.md)**. For what `amicus council tally|verdict|report|stats` actually take as input and produce — field-by-field schemas, verdict.json's provenance, and a full worked example run against the real CLI — see **[docs/council.md](./docs/council.md)**.
|
|
118
124
|
|
|
119
125
|
---
|
|
@@ -127,10 +133,14 @@ The council is the hero — start with the everyday way, and reach for the more
|
|
|
127
133
|
- **With a debate round.** Add `--debate` and every Contested or Disputed finding goes back to its raiser to **defend, amend, or withdraw** while the disputing judges re-vote — exactly one rebuttal round, then the final tally. → [The Council](#the-council)
|
|
128
134
|
- **On free, local, private models — at $0.** Point the council (and sidecars) at an OpenAI-compatible server already running on your machine — Ollama, LM Studio, or vLLM — with `amicus provider add`. No API key, no per-token bill, nothing leaves your machine, and it works offline. → [`amicus provider`](./docs/usage.md#amicus-provider)
|
|
129
135
|
|
|
136
|
+
<p align="center"><img src="./docs/cards/same-table.svg" width="520" alt="A local Ollama model seated as an equal council member at $0.00 — a member, not a mode"></p>
|
|
137
|
+
|
|
130
138
|
### Headless council (CI)
|
|
131
139
|
|
|
132
140
|
The same pipeline runs with no Claude runtime at all: `amicus council run --prompt-file briefing.md --models gemini,glm --chair deepseek --json` executes the review waves, the anonymized cross-review, the tally, and the chair verdict in one command, and writes the full run directory (`verdict.json` with the chair's parsed `overallVerdict`, `report.html`, every review and judge output). That is what powers the repo's own **Council Review GitHub Action v2** — on PRs labeled `council-review` it posts an adjudicated verdict as a check run plus a sticky comment, uploads the run directory as an evidence artifact, and can optionally gate merges via its `fail_on` input (default: report-only). Reference: [docs/council.md](./docs/council.md#amicus-council-run).
|
|
133
141
|
|
|
142
|
+
<p align="center"><img src="./docs/cards/ship-gate.svg" alt="A council gating a release pipeline: exit 0 ships it, exit 1 sends it back"></p>
|
|
143
|
+
|
|
134
144
|
### Free council (zero-cost)
|
|
135
145
|
|
|
136
146
|
Want the cross-examination without the model spend? `amicus setup` offers a **Free OpenRouter council** mode — readline wizard option 2, and the Electron **Models** step. It detects the free `:free` models live from the catalog, lets you multi-pick (Enter takes a vendor-diverse default), and saves them as `councils.free` — a first-class `councils` config primitive seeded under collision-safe `free-*` aliases. Your `config.default` is left untouched, and all you need is an `OPENROUTER_API_KEY`.
|
|
@@ -397,7 +407,7 @@ When you don't need a full council — just one other model's take — fork a co
|
|
|
397
407
|
|---------|--------------|
|
|
398
408
|
| `amicus start` | Launch a new session (interactive or `--no-ui`). |
|
|
399
409
|
| `amicus fanout` | Run N models on the same prompt in parallel (headless). |
|
|
400
|
-
| `amicus list` | Show previous sessions. |
|
|
410
|
+
| `amicus list` | Show previous sessions — shows the `--tag <t>` set at launch, `--search <q>` filters, `--limit <n>` caps the rows, `--all` spans every known project. |
|
|
401
411
|
| `amicus resume` | Reopen a previous session with full history. |
|
|
402
412
|
| `amicus continue` | Start a new session building on a previous one. |
|
|
403
413
|
| `amicus read` | Output a session's summary / conversation / metadata. |
|
|
@@ -440,13 +450,13 @@ $ amicus status demo123 --json
|
|
|
440
450
|
"taskId": "demo123",
|
|
441
451
|
"status": "complete",
|
|
442
452
|
"elapsed": "5m 0s",
|
|
443
|
-
"version": "4.
|
|
453
|
+
"version": "4.7.1",
|
|
444
454
|
"model": "google/gemini-2.5-flash",
|
|
445
455
|
"phase": "terminal"
|
|
446
456
|
}
|
|
447
457
|
```
|
|
448
458
|
|
|
449
|
-
`amicus list --status` accepts `running`, `complete`, `error`, `timed-out`, `aborted`, `crashed`, `idle-timeout`. Full field-by-field docs (a running session's `messages`/`STALLED` reporting, wave-ID status shape, etc.) are in [docs/usage.md](./docs/usage.md).
|
|
459
|
+
`amicus list --status` accepts `running`, `complete`, `error`, `timed-out`, `aborted`, `crashed`, `idle-timeout`. Rows carry a `TAG` column (set at launch with `--tag <t>`); `--search <q>` filters by id/tag/briefing substring, `--limit <n>` caps the row count (0 = unlimited), and `--all` lists across every known project. Full field-by-field docs (a running session's `messages`/`STALLED` reporting, wave-ID status shape, etc.) are in [docs/usage.md](./docs/usage.md).
|
|
450
460
|
|
|
451
461
|
---
|
|
452
462
|
|
|
@@ -461,6 +471,8 @@ amicus models --search gemini # filter by substring
|
|
|
461
471
|
|
|
462
472
|
`start`/`fanout` validate your model against the catalog before launching (skip with `--no-validate-model`). You can also always bypass aliases and pass a full model ID directly — bare `provider/model` (canonical) or `openrouter/provider/model` (explicit override); see Routing below. Catalog internals, alias management, and the full-id passthrough table are in **[docs/usage.md § Models](./docs/usage.md#amicus-models--the-model-catalog)**.
|
|
463
473
|
|
|
474
|
+
<p align="center"><img src="./docs/cards/build-the-bench.svg" alt="Direct keys, one OpenRouter key, or local runtimes seat a mixed bench — different families, different blind spots"></p>
|
|
475
|
+
|
|
464
476
|
### Routing
|
|
465
477
|
|
|
466
478
|
- **Bare `provider/model`** (e.g. `openai/gpt-5.5`, `anthropic/claude-opus-4-8`, `google/gemini-3.6-flash`) is the canonical, policy-routed form — Amicus routes it **direct-first**: your direct provider key when one is configured, falling back to OpenRouter automatically when only an OpenRouter key exists.
|
|
@@ -474,7 +486,7 @@ Full details, the API-key/prefix table, and the migration notice are in **[docs/
|
|
|
474
486
|
|
|
475
487
|
## MCP integration
|
|
476
488
|
|
|
477
|
-
The MCP server is auto-registered on install (Claude Code and Claude Desktop / Cowork). It exposes
|
|
489
|
+
The MCP server is auto-registered on install (Claude Code and Claude Desktop / Cowork). It exposes these tools:
|
|
478
490
|
|
|
479
491
|
| Tool | What it does |
|
|
480
492
|
|------|--------------|
|
|
@@ -482,7 +494,7 @@ The MCP server is auto-registered on install (Claude Code and Claude Desktop / C
|
|
|
482
494
|
| `amicus_status` | Poll a task (or a fanout wave) for completion. |
|
|
483
495
|
| `amicus_wait` | Block inside one tool call until a session/wave finishes or the wait window closes. |
|
|
484
496
|
| `amicus_read` | Read results: summary, conversation, metadata, or JSON. |
|
|
485
|
-
| `amicus_list` | List past sessions. |
|
|
497
|
+
| `amicus_list` | List past sessions (`tag` field; `search` filters by id/tag/briefing substring). |
|
|
486
498
|
| `amicus_resume` | Reopen a session. |
|
|
487
499
|
| `amicus_continue` | New session building on a previous one. |
|
|
488
500
|
| `amicus_abort` | Stop a running session. |
|
package/bin/amicus.js
CHANGED
|
@@ -23,6 +23,8 @@ const { handleResume, handleContinue } = require('../src/cli-handlers-resume-con
|
|
|
23
23
|
const { isOneShotCommand, armExitWatchdog } = require('../src/utils/lifecycle');
|
|
24
24
|
const { suggestCommand } = require('../src/utils/input-validators');
|
|
25
25
|
const { unknownFlags, getKnownFlags } = require('../src/utils/known-flags');
|
|
26
|
+
const { packSaveVersionConflict } = require('../src/utils/cli-preflight');
|
|
27
|
+
const { failJson } = require('../src/utils/error-doc');
|
|
26
28
|
const { logger } = require('../src/utils/logger');
|
|
27
29
|
|
|
28
30
|
const VERSION = require('../package.json').version;
|
|
@@ -53,6 +55,23 @@ async function main() {
|
|
|
53
55
|
process.exit(1);
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
// `--cwd` typed with no value parses as boolean `true` (src/cli.js:101) and
|
|
59
|
+
// `--cwd=` as '' (src/cli.js:72). DEFAULTS (src/cli.js:28) always seeds a
|
|
60
|
+
// real absolute string, so a non-string or empty cwd can ONLY mean "typed
|
|
61
|
+
// without a value" — which makes this guard provably free of false
|
|
62
|
+
// positives. Left unguarded it reached 16 `args.cwd || process.cwd()`
|
|
63
|
+
// sites across 9 handlers: council run threw a raw TypeError, template
|
|
64
|
+
// silently resolved <cwd>/true.
|
|
65
|
+
// No dash check here: absolute paths never start with '-', and
|
|
66
|
+
// `--cwd ./x` is legitimate.
|
|
67
|
+
if (typeof args.cwd !== 'string' || args.cwd === '') {
|
|
68
|
+
console.error('Error: --cwd requires a value');
|
|
69
|
+
console.error(command
|
|
70
|
+
? `Run \`amicus ${command} --help\` to see valid options.`
|
|
71
|
+
: 'Run `amicus --help` to see valid options.');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
56
75
|
// Install crash handler for MCP-spawned processes (have --task-id)
|
|
57
76
|
if (args['task-id'] && (command === 'start' || command === 'continue')) {
|
|
58
77
|
const { installCrashHandler } = require('../src/sidecar/crash-handler');
|
|
@@ -86,6 +105,16 @@ async function main() {
|
|
|
86
105
|
}
|
|
87
106
|
}
|
|
88
107
|
|
|
108
|
+
// `pack save` documents a per-pack `--pack-version <semver>`. `--version` is a
|
|
109
|
+
// global BOOLEAN_FLAG, so `pack save … --version 2.0.0` used to fall straight
|
|
110
|
+
// into the banner below: exit 0, no pack written, the semver stranded in
|
|
111
|
+
// positionals. Reject that one combination by name instead of silently doing
|
|
112
|
+
// something else; every other --version still prints the banner.
|
|
113
|
+
const versionConflict = packSaveVersionConflict(args);
|
|
114
|
+
if (versionConflict) {
|
|
115
|
+
process.exit(failJson(!!args.json, versionConflict));
|
|
116
|
+
}
|
|
117
|
+
|
|
89
118
|
// Handle --version
|
|
90
119
|
if (args.version) {
|
|
91
120
|
console.log(`amicus v${VERSION}`);
|
|
@@ -221,6 +250,8 @@ async function handleList(args) {
|
|
|
221
250
|
status: args.status,
|
|
222
251
|
all: args.all,
|
|
223
252
|
json: args.json,
|
|
253
|
+
search: args.search,
|
|
254
|
+
limit: args.limit,
|
|
224
255
|
project: args.cwd
|
|
225
256
|
});
|
|
226
257
|
}
|