amicus 1.0.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/CHANGELOG.md +46 -0
- package/LICENSE +21 -0
- package/README.md +477 -0
- package/bin/amicus.js +382 -0
- package/electron/assets/icon.png +0 -0
- package/electron/assets/icon.svg +5 -0
- package/electron/fold.js +163 -0
- package/electron/ipc-setup.js +176 -0
- package/electron/load-failsafe.js +85 -0
- package/electron/main.js +468 -0
- package/electron/preload-setup.js +38 -0
- package/electron/preload.js +33 -0
- package/electron/setup-ui-alias-script.js +218 -0
- package/electron/setup-ui-aliases.js +85 -0
- package/electron/setup-ui-keys-script.js +115 -0
- package/electron/setup-ui-keys.js +97 -0
- package/electron/setup-ui-model.js +138 -0
- package/electron/setup-ui-styles.js +327 -0
- package/electron/setup-ui.js +465 -0
- package/electron/summary.js +118 -0
- package/electron/toolbar.js +229 -0
- package/electron/window-position.js +35 -0
- package/package.json +98 -0
- package/scripts/postinstall.js +193 -0
- package/scripts/setup-hooks.js +42 -0
- package/skill/SKILL.md +976 -0
- package/skills/second-opinion/COUNCIL-DESIGN.md +227 -0
- package/skills/second-opinion/MODEL-NOTES.md +104 -0
- package/skills/second-opinion/SKILL.md +389 -0
- package/src/cli-handlers.js +188 -0
- package/src/cli.js +400 -0
- package/src/conflict.js +144 -0
- package/src/context-compression.js +102 -0
- package/src/context.js +199 -0
- package/src/drift.js +144 -0
- package/src/environment.js +157 -0
- package/src/headless.js +742 -0
- package/src/index.js +106 -0
- package/src/jsonl-parser.js +180 -0
- package/src/mcp-server.js +625 -0
- package/src/mcp-tools.js +407 -0
- package/src/opencode-client.js +615 -0
- package/src/prompt-builder.js +355 -0
- package/src/prompts/cowork-agent-prompt.js +118 -0
- package/src/session-manager.js +414 -0
- package/src/session.js +180 -0
- package/src/sidecar/context-builder.js +297 -0
- package/src/sidecar/continue.js +212 -0
- package/src/sidecar/crash-handler.js +56 -0
- package/src/sidecar/fanout-leg.js +107 -0
- package/src/sidecar/fanout-output.js +46 -0
- package/src/sidecar/fanout.js +236 -0
- package/src/sidecar/interactive.js +217 -0
- package/src/sidecar/models.js +135 -0
- package/src/sidecar/progress.js +218 -0
- package/src/sidecar/read.js +183 -0
- package/src/sidecar/resume.js +221 -0
- package/src/sidecar/session-utils.js +288 -0
- package/src/sidecar/setup-window.js +79 -0
- package/src/sidecar/setup.js +280 -0
- package/src/sidecar/start.js +251 -0
- package/src/utils/agent-mapping.js +138 -0
- package/src/utils/alias-audit.js +98 -0
- package/src/utils/alias-resolver.js +77 -0
- package/src/utils/api-key-store.js +259 -0
- package/src/utils/api-key-validation.js +97 -0
- package/src/utils/auth-json.js +109 -0
- package/src/utils/config.js +291 -0
- package/src/utils/curated-models.js +82 -0
- package/src/utils/env-compat.js +38 -0
- package/src/utils/env-loader.js +54 -0
- package/src/utils/idle-watchdog.js +225 -0
- package/src/utils/input-validators.js +127 -0
- package/src/utils/lifecycle.js +43 -0
- package/src/utils/logger.js +84 -0
- package/src/utils/mcp-discovery.js +194 -0
- package/src/utils/mcp-validators.js +78 -0
- package/src/utils/model-catalog.js +103 -0
- package/src/utils/model-fetcher.js +179 -0
- package/src/utils/model-validator.js +207 -0
- package/src/utils/path-setup.js +41 -0
- package/src/utils/port-pid.js +39 -0
- package/src/utils/prompt-source.js +53 -0
- package/src/utils/result-schema.js +261 -0
- package/src/utils/server-setup.js +93 -0
- package/src/utils/session-abort.js +53 -0
- package/src/utils/session-lock.js +95 -0
- package/src/utils/shared-server.js +216 -0
- package/src/utils/start-helpers.js +76 -0
- package/src/utils/thinking-validators.js +92 -0
- package/src/utils/update-notifier-loader.js +18 -0
- package/src/utils/updater.js +157 -0
- package/src/utils/validators.js +300 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: second-opinion
|
|
3
|
+
description: Run a structured, multi-model "council" review of material the user provides and turn it into decisions. Use when the user supplies one or more documents, artifacts, or links PLUS an analysis request and criteria, and wants other AI model(s) to independently evaluate, critique, fact-check, stress-test, or red-team that material and then help act on the findings. The council adds a peer cross-review stage — models review the material independently, then anonymously rank and adjudicate each other's reviews, and a designated non-Claude "chair" model synthesizes the verdict while Claude orchestrates but does not synthesize. Trigger on "second opinion", "multi-model review", "council review", "have other models review/critique/evaluate this", "cross-check this against the research", "red-team/stress-test this doc", "what would other models conclude about this", or any request to review provided material with external model(s) and turn it into accept/deny edits — even if the user never says "sidecar". Optionally includes Claude as a judged (non-voting, non-chairing) council member to see how the bench rates Claude's own take. Defaults to 3 models from different families; scales down to a single thorough pass. This is NOT for quick or exploratory single-model chats — for "ask Gemini…", "what does DeepSeek think", brainstorming with a model, or spawning/forking a conversation with another model, use the sidecar skill instead.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Second Opinion (LLM Council)
|
|
7
|
+
|
|
8
|
+
Independent, multi-model review of material the user provides, turned into decisions. Any single model — including the one running this conversation — has consistent blind spots. Routing the same material through models from *different* families surfaces disagreements, missed issues, and overstated claims that one model alone won't catch.
|
|
9
|
+
|
|
10
|
+
Four principles govern this skill:
|
|
11
|
+
|
|
12
|
+
1. **Secondary tool.** By the time this skill runs, Claude has already given its opinion in the main conversation. The skill exists to bring in *independent outside* views — it does not replace or re-run Claude's upstream analysis.
|
|
13
|
+
2. **The council is the non-Claude bench by default.** Council members are models from families other than the orchestrator (Gemini, DeepSeek, GPT, etc.). Claude is not a first-opinion council member unless the optional "Claude in the council" toggle is on — and even then it is judged but does not vote or chair.
|
|
14
|
+
3. **Claude orchestrates; Claude does not synthesize the verdict.** Claude preps material, recommends the council, anonymizes reviews, drives the stages, scores, and presents accept/deny decisions. A designated non-Claude chair model synthesizes the final verdict. Claude's role ends at presenting it.
|
|
15
|
+
4. **The subject of cross-review is the other reviews, not the artifact again.** In the peer cross-review stage, models critique and rank *each other's reviews* — not re-review the original artifact. This is the mechanism that surfaces reviewer blind spots and inflated confidence.
|
|
16
|
+
|
|
17
|
+
Operating lessons from each run fold back into `MODEL-NOTES.md` (with approval), so the skill gets better at driving each model over time.
|
|
18
|
+
|
|
19
|
+
**Before launching any model, READ `MODEL-NOTES.md`** (next to this file). It holds the operating rules and per-model quirks that decide whether a run succeeds or silently fails. These were learned the hard way; skipping them wastes runs and produces empty results that look like answers.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- The user provides documents, artifacts, or links **and** an analysis request **and** criteria, and wants other models to weigh in independently.
|
|
24
|
+
- They want a fact-check, critique, research-backed evaluation, red-team, or "what would another model conclude about this?"
|
|
25
|
+
- They need actionable decisions (accept / defer / deny) on the material, not just a summary of findings.
|
|
26
|
+
- A thorough **single-model** pass is wanted — scales down gracefully; the boundary vs the `sidecar` skill is **intent** (reviewing provided material against criteria), not model count.
|
|
27
|
+
|
|
28
|
+
## When NOT to use
|
|
29
|
+
|
|
30
|
+
- The user just wants a **quick or exploratory** take from a model, to chat/brainstorm, or to "ask \<model\> X" — that is the **sidecar** skill, not this. The boundary is intent and criteria, not model count: a thorough single-model review still belongs here.
|
|
31
|
+
- The user wants *you* (Claude) to do the analysis directly with no external models, or the task is trivial.
|
|
32
|
+
- The request is to spawn or fork a conversation with another model — again, `sidecar`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## The council flow
|
|
37
|
+
|
|
38
|
+
The flow runs as a **Stage 0 intake/prep step** followed by **three sequential review waves** (Stage 1 → Stage 2 → Stage 3), each wave dependent on the prior. Within each wave, models run in parallel. Track the stages as todos and do not advance until the prior wave's outputs are in hand.
|
|
39
|
+
|
|
40
|
+
### Stage 0 — Intake & prep
|
|
41
|
+
|
|
42
|
+
Confirm the three inputs before doing anything else: **source material**, **the analysis** (the thing to be reviewed), and **the criteria** (what quality/correctness means for this material). Ask only for what is missing; don't re-ask for what is already provided.
|
|
43
|
+
|
|
44
|
+
**Establish the run folder first:** `output/<stem>-council/` (or `./second-opinion/<stem>-council/`
|
|
45
|
+
if no `output/` directory exists). Create it now — every temp file, briefing, review, and artifact
|
|
46
|
+
in this run is written here. Use its absolute path in all `--prompt-file` arguments.
|
|
47
|
+
|
|
48
|
+
**Prepare material for council models:**
|
|
49
|
+
- Large, linked, or heavily marked-up sources → extract clean text to a small, clearly-named temp
|
|
50
|
+
file in the run folder (briefing hygiene: token cost and model focus). Reference its absolute
|
|
51
|
+
path in the briefing, or inline it if small.
|
|
52
|
+
- Small, clean text → feed inline in the briefing.
|
|
53
|
+
- Write every briefing to a temp file (`_tmp-*.md` in the run folder) and pass it with
|
|
54
|
+
`--prompt-file` — never inline a briefing as a CLI argument. All `_tmp-*` files are cleaned up
|
|
55
|
+
after the run.
|
|
56
|
+
|
|
57
|
+
**Pick the council.** Default: **3 models from different families (non-Claude)**. Recommend them ranked by fit, consulting the reviewer-reliability table in `MODEL-NOTES.md`. State the estimated cost. **Disclose the run shape up front** before asking for confirmation — e.g.:
|
|
58
|
+
|
|
59
|
+
> This run uses 3 council models across 2 fanout waves + 1 chair call (~7 model runs), ~10 min.
|
|
60
|
+
|
|
61
|
+
Then **wait for confirmation**. Never launch without it. Honor the cost guardrail in `MODEL-NOTES.md` (no `o3`/`o3-pro` without explicit ask-by-name).
|
|
62
|
+
|
|
63
|
+
**Scale-down is explicit — state which mode applies:**
|
|
64
|
+
- **1 model** → thorough single pass; Stage 2 (cross-review) and Stage 3 (chair synthesis) are skipped entirely; Claude synthesizes directly. Transport: a single solo `amicus start --no-ui --json` (no fanout).
|
|
65
|
+
- **2 models** → Stage 2 runs but the ranking is thin (one ranker per review); note this limitation.
|
|
66
|
+
- **3 models (default)** → full deep council with meaningful cross-review and tie-breaking.
|
|
67
|
+
|
|
68
|
+
The scale-down levels count **non-Claude judges**; enabling "Claude in the council" adds a judged review but not a judge, so it does not change these levels. If the bench drops below 2 surviving reviews during the run, apply the Stage-1 wave-degrade rule (offer re-run or a disclosed single-pass downgrade).
|
|
69
|
+
|
|
70
|
+
**Present the "Claude in the council" toggle (default: off).** At launch, offer:
|
|
71
|
+
|
|
72
|
+
> Claude-in-the-council (default off): I can add my own fresh review to the bundle so the bench can rank and adjudicate it. You'd see how my take compares to the other reviewers. This means Claude is judged but does not vote (Stage 2) or chair (Stage 3) — the verdict stays independent. Turn on?
|
|
73
|
+
|
|
74
|
+
When off, Claude does not contribute a review and does not appear in the bundle. When on, see Stage 1.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Stage 1 — Independent reviews
|
|
79
|
+
|
|
80
|
+
Each council model reviews **the artifact** independently. Write one Stage-1 briefing file
|
|
81
|
+
(`_tmp-briefing-stage1.md` in the run folder) and launch the whole wave as ONE background call:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
amicus fanout --models <m1,m2,m3> --prompt-file <run-folder>/_tmp-briefing-stage1.md --json \
|
|
85
|
+
--agent Plan --no-context --summary-length verbose --timeout <minutes>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run it in the background (`run_in_background: true`); you are notified on completion — do not
|
|
89
|
+
poll. `fanout` is headless by definition. The command exits when every leg is terminal and prints
|
|
90
|
+
ONE JSON wave document on stdout (`schemaVersion: 1`; the wave's id field is `waveId`, each leg's id is `taskId`): check `status` (`complete` | `partial` |
|
|
91
|
+
`error`), `counts`, and each leg in `legs[]` — a leg's `summary` field IS that model's review;
|
|
92
|
+
`model`/`modelInput` identify the reviewer (`model` is the resolved id, `modelInput` the alias you passed — use the alias for `review-<model>.md` filenames); `status`/`error` identify failures. Exit code 0 =
|
|
93
|
+
all legs complete, 2 = partial (apply the wave-degrade rules below), 1 = error/aborted. (To re-fetch a single leg later: `amicus read <taskId> --json`.)
|
|
94
|
+
|
|
95
|
+
**Red-team variant:** fanout legs share a single prompt by design. When one model gets a distinct
|
|
96
|
+
red-team brief, launch it as a separate concurrent solo run alongside the wave:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
amicus start --model <redteam-model> --no-ui --json \
|
|
100
|
+
--prompt-file <run-folder>/_tmp-briefing-redteam.md \
|
|
101
|
+
--agent Plan --no-context --summary-length verbose --timeout <minutes>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Its stdout is a single run document; the `summary` field is the review.
|
|
105
|
+
|
|
106
|
+
**Cowork / no-Bash environments:** use the MCP tools instead — `amicus_fanout` (briefing via
|
|
107
|
+
file) returns `{waveId, taskIds[]}` immediately; poll `amicus_status`, then `amicus_read` each
|
|
108
|
+
leg. The council's briefings are always self-contained (`--no-context`), so MCP transport is
|
|
109
|
+
equivalent.
|
|
110
|
+
|
|
111
|
+
**Required structured output from every model.** Instruct each council model to produce:
|
|
112
|
+
|
|
113
|
+
1. A **findings list** — every finding contains:
|
|
114
|
+
- `id` — sequential integer within this review (1, 2, 3…)
|
|
115
|
+
- `claim` — the specific issue or observation
|
|
116
|
+
- `severity` — one of: `blocker | major | minor | nit`
|
|
117
|
+
- `location` — section heading or verbatim quote identifying where in the artifact
|
|
118
|
+
- `rationale` — why this is a problem or worth noting
|
|
119
|
+
|
|
120
|
+
2. A **short overall take** — 2–4 sentences summarizing the reviewer's overall assessment.
|
|
121
|
+
|
|
122
|
+
Instruct models to emit the structured output verbatim, without preamble, so it reads cleanly.
|
|
123
|
+
|
|
124
|
+
When the wave returns, save each leg's `summary` to the run folder as `review-<model>.md`
|
|
125
|
+
(one file per reviewer) before moving on.
|
|
126
|
+
|
|
127
|
+
**"Claude in the council" (when toggled on):** Claude also produces a **fresh** Stage-1 review on the artifact in the identical findings format — a new structured pass on the artifact, not a formalization of anything said upstream. This review is added to the bundle as one more anonymous entry. Claude does not rank or adjudicate in Stage 2 (it holds the label map), and does not chair in Stage 3. Save it as `review-claude.md`.
|
|
128
|
+
|
|
129
|
+
**Wave-degrade rules (Stage 1).** Read failures from the wave document — never silently ignore
|
|
130
|
+
them:
|
|
131
|
+
- All legs `complete` → proceed normally.
|
|
132
|
+
- A leg ends `error`/`timeout`/`crashed`/`aborted` but **≥ 2 reviews survive** → proceed with the
|
|
133
|
+
survivors; name the dead leg and its `error` when presenting; the bench shrinks accordingly. If this leaves exactly 2 surviving reviews, the run is now effectively a 2-model council — apply the thin-ranking disclosure (Stage 0 / Stage 4) from here on.
|
|
134
|
+
- **Fewer than 2 reviews survive** → offer the user a re-run of the dead leg(s) (solo
|
|
135
|
+
`amicus start --json`, same briefing file) or a disclosed downgrade to single-pass mode
|
|
136
|
+
(Stage 2 and Stage 3 skipped, per the scale-down rules).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### Stage 2 — Cross-review
|
|
141
|
+
|
|
142
|
+
This is the peer-validation step. Claude builds one shared anonymized bundle, distributes it to every council model for ranking and finding adjudication, then de-anonymizes for scoring.
|
|
143
|
+
|
|
144
|
+
**Build the shared anonymized bundle.** After all Stage-1 reviews are in hand, Claude:
|
|
145
|
+
1. Assigns stable labels: **Review A**, **Review B**, **Review C**, … (one per review, including Claude's if the toggle is on).
|
|
146
|
+
2. Keeps a **private label↔model map** (e.g., `Review A → deepseek`, `Review B → gemini`, `Review C → claude`) that is never sent to any sidecar model.
|
|
147
|
+
3. Assembles one bundle document containing all labeled reviews. The bundle is identical for every judge.
|
|
148
|
+
|
|
149
|
+
Each model **unknowingly ranks and adjudicates its own review** — this is the anti-favoritism mechanism, not a bug. Because no model knows which review is its own, self-bias washes out symmetrically across judges.
|
|
150
|
+
|
|
151
|
+
**Distribute the same bundle to every council model** — this is exactly fanout's shared-prompt
|
|
152
|
+
model. Write the bundle + judging instructions to `_tmp-bundle-stage2.md` and launch one wave:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
amicus fanout --models <m1,m2,m3> --prompt-file <run-folder>/_tmp-bundle-stage2.md --json \
|
|
156
|
+
--agent Plan --no-context --summary-length verbose --timeout <minutes>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
(Background, same JSON handling as Stage 1.) Each judge's leg `summary` is its ranking +
|
|
160
|
+
adjudication response. **Stage-2 degrade:** a judge leg dies → tally over the surviving judges
|
|
161
|
+
(≥ 1) and disclose the reduced bench in `crossreview-matrix.md`; tier definitions are unchanged
|
|
162
|
+
(they already count "judges engaged"). Each judge is asked to do two things on the bundle:
|
|
163
|
+
|
|
164
|
+
**Task A — Rank.** Order the reviews from most to least accurate and insightful. End the response with a parseable block in exactly this format (no other text on those lines):
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
FINAL RANKING:
|
|
168
|
+
1. Review C
|
|
169
|
+
2. Review A
|
|
170
|
+
3. Review B
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Task B — Adjudicate findings.** For every finding in the bundle, state: `agree | dispute | neutral` plus one-line reason. Reference each finding as **review-label + finding-id** — for example, `A2` means Review A's 2nd finding, `B1` means Review B's 1st finding. An "I missed this — it's valid" counts as `agree`.
|
|
174
|
+
|
|
175
|
+
As each judge's ranking + adjudication response returns, collect it (the raw per-judge responses are working intermediates, not separate run-folder artifacts). Once all are in, de-anonymize and tally them into the single `crossreview-matrix.md` — the adjudication grid plus the street-cred table (see *Output & naming*). This de-anonymized data feeds Stage 3 (chair briefing), the scoring/street-cred table, and the cross-review matrix artifact — but is never re-anonymized or forwarded to any council model.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Stage 3 — Council-chair synthesis
|
|
180
|
+
|
|
181
|
+
A designated **non-Claude** chair synthesizes the verdict across all reviews, rankings, and adjudications. The chair produces an independent verdict that Claude then presents — Claude does not paraphrase, edit, or re-synthesize it.
|
|
182
|
+
|
|
183
|
+
**Chair selection (confirmed in Stage 0).** Default: Claude recommends the strongest reasoner in the council (guided by the reviewer-reliability table in `MODEL-NOTES.md`) and the user confirms before the run launches. The chair may be a council member who already participated in Stages 1 and 2 — it receives the de-anonymized full bundle, all ranking outputs, and all adjudications so it has the complete picture.
|
|
184
|
+
|
|
185
|
+
**Fallback order if the chair fails:**
|
|
186
|
+
1. Re-run the chair call (transient failure — `MODEL-NOTES.md` mitigations apply).
|
|
187
|
+
2. Promote the next-best non-Claude council model as chair.
|
|
188
|
+
3. **Claude chairs only as last resort — with explicit disclosure** that the verdict is no longer fully independent of the orchestrator.
|
|
189
|
+
|
|
190
|
+
**Chair briefing.** Write the chair packet to `_tmp-chair-packet.md` and send one solo run
|
|
191
|
+
(background):
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
amicus start --model <chair> --no-ui --json \
|
|
195
|
+
--prompt-file <run-folder>/_tmp-chair-packet.md \
|
|
196
|
+
--agent Plan --no-context --summary-length verbose --timeout <minutes>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The run document's `summary` is the verdict. The packet contains:
|
|
200
|
+
- All Stage-1 reviews (de-anonymized — model attribution restored)
|
|
201
|
+
- All cross-review ranking outputs (with model attribution)
|
|
202
|
+
- All adjudication outputs (with model attribution and `agree | dispute | neutral` verdicts per finding)
|
|
203
|
+
|
|
204
|
+
Instruct the chair to write a **synthesized verdict** that:
|
|
205
|
+
- Weighs each reviewer's findings by their peer-validated standing (street-cred rank and adjudication pattern)
|
|
206
|
+
- Distinguishes findings the bench broadly endorsed from contested or singleton claims
|
|
207
|
+
- Arrives at an overall assessment of the artifact
|
|
208
|
+
|
|
209
|
+
Save the chair's output to the run folder as `verdict.md`.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### Stage 4 — Tiered decisions (peer-validated)
|
|
214
|
+
|
|
215
|
+
All findings from the bundle are sorted into two tiers based on the **peer-confidence tier derived from the Stage 2 adjudication data**. These tiers are **derived from the Stage 2 adjudications** — a judgment call, not a rigid formula (see *Key mechanics → §5.2 Scoring* for the full rule): a finding is **Confirmed** when agrees clearly outweigh disputes (≥ 2 judges engaged), **Contested** when there is a meaningful split or explicit disputes, and **Singleton** when only its original raiser stands behind it. Present the tiers in this order.
|
|
216
|
+
|
|
217
|
+
**Scale-down:** In a 1-model run, Stage 2 was skipped — there is no peer-confidence data, so present every finding individually for decision (no tiers). In a 2-model run, the Confirmed tier rests on thin cross-review (one ranker per review, per Stage 0) — say so when presenting it.
|
|
218
|
+
|
|
219
|
+
**Consensus tier — Confirmed findings** (peers agree clearly outweigh disputes, with ≥ 2 judges engaged)
|
|
220
|
+
|
|
221
|
+
- Present the full list in one block: id, claim, severity, and which models raised / endorsed it.
|
|
222
|
+
- Offer one **bulk accept/deny decision** over the whole tier:
|
|
223
|
+
|
|
224
|
+
> Accept all Confirmed findings? (or name any you want to skip)
|
|
225
|
+
|
|
226
|
+
- The user may accept the block, deny the block, or enumerate exceptions. Handle exceptions individually before moving on.
|
|
227
|
+
|
|
228
|
+
**Judgment tier — Contested and Singleton findings**
|
|
229
|
+
|
|
230
|
+
This is one tier with two sub-types presented separately. Present each finding individually. Handle the two sub-types distinctly:
|
|
231
|
+
|
|
232
|
+
- **Contested** (meaningful split or explicit disputes): For each finding show the claim and severity, which model raised it, who agreed, who disputed, and the one-line reasons from the adjudications. Ask for a decision before proceeding to the next: **accept / deny / modify**.
|
|
233
|
+
- **Singleton** (only the original raiser; no other judge engaged — neutral or silent): For each finding show the claim and severity and that no other judge engaged with it. Name the sole raiser. Ask for a decision before proceeding to the next: **accept / deny / modify**.
|
|
234
|
+
|
|
235
|
+
**Recording decisions.** Keep a running decision log throughout this stage — every finding's outcome (accepted / denied / modified, with any modification noted). This log feeds Stage 5 (only accepted changes go into the reviewed copy) and Stage 6 (the run-folder report).
|
|
236
|
+
|
|
237
|
+
Do not advance to Stage 5 until every finding in both tiers has a recorded decision.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Stage 5 — Outputs
|
|
242
|
+
|
|
243
|
+
**Editable source** (the artifact is a file you can write — `.md`, `.docx`, `.py`, any text format):
|
|
244
|
+
- Apply only the **accepted findings** from Stage 4.
|
|
245
|
+
- Write the result as `<stem>-reviewed.<ext>` **next to the original file** — same directory, same extension, `-reviewed` appended before the extension.
|
|
246
|
+
- Before writing, validate structural integrity: check that headings are balanced, code blocks close, front-matter is valid, etc. Fix any structural integrity issues **your edits introduce** — do not touch pre-existing issues in the original. Do not alter any content beyond the accepted findings.
|
|
247
|
+
|
|
248
|
+
**Fixed source** (the artifact is a link, PDF, or something you cannot directly edit):
|
|
249
|
+
- Do not attempt to produce a modified copy.
|
|
250
|
+
- Write a **standalone reviewed report** instead: the full decision log, the chair's verdict, and clear callouts of what should be changed and where — formatted so the user can apply the changes manually.
|
|
251
|
+
|
|
252
|
+
**Run-folder artifacts — always write these** regardless of source type. The full artifact set and naming conventions are defined in the *Output & naming* section of this skill; write every artifact specified there. The four canonical run-folder files are:
|
|
253
|
+
- `review-<model>.md` × N (already saved in Stage 1)
|
|
254
|
+
- `crossreview-matrix.md` — the de-anonymized adjudication grid and street-cred table
|
|
255
|
+
- `verdict.md` (already saved in Stage 3)
|
|
256
|
+
- `report.md` — the chair's synthesis + the full Stage-4 decision log + a summary of what was
|
|
257
|
+
applied (+ the "How Claude's review fared" readout when "Claude in the council" is on) + a
|
|
258
|
+
**run-stats table**: one row per model call — **stage** (which stage you launched the call for)
|
|
259
|
+
plus **model, status, durationMs** read from the wave/run JSON documents. The schema carries no
|
|
260
|
+
cost data — do not invent cost figures.
|
|
261
|
+
|
|
262
|
+
Tell the user exactly which files were written and where.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### Stage 6 — Capture lessons (compounding)
|
|
267
|
+
|
|
268
|
+
This stage updates `MODEL-NOTES.md` to make future runs better. **Nothing is written until the user approves a specific diff.**
|
|
269
|
+
|
|
270
|
+
**Reflect on this run.** Review the run for:
|
|
271
|
+
- Failures, near-misses, and mitigations that worked (poller traps, empty responses, timeout issues, briefing problems)
|
|
272
|
+
- Briefing wording that produced **richer or poorer** structured output than expected
|
|
273
|
+
- Chair or council model behavior worth noting
|
|
274
|
+
|
|
275
|
+
Draft new or updated entries for the per-model sections of `MODEL-NOTES.md` that capture what was learned.
|
|
276
|
+
|
|
277
|
+
**Update the reviewer-reliability table.** After every completed council run, update the rolling table in `MODEL-NOTES.md` (the "Reviewer reliability" table) for each council model that participated:
|
|
278
|
+
- **avg street-cred** — incorporate this run's rank position into each model's running average.
|
|
279
|
+
- **confirm-rate** — incorporate this run's share of each model's findings that ended up Confirmed.
|
|
280
|
+
- Merge into the existing row for that model; prune the notes column to stay tight.
|
|
281
|
+
|
|
282
|
+
**Compose the proposed MODEL-NOTES diff.** Combine the run-lessons updates and the reviewer-reliability table updates into a single proposed diff (old → new for every changed section). Show it to the user in full.
|
|
283
|
+
|
|
284
|
+
**Wait for explicit approval before writing anything.** Present the diff and ask:
|
|
285
|
+
|
|
286
|
+
> Approve this MODEL-NOTES update? (yes / no / edit)
|
|
287
|
+
|
|
288
|
+
If the user approves, write the changes. If they say "edit", incorporate their corrections and show the revised diff before writing. Do not write any partial update — write only after the full diff is approved.
|
|
289
|
+
|
|
290
|
+
**Keep MODEL-NOTES tight.** Do not append new bullets when an existing entry covers the same ground — merge or reword instead. If a note has been superseded by a better mitigation, prune the old one. The goal is a compact, authoritative reference, not a changelog.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Key mechanics
|
|
295
|
+
|
|
296
|
+
### §5.1 Anonymization
|
|
297
|
+
|
|
298
|
+
Stage 2 distributes a single anonymized bundle; this section details the mechanics that make that safe and fair.
|
|
299
|
+
|
|
300
|
+
After all Stage-1 reviews are in hand, Claude assembles **one shared bundle** — every review relabeled with stable letter identifiers: **Review A**, **Review B**, **Review C**, and so on. Claude keeps a **private label↔model map** (e.g., `Review A → deepseek`, `Review B → gemini`, `Review C → claude`) that is never shared with any sidecar model.
|
|
301
|
+
|
|
302
|
+
The **identical** bundle goes to every judge. Because no judge can tell which review is its own, each model unknowingly ranks and adjudicates its own review — this is the anti-favoritism mechanism, not a bug. Self-bias washes out symmetrically across judges rather than systematically inflating any one model.
|
|
303
|
+
|
|
304
|
+
Claude **de-anonymizes only** at two points: when computing scores and when writing `crossreview-matrix.md` and `report.md`. The label↔model map is never re-forwarded to any council model after de-anonymization.
|
|
305
|
+
|
|
306
|
+
**When "Claude in the council" is on:** Claude's own Stage-1 review enters the **same** bundle alongside the other reviews. Claude holds the label map and therefore cannot judge blind; see §5.4 for how this asymmetry is handled. Claude **never ranks or adjudicates** in Stage 2.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### §5.2 Scoring
|
|
311
|
+
|
|
312
|
+
Claude tallies two scoring signals from the Stage-2 outputs. No code is required; Claude works through the structured output directly.
|
|
313
|
+
|
|
314
|
+
**Street-cred** = each model's **average rank position** across all judges' `FINAL RANKING:` blocks (lower is better). For example, if three judges rank DeepSeek 1st, 2nd, and 1st, its street-cred score is 1.33. Surface this as a compact table in the cross-review matrix and report. Street-cred drives the chair's weighting of reviewer findings in Stage 3 and feeds the reviewer-reliability table updated in Stage 6.
|
|
315
|
+
|
|
316
|
+
**Per-finding peer-confidence tier** = a qualitative label derived from the Stage-2 adjudications for each finding:
|
|
317
|
+
|
|
318
|
+
- **Confirmed** — agrees clearly outweigh disputes, with at least 2 judges having engaged with the finding.
|
|
319
|
+
- **Contested** — a meaningful split exists or explicit disputes were recorded.
|
|
320
|
+
- **Singleton** — only the original raiser stands behind it; all other judges were neutral or silent.
|
|
321
|
+
|
|
322
|
+
These three tiers drive the Stage-4 decision flow. Assigning a tier is a **judgment call, not a rigid formula** — Claude reads the adjudication signals and makes the call at the margins, especially when engagement is sparse or agreements and disputes are close in number. When in doubt, downgrade toward Contested or Singleton rather than overstate confidence.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### §5.3 Chair selection & fallback
|
|
327
|
+
|
|
328
|
+
The default is for Claude to **recommend a non-Claude chair** from the council — typically the model with the strongest reasoning capability or the best reviewer-reliability score in `MODEL-NOTES.md` — and the user confirms this recommendation before the run launches (Stage 0). The chair **may** be a council member who already participated in Stages 1 and 2; it receives the full de-anonymized picture (all reviews with model attribution, all rankings, all adjudications) so it can synthesize from a complete view.
|
|
329
|
+
|
|
330
|
+
**Fallback chain if the chair call fails:**
|
|
331
|
+
|
|
332
|
+
1. Re-run the chair call — transient provider failures are common; apply the mitigation from `MODEL-NOTES.md`.
|
|
333
|
+
2. Promote the next-best non-Claude council model to chair.
|
|
334
|
+
3. **Claude chairs only as last resort — with explicit disclosure** that the verdict is no longer fully independent of the orchestrator. State this clearly in the report.
|
|
335
|
+
|
|
336
|
+
Never silently degrade to Claude-chairs without informing the user.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### §5.4 Claude in the council (default off)
|
|
341
|
+
|
|
342
|
+
Enabling this toggle lets the bench judge Claude's own take, so you can see how it compares to the independent council.
|
|
343
|
+
|
|
344
|
+
**Asymmetric by design.** Claude is the orchestrator and holds the label↔model map, so it cannot judge blind. The rule is therefore **asymmetric**: Claude contributes a review to be judged by the council but does **not** vote (Stage 2) or chair (Stage 3). Claude participates on the supply side only; the verdict remains independent of the orchestrator.
|
|
345
|
+
|
|
346
|
+
**Always fresh.** When the toggle is on, Claude performs a new structured Stage-1 review on the artifact — a fresh pass in the required findings format, not a formalization or summary of anything said earlier in the main conversation. Upstream feedback does not seed or constrain this review.
|
|
347
|
+
|
|
348
|
+
**"How Claude's review fared" readout.** Included in both `crossreview-matrix.md` and `report.md` when the toggle is on:
|
|
349
|
+
- Claude's street-cred rank among peers (its average rank position in the judges' `FINAL RANKING:` blocks).
|
|
350
|
+
- The Confirmed / Contested / Singleton split of Claude's findings — how many of its claims the bench endorsed, contested, or ignored.
|
|
351
|
+
|
|
352
|
+
**Integrity.** When Claude presents results — including the bench's assessment of its own review — it reports the verdict at face value. Claude does not defend, contextualize away, or re-litigate findings the bench disputed or ranked poorly. The point of the toggle is an honest external read on Claude's review; undermining that defeats the purpose.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Model-recommendation heuristics
|
|
357
|
+
|
|
358
|
+
Use these together with the reviewer-reliability table in `MODEL-NOTES.md`, which holds live performance data from prior runs:
|
|
359
|
+
|
|
360
|
+
- **Large or long material, broad coverage sweep** → favor a large-context model (e.g., Gemini) that won't truncate or degrade on the full source.
|
|
361
|
+
- **Reasoning-heavy critique, structured argument evaluation, citations** → favor a strong reasoner (e.g., DeepSeek, GPT, Opus) that will interrogate claims rather than accept them.
|
|
362
|
+
- **Code review** → favor a code-strong model (e.g., DeepSeek, GPT, Opus); general-purpose models often miss implementation-level issues.
|
|
363
|
+
- **Independence matters** → pick models from **different families**; two models from the same family produce correlated opinions and reduce the value of the cross-review.
|
|
364
|
+
- **Contrarian / red-team value** → when material is persuasive, consensus-prone, or high-stakes, assign one model an explicit red-team brief: argue against the others, hunt for what they will miss. This is especially valuable when the default council is likely to agree.
|
|
365
|
+
- **Consult the reviewer-reliability table** in `MODEL-NOTES.md` — a model's historical confirm-rate and avg street-cred are the best predictors of council value for a given run type.
|
|
366
|
+
|
|
367
|
+
Always **rank recommendations by fit**, state the trade-off for each option, and surface the estimated cost. Never present a single option without explanation.
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Output & naming
|
|
372
|
+
|
|
373
|
+
- Run folder: `output/<stem>-council/` (or `./second-opinion/<stem>-council/` if no `output/` exists), containing:
|
|
374
|
+
- `review-<model>.md` ×N — raw Stage 1 reviews (plus `review-claude.md` when "Claude in the council" is on)
|
|
375
|
+
- `crossreview-matrix.md` — adjudication grid + de-anonymized street-cred table
|
|
376
|
+
- `verdict.md` — the chair's synthesis
|
|
377
|
+
- `report.md` — synthesis + decision log + what was applied (+ the "How Claude's review fared" readout when the toggle is on) + a
|
|
378
|
+
**run-stats table**: one row per model call — **stage** (which stage you launched the call for) plus **model, status, durationMs** read from the wave/run JSON documents. The schema carries no cost data — do not invent cost figures.
|
|
379
|
+
- Reviewed copy: `<stem>-reviewed.<ext>`, next to the source.
|
|
380
|
+
- Temp working files (`_tmp-*.md`: extracts, stage briefings, red-team brief, bundle, chair packet) live in the
|
|
381
|
+
run folder and are cleaned up at the end of the run.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Files
|
|
386
|
+
|
|
387
|
+
- `MODEL-NOTES.md` — operating rules, per-model quirks, cost guardrail, and the reviewer-reliability rolling table. **Read it before Stage 0 (council selection and launch); update it (with approval) in Stage 6.**
|
|
388
|
+
- `COUNCIL-DESIGN.md` — the design spec this skill implements (v3). Consult it if a mechanics
|
|
389
|
+
question arises that the skill prose does not resolve.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Command Handlers
|
|
3
|
+
*
|
|
4
|
+
* Extracted from bin/amicus.js to keep the CLI entry point
|
|
5
|
+
* under the 300-line limit.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { validateTaskId, safeSessionDir } = require('./utils/validators');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handle 'amicus setup' command
|
|
14
|
+
* Runs interactive setup wizard or adds an alias via --add-alias
|
|
15
|
+
*/
|
|
16
|
+
async function handleSetup(args) {
|
|
17
|
+
const { addAlias, runInteractiveSetup, runApiKeySetup } = require('./sidecar/setup');
|
|
18
|
+
|
|
19
|
+
// Standalone API key window
|
|
20
|
+
if (args['api-keys']) {
|
|
21
|
+
const success = await runApiKeySetup();
|
|
22
|
+
if (success) {
|
|
23
|
+
console.log('API keys configured successfully.');
|
|
24
|
+
} else {
|
|
25
|
+
console.log('API key setup was not completed.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (args['add-alias']) {
|
|
32
|
+
const spec = args['add-alias'];
|
|
33
|
+
const eqIndex = spec.indexOf('=');
|
|
34
|
+
if (eqIndex === -1) {
|
|
35
|
+
console.error('Error: --add-alias must be in format name=model');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const name = spec.slice(0, eqIndex);
|
|
39
|
+
const model = spec.slice(eqIndex + 1);
|
|
40
|
+
if (!name || !model) {
|
|
41
|
+
console.error('Error: --add-alias must be in format name=model');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
addAlias(name, model);
|
|
45
|
+
console.log(`Alias '${name}' added: ${model}`);
|
|
46
|
+
// F5: warn (never block) when the model is absent from a checkable catalog.
|
|
47
|
+
try {
|
|
48
|
+
const { getCatalog } = require('./utils/model-catalog');
|
|
49
|
+
const { findStaleAliases, suggestReplacements } = require('./utils/alias-audit');
|
|
50
|
+
const catalog = await getCatalog();
|
|
51
|
+
const stale = findStaleAliases([{ alias: name, model, source: 'user-config' }], catalog);
|
|
52
|
+
if (stale.length > 0) {
|
|
53
|
+
const candidates = suggestReplacements(model, catalog);
|
|
54
|
+
console.warn(
|
|
55
|
+
`Warning: '${model}' not found in the model catalog.` +
|
|
56
|
+
(candidates.length > 0 ? ` Did you mean: ${candidates.join(', ')}` : '') +
|
|
57
|
+
`\nDouble-check with: amicus models --search ${model.split('/').pop()}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
} catch { /* warn-only path */ }
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await runInteractiveSetup();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handle 'sidecar abort' command
|
|
69
|
+
* Marks a running session as aborted
|
|
70
|
+
*/
|
|
71
|
+
async function handleAbort(args) {
|
|
72
|
+
if (args.all) {
|
|
73
|
+
const project = args.cwd || process.cwd();
|
|
74
|
+
const { enumerateSessions } = require('./sidecar/read');
|
|
75
|
+
const { markAborted } = require('./utils/session-abort');
|
|
76
|
+
const { resolveExistingSessionDir } = require('./session-manager');
|
|
77
|
+
// A session may complete between enumeration and the write (TOCTOU); the
|
|
78
|
+
// window is tiny for a local CLI and markAborted is best-effort, so we count
|
|
79
|
+
// only sessions actually marked aborted.
|
|
80
|
+
const running = enumerateSessions(project, { status: 'running' });
|
|
81
|
+
if (running.length === 0) {
|
|
82
|
+
console.log('No running sessions to abort.');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let aborted = 0;
|
|
86
|
+
for (const s of running) {
|
|
87
|
+
if (markAborted(resolveExistingSessionDir(project, s.id), 'abort --all')) {
|
|
88
|
+
aborted++;
|
|
89
|
+
console.log(`Aborted ${s.id}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log(`Aborted ${aborted} running session(s).`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const taskId = args._[1];
|
|
97
|
+
|
|
98
|
+
if (!taskId) {
|
|
99
|
+
console.error('Error: task_id is required for abort');
|
|
100
|
+
console.error('Usage: sidecar abort <task_id>');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const taskIdCheck = validateTaskId(taskId);
|
|
105
|
+
if (!taskIdCheck.valid) {
|
|
106
|
+
console.error(taskIdCheck.error);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const project = args.cwd || process.cwd();
|
|
111
|
+
const sessionDir = safeSessionDir(project, taskId);
|
|
112
|
+
const metaPath = path.join(sessionDir, 'metadata.json');
|
|
113
|
+
|
|
114
|
+
if (!fs.existsSync(metaPath)) {
|
|
115
|
+
console.error(`Session ${taskId} not found`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let meta;
|
|
120
|
+
try {
|
|
121
|
+
meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
122
|
+
} catch (_err) {
|
|
123
|
+
console.error(`Session ${taskId} has malformed metadata`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const { markAborted } = require('./utils/session-abort');
|
|
127
|
+
|
|
128
|
+
// F4: aborting a wave aborts every still-running leg too.
|
|
129
|
+
if (meta.type === 'wave') {
|
|
130
|
+
const { resolveExistingSessionDir } = require('./session-manager');
|
|
131
|
+
let aborted = 0;
|
|
132
|
+
for (const legId of meta.legs || []) {
|
|
133
|
+
const legDir = resolveExistingSessionDir(project, legId);
|
|
134
|
+
try {
|
|
135
|
+
const legMeta = JSON.parse(fs.readFileSync(path.join(legDir, 'metadata.json'), 'utf-8'));
|
|
136
|
+
// TOCTOU: a leg may complete between this read and markAborted —
|
|
137
|
+
// best-effort, same contract as abort --all above.
|
|
138
|
+
if (legMeta.status === 'running') {
|
|
139
|
+
if (markAborted(legDir, 'wave abort')) { aborted++; }
|
|
140
|
+
}
|
|
141
|
+
} catch { /* skip unreadable leg */ }
|
|
142
|
+
}
|
|
143
|
+
markAborted(sessionDir, 'manual abort');
|
|
144
|
+
console.log(`Wave ${taskId} marked as aborted (${aborted} running leg(s) aborted).`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
markAborted(sessionDir, 'manual abort');
|
|
149
|
+
console.log(`Session ${taskId} marked as aborted.`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Handle 'amicus update' command
|
|
154
|
+
* Updates amicus to the latest version
|
|
155
|
+
*/
|
|
156
|
+
async function handleUpdate() {
|
|
157
|
+
const { performUpdate, getUpdateInfo, initUpdateCheck } = require('./utils/updater');
|
|
158
|
+
await initUpdateCheck();
|
|
159
|
+
const info = getUpdateInfo();
|
|
160
|
+
if (info) {
|
|
161
|
+
console.log(`Updating amicus ${info.current} → ${info.latest}...`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log('Updating amicus to latest...');
|
|
164
|
+
}
|
|
165
|
+
const result = await performUpdate();
|
|
166
|
+
if (result.success) {
|
|
167
|
+
console.log("Updated successfully! Run 'amicus --version' to verify.");
|
|
168
|
+
} else {
|
|
169
|
+
console.error(`Update failed: ${result.error}`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Handle 'amicus mcp' command
|
|
176
|
+
* Starts the MCP server on stdio transport
|
|
177
|
+
*/
|
|
178
|
+
async function handleMcp() {
|
|
179
|
+
const { startMcpServer } = require('./mcp-server');
|
|
180
|
+
await startMcpServer();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
handleSetup,
|
|
185
|
+
handleAbort,
|
|
186
|
+
handleUpdate,
|
|
187
|
+
handleMcp,
|
|
188
|
+
};
|