amicus 4.9.8 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +31 -0
- package/README.md +2 -1
- package/bin/amicus.js +5 -0
- package/docs/ROADMAP.md +32 -4
- package/docs/architecture-map.md +22 -2
- package/docs/configuration.md +14 -8
- package/docs/usage.md +22 -3
- package/electron/ipc-setup.js +6 -9
- package/electron/setup-ui-alias-groups.js +29 -124
- package/package.json +1 -1
- package/src/cli-handlers.js +8 -1
- package/src/cli.js +11 -0
- package/src/sidecar/aliases-review-gate.js +65 -0
- package/src/sidecar/aliases-review-prompt.js +91 -0
- package/src/sidecar/aliases-review-render.js +116 -0
- package/src/sidecar/aliases-review.js +298 -0
- package/src/sidecar/aliases.js +279 -0
- package/src/sidecar/models.js +20 -7
- package/src/sidecar/setup.js +20 -18
- package/src/utils/alias-groups.js +128 -0
- package/src/utils/alias-proposals.js +151 -0
- package/src/utils/alias-resolver.js +1 -1
- package/src/utils/alias-state.js +88 -0
- package/src/utils/alias-store.js +65 -0
- package/src/utils/config.js +10 -5
- package/src/utils/model-id-siblings.js +106 -0
- package/src/utils/model-validator.js +1 -1
- package/src/utils/quick-picks.js +13 -32
- package/src/utils/text-sanitize.js +27 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christian Wagner"
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
All notable changes to Amicus are documented here. Format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semver.
|
|
5
5
|
|
|
6
|
+
## [4.10.0] - 2026-09-14
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`amicus aliases`** — your model aliases as a standing command: `following` (the shipped
|
|
11
|
+
pin, moves with releases) vs `pinned` (yours), grouped by vendor; `--review` walks every
|
|
12
|
+
proposal in a numbered picker (accept a newer same-tier sibling, follow the shipped pin,
|
|
13
|
+
choose another catalog id, skip, or never ask again — no copy-paste); `--json` for scripts.
|
|
14
|
+
Without a terminal `--review` prints the list, says it is interactive, and exits 1;
|
|
15
|
+
`--unpin <name>` removes a pin (a curated name goes back to following, a custom name is
|
|
16
|
+
deleted); refuses instead when `name` is also your (non-curated) `config.default`, so a
|
|
17
|
+
delete can never leave the default dangling on a key that no longer resolves — pick a new
|
|
18
|
+
default first. The sibling comparator behind "newer same-tier sibling" (and shared with the
|
|
19
|
+
CI alias-pin drift gate, `scripts/check-ci-alias-pins.js`) never reads a size/variant token
|
|
20
|
+
glued to a number (`20b`, `8x22b`) as a version, so a differently-sized variant is never
|
|
21
|
+
offered as a sibling of another. (#238, #249)
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- **A curated alias follows the shipped pin unless you pin it.** A name absent from
|
|
26
|
+
`config.aliases` resolves to the pin amicus ships (this is how the merge always worked);
|
|
27
|
+
a present key is a pin. On save, a key equal to the shipped pin is dropped with a Notice —
|
|
28
|
+
every alias still resolves to the same id it did before. The setup wizard no longer seeds
|
|
29
|
+
the 21 curated ids; it pins only the default you chose, and only when its live flagship
|
|
30
|
+
differs from the shipped pin, and says so. (#238 D1/D6/Q9)
|
|
31
|
+
- **The drift report cannot propose a downgrade.** `models --check`'s family fallback-drift
|
|
32
|
+
line ignores non-authoritative catalog rows and is silent when the OpenRouter namespace was
|
|
33
|
+
rejected this run. (#238 §5)
|
|
34
|
+
- The `--add-alias` copy-paste hints in `models --check`, model validation and alias repair now
|
|
35
|
+
point at `amicus aliases --review` where the alias is reviewable there. (#238 D4)
|
|
36
|
+
|
|
6
37
|
## [4.9.8] - 2026-09-13
|
|
7
38
|
|
|
8
39
|
### Changed
|
package/README.md
CHANGED
|
@@ -419,6 +419,7 @@ When you don't need a full council — just one other model's take — fork a co
|
|
|
419
419
|
| `amicus status <id>` | One-shot status for a session or fan-out wave (human or `--json`; `--wave <id>` alternative spelling). |
|
|
420
420
|
| `amicus watch <id>` | Live-render a fan-out wave, council run, or session from any terminal until it finishes — in-place table on a TTY, `--plain` milestone lines, or `--json` NDJSON; exit code maps the terminal state. `--ui` opens the [Council Workspace](./docs/council.md#council-workspace-gui) window instead (interactive-only, no `--json`). |
|
|
421
421
|
| `amicus models` | List, search, refresh the catalog, or audit aliases. |
|
|
422
|
+
| `amicus aliases` | Your model aliases — following / pinned; `--review` walks the update proposals, `--unpin <name>` removes a pin, `--json` for scripts. |
|
|
422
423
|
| `amicus doctor` | Diagnose your setup — keys, default model, catalog, aliases, OpenCode binary, Electron, skills, MCP registration, OpenRouter credit (`--json`; `--fix` self-heals what it can). |
|
|
423
424
|
| `amicus spend` | Cross-run cost rollup from the spend ledger, with per-run attribution — total + per-model spend, tokens, and source mix, most-expensive first (`--wave`/`--council`/`--project`/`--model`/`--op`/`--failed` filter it, `--group-by` buckets it, `--since 7d` windows it; `--json` for a versioned doc; shows remaining OpenRouter credit when a key is configured). |
|
|
424
425
|
| `amicus key` | Manage API keys non-interactively: `amicus key <provider> <key>` saves after live validation; `--remove`; bare `amicus key` lists providers. |
|
|
@@ -455,7 +456,7 @@ $ amicus status demo123 --json
|
|
|
455
456
|
"taskId": "demo123",
|
|
456
457
|
"status": "complete",
|
|
457
458
|
"elapsed": "5m 0s",
|
|
458
|
-
"version": "4.
|
|
459
|
+
"version": "4.10.0",
|
|
459
460
|
"model": "google/gemini-2.5-flash",
|
|
460
461
|
"phase": "terminal"
|
|
461
462
|
}
|
package/bin/amicus.js
CHANGED
|
@@ -165,6 +165,11 @@ async function main() {
|
|
|
165
165
|
exitCode = await handleModels(args);
|
|
166
166
|
break;
|
|
167
167
|
}
|
|
168
|
+
case 'aliases': {
|
|
169
|
+
const { handleAliases } = require('../src/sidecar/aliases');
|
|
170
|
+
exitCode = await handleAliases(args);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
168
173
|
case 'council': {
|
|
169
174
|
const { handleCouncil } = require('../src/cli-handlers-council');
|
|
170
175
|
exitCode = await handleCouncil(args);
|
package/docs/ROADMAP.md
CHANGED
|
@@ -13,13 +13,14 @@ lives under **Backlog (tracked, not scheduled)** with everything else that is re
|
|
|
13
13
|
Nothing about the content changed and no judgment about its value is implied; only its status. When
|
|
14
14
|
an org buyer and the org to support them exist, it earns a number then.
|
|
15
15
|
|
|
16
|
-
Amicus is at **v4.
|
|
17
|
-
plumbing; the
|
|
16
|
+
Amicus is at **v4.10.0** (2026-09-14). Each 4.x rev below leads with the benefit, not the
|
|
17
|
+
plumbing; the v4.9.x patch releases carry no section of their own, because each corrected a
|
|
18
18
|
defect rather than adding scope — where one added a surface (v4.9.4's `--thinking` refusals and
|
|
19
19
|
`output-budget` doctor row, v4.9.5's Electron digest gate, v4.9.6's artifact custody, v4.9.7's dual name-table rescue boundary, v4.9.8's per-run seat tool allowlist and its unverified/refused seat census) it did so to
|
|
20
|
-
make an existing promise true, not to widen it.
|
|
20
|
+
make an existing promise true, not to widen it. v4.10.0 adds a surface (`amicus aliases`) and so
|
|
21
|
+
gets a section. See `CHANGELOG.md` for what each one contained.
|
|
21
22
|
|
|
22
|
-
**Status:** v4.0 through **v4.
|
|
23
|
+
**Status:** v4.0 through **v4.10.0** have **shipped**, plus the v4.9.1–v4.9.8 patch releases —
|
|
23
24
|
everything on this page is a record of what landed, not a plan. Composition — the scope that
|
|
24
25
|
carried the number v4.6 here until the degrade-announcement-invariant milestone took the v4.6.0
|
|
25
26
|
release (2026-08-02) — is now an unscheduled candidate for the next rev, tabled in its own section
|
|
@@ -447,6 +448,33 @@ a verdict about a review that never happened.
|
|
|
447
448
|
> to do* — announcing losses, counting money, attributing seats. v4.9 changes what a council can be
|
|
448
449
|
> asked for in the first place, which is a different kind of rev and is scoped as one.
|
|
449
450
|
|
|
451
|
+
## v4.10 — "Your aliases follow the pins" *(follow-or-pin aliases — #238 Phase 1)* — ✅ SHIPPED v4.10.0, 2026-09-14
|
|
452
|
+
**Benefit:** a curated alias tracks the pin amicus ships until you deliberately pin it, and
|
|
453
|
+
`amicus aliases` shows which is which and walks every worthwhile change in a numbered picker —
|
|
454
|
+
no more copy-pasting `--add-alias` lines out of `models --check`.
|
|
455
|
+
|
|
456
|
+
- **★ `amicus aliases`** — every alias as `following` (the shipped pin, moves with releases) or
|
|
457
|
+
`pinned` (yours), grouped by vendor; `--review` proposes one change per pinned alias (a strictly
|
|
458
|
+
newer same-tier sibling, the shipped pin, a same-vendor replacement for an id gone from the
|
|
459
|
+
catalog) and accepts, skips or dismisses it in place; `--json` for scripts; `--unpin <name>`
|
|
460
|
+
removes a pin (a curated name goes back to following, a custom name is deleted, the alias your
|
|
461
|
+
default points at is refused). **Closes the user half of #238** *(L)*
|
|
462
|
+
- **Absence is follow** — a name absent from `config.aliases` resolves to the shipped pin, a
|
|
463
|
+
present key is a pin, and a key equal to the shipped pin is dropped on save with a Notice; the
|
|
464
|
+
setup wizard stops seeding the 21 curated ids and pins only the default you chose, and only when
|
|
465
|
+
its live flagship differs from the shipped one *(M)*
|
|
466
|
+
- **The drift report cannot propose a downgrade** — `models --check`'s fallback-drift line ignores
|
|
467
|
+
non-authoritative catalog rows and is silent when the OpenRouter namespace was rejected *(S)*
|
|
468
|
+
- **The sibling comparator is tier-safe** — lifted out of the CI alias-pin drift gate and shared
|
|
469
|
+
with it; a size or variant token glued to a number (`20b`, `8x22b`, `4o`) is never read as a
|
|
470
|
+
version, so a differently-sized model is never offered as a "newer" sibling *(S)*
|
|
471
|
+
> Why here: the two council rounds on #249 (16 + 13 findings) shaped the release — the typed
|
|
472
|
+
> shipped id follows without a catalog, the comparator's size rule, the `--unpin` default guard
|
|
473
|
+
> and the terminal-escape sanitizer on every alias surface all came out of them. Phases 2–4 of
|
|
474
|
+
> the #238 design (owner mode with `curated-pins.json`, the Electron "Needs review" section, the
|
|
475
|
+
> quiet update notice) are unscheduled; the spec that scopes them ships in the repo under
|
|
476
|
+
> `docs/superpowers/specs/`.
|
|
477
|
+
|
|
450
478
|
## Backlog (tracked, not scheduled)
|
|
451
479
|
|
|
452
480
|
### Enterprise-readiness *(unscheduled — gated on funding / cofounder)*
|
package/docs/architecture-map.md
CHANGED
|
@@ -106,6 +106,11 @@ src/
|
|
|
106
106
|
├── prompts/
|
|
107
107
|
│ └── cowork-agent-prompt.js # Cowork Agent Prompt
|
|
108
108
|
├── sidecar/
|
|
109
|
+
│ ├── aliases-review-gate.js # Pure §5-gate helpers for `amicus aliases --review` (#249 r1 R2/R3), split
|
|
110
|
+
│ ├── aliases-review-prompt.js # The real-readline prompt for `amicus aliases --review`, split out of
|
|
111
|
+
│ ├── aliases-review-render.js # Pure, side-effect-free screen text for `amicus aliases --review` (#238 §4),
|
|
112
|
+
│ ├── aliases-review.js # `amicus aliases --review` (#238 §4): a numbered readline picker over the
|
|
113
|
+
│ ├── aliases.js # `amicus aliases` (#238 D4) — the user's alias map as a standing command.
|
|
109
114
|
│ ├── budget.js
|
|
110
115
|
│ ├── child-sessions.js
|
|
111
116
|
│ ├── context-builder.js # Context Builder Module
|
|
@@ -185,9 +190,13 @@ src/
|
|
|
185
190
|
│ ├── activity-poller.js
|
|
186
191
|
│ ├── agent-mapping.js # Agent Mapping Module
|
|
187
192
|
│ ├── alias-audit.js # Alias Audit (F5) — report + suggest for most classes; doctor --fix auto-repairs one narrow, mechanically-unambiguous class (B3).
|
|
193
|
+
│ ├── alias-groups.js # Vendor-derived alias grouping (issue 213). Moved VERBATIM out of
|
|
194
|
+
│ ├── alias-proposals.js # The alias review ENGINE (#238 §2): pure, no I/O, no prompts. Turns
|
|
188
195
|
│ ├── alias-resolver.js # Alias Resolver Utilities
|
|
189
196
|
│ ├── alias-shadow-writer.js # The alias-shadow notice's WRITE half: say it without ever sinking the run.
|
|
190
197
|
│ ├── alias-shadow.js # Alias-shadow self-diagnosis — name a local alias that repoints a curated one.
|
|
198
|
+
│ ├── alias-state.js # Following-vs-pinned state for model aliases (#238 D1) and the normalization
|
|
199
|
+
│ ├── alias-store.js # The write sinks the alias review flow needs beyond `setup.js :: addAlias`
|
|
191
200
|
│ ├── api-key-store.js # API Key Store — reading, saving, and validating API keys.
|
|
192
201
|
│ ├── api-key-validation.js # API Key Validation — test API keys against provider endpoints.
|
|
193
202
|
│ ├── atomic-write.js # Atomic file write helper.
|
|
@@ -252,6 +261,7 @@ src/
|
|
|
252
261
|
│ ├── model-classification.js # Tri-state catalog classification (#61).
|
|
253
262
|
│ ├── model-descriptor.js # Model-descriptor grammar + RouteResult factories (#61).
|
|
254
263
|
│ ├── model-fetcher.js # Model Fetcher
|
|
264
|
+
│ ├── model-id-siblings.js # The tier-safe sibling comparator (#238 D7). A sibling shares a pin's vendor
|
|
255
265
|
│ ├── model-input-default.js
|
|
256
266
|
│ ├── model-output-limit.js # Issue #218 — the per-model `limit` descriptor amicus hands opencode.
|
|
257
267
|
│ ├── model-shortlist.js # Vendor model shortlist (#138) -- the family -> model second level.
|
|
@@ -573,6 +583,11 @@ evals/
|
|
|
573
583
|
| `pack/pack-store.js` | | `packsDir()`, `canonicalHash()`, `resolvePackRef()`, `readPack()`, `writePack()` |
|
|
574
584
|
| `pack/pack-validate.js` | | `validatePack()`, `KIND_OPTIONS()`, `KINDS()` |
|
|
575
585
|
| `prompts/cowork-agent-prompt.js` | Cowork Agent Prompt | `buildCoworkAgentPrompt()` |
|
|
586
|
+
| `sidecar/aliases-review-gate.js` | Pure §5-gate helpers for `amicus aliases --review` (#249 r1 R2/R3), split | `classifyTypedId()`, `notInCatalogLine()`, `notVerifiedLine()`, `staleCatalogBanner()` |
|
|
587
|
+
| `sidecar/aliases-review-prompt.js` | The real-readline prompt for `amicus aliases --review`, split out of | `createPrompt()` |
|
|
588
|
+
| `sidecar/aliases-review-render.js` | Pure, side-effect-free screen text for `amicus aliases --review` (#238 §4), | `ageLabel()`, `menuFor()`, `menuLineText()`, `renderScreen()`, `refreshingCatalogLine()` |
|
|
589
|
+
| `sidecar/aliases-review.js` | `amicus aliases --review` (#238 §4): a numbered readline picker over the | `runReview()` |
|
|
590
|
+
| `sidecar/aliases.js` | `amicus aliases` (#238 D4) — the user's alias map as a standing command. | `handleAliases()`, `collectAliasView()`, `renderAliasList()`, `buildAliasesDoc()`, `loadDeps()` |
|
|
576
591
|
| `sidecar/budget.js` | | `checkBudget()`, `formatBudgetError()`, `DEFAULT_MAX_COST_PER_MTOK()`, `ASSUMED_OUTPUT_TOKENS()` |
|
|
577
592
|
| `sidecar/child-sessions.js` | | `collectSubtreeUsage()`, `subtreeIsUnknown()`, `SUBTREE_MAX_DEPTH()`, `SUBTREE_MAX_SESSIONS()` |
|
|
578
593
|
| `sidecar/context-builder.js` | Context Builder Module | `buildContext()`, `parseDuration()`, `resolveSessionFile()`, `applyContextFilters()`, `findCoworkSession()` |
|
|
@@ -650,9 +665,13 @@ evals/
|
|
|
650
665
|
| `utils/activity-poller.js` | | `createActivityPoller()`, `killIfAlive()` |
|
|
651
666
|
| `utils/agent-mapping.js` | Agent Mapping Module | `PRIMARY_AGENTS()`, `OPENCODE_AGENTS()`, `HEADLESS_SAFE_AGENTS()`, `mapAgentToOpenCode()`, `isValidAgent()` |
|
|
652
667
|
| `utils/alias-audit.js` | Alias Audit (F5) — report + suggest for most classes; doctor --fix auto-repairs one narrow, mechanically-unambiguous class (B3). | `collectAliasSources()`, `findStaleAliases()`, `findDriftedStoredAliases()`, `suggestReplacements()`, `findFabricatedAliasRepairs()` |
|
|
668
|
+
| `utils/alias-groups.js` | Vendor-derived alias grouping (issue 213). Moved VERBATIM out of | `groupAliases()`, `aliasVendorOf()`, `vendorLabel()`, `titleCaseVendor()`, `PREFERRED_VENDOR_ORDER()` |
|
|
669
|
+
| `utils/alias-proposals.js` | The alias review ENGINE (#238 §2): pure, no I/O, no prompts. Turns | `buildAliasProposals()`, `gatedCatalogIds()` |
|
|
653
670
|
| `utils/alias-resolver.js` | Alias Resolver Utilities | `autoRepairAlias()` |
|
|
654
671
|
| `utils/alias-shadow-writer.js` | The alias-shadow notice's WRITE half: say it without ever sinking the run. | `safeWrite()`, `armStream()`, `writeNoticeToStderr()` |
|
|
655
672
|
| `utils/alias-shadow.js` | Alias-shadow self-diagnosis — name a local alias that repoints a curated one. | `findAliasShadows()`, `formatAliasShadow()`, `noteAliasShadows()`, `auditAliasShadows()` |
|
|
673
|
+
| `utils/alias-state.js` | Following-vs-pinned state for model aliases (#238 D1) and the normalization | `normalizeAliases()`, `listAliasRows()`, `isCurated()` |
|
|
674
|
+
| `utils/alias-store.js` | The write sinks the alias review flow needs beyond `setup.js :: addAlias` | `removeAlias()`, `readDismissals()`, `recordDismissal()` |
|
|
656
675
|
| `utils/api-key-store.js` | API Key Store — reading, saving, and validating API keys. | `getEnvPath()`, `loadEnvEntries()`, `readApiKeys()`, `readApiKeyHints()`, `readApiKeyValues()` |
|
|
657
676
|
| `utils/api-key-validation.js` | API Key Validation — test API keys against provider endpoints. | `validateApiKey()`, `redactSecret()`, `validateOpenRouterKey()`, `checkOpenRouterCredit()`, `OPENROUTER_NO_CREDIT_WARNING()` |
|
|
658
677
|
| `utils/atomic-write.js` | Atomic file write helper. | `writeFileAtomic()` |
|
|
@@ -717,6 +736,7 @@ evals/
|
|
|
717
736
|
| `utils/model-classification.js` | Tri-state catalog classification (#61). | `classifyModel()` |
|
|
718
737
|
| `utils/model-descriptor.js` | Model-descriptor grammar + RouteResult factories (#61). | `GATEWAY_MODES()`, `parseDescriptor()`, `resolved()`, `selectionRequired()`, `routeError()` |
|
|
719
738
|
| `utils/model-fetcher.js` | Model Fetcher | `fetchModelsFromProvider()`, `fetchAllModels()`, `fetchAllModelsDetailed()`, `fetchModelsFromProviderDetailed()`, `providersToFetch()` |
|
|
739
|
+
| `utils/model-id-siblings.js` | The tier-safe sibling comparator (#238 D7). A sibling shares a pin's vendor | `parsePin()`, `compareVersions()`, `newestSibling()` |
|
|
720
740
|
| `utils/model-input-default.js` | | `resolveModelInputOrDefault()` |
|
|
721
741
|
| `utils/model-output-limit.js` | Issue #218 — the per-model `limit` descriptor amicus hands opencode. | `normalizeOutputBudget()`, `buildLimitLookup()`, `computeModelLimit()`, `positiveCount()` |
|
|
722
742
|
| `utils/model-shortlist.js` | Vendor model shortlist (#138) -- the family -> model second level. | `buildModelShortlist()`, `compareShortlistRows()`, `SHORTLIST_LIMIT()` |
|
|
@@ -736,7 +756,7 @@ evals/
|
|
|
736
756
|
| `utils/provider-default-picker.js` | Provider-default picker core (Part 2, Task 4). | `buildProviderDefaultChoices()`, `applyProviderDefault()`, `pricePerMInputFrom()`, `directFormIfSafe()`, `directFormIfProven()` |
|
|
737
757
|
| `utils/provider-default-prompt.js` | Provider-default prompt flow (Part 2, Task 6/7 shared helper). | `runProviderDefaultFlow()`, `formatPrice()`, `formatRow()` |
|
|
738
758
|
| `utils/provider-registry.js` | Provider-capability registry — the single source of truth for provider | `PROVIDERS()`, `getProvider()`, `isDirectProvider()`, `listDirectProviders()`, `PROVIDER_ENV_MAP()` |
|
|
739
|
-
| `utils/quick-picks.js` | Quick-pick resolution (wizard Step 2) — resolves each curated family to | `compareIdsDesc()`, `canonicalRoutesFor()`, `pickCurrent()`, `resolveQuickPicks()`, `
|
|
759
|
+
| `utils/quick-picks.js` | Quick-pick resolution (wizard Step 2) — resolves each curated family to | `compareIdsDesc()`, `canonicalRoutesFor()`, `pickCurrent()`, `resolveQuickPicks()`, `toStorableRoute()` |
|
|
740
760
|
| `utils/read-slice.js` | Byte-bounded slicing for amicus_read (15a.3 / B17). | `sliceForRead()`, `READ_CAP_BYTES()` |
|
|
741
761
|
| `utils/remediation-hints.js` | | |
|
|
742
762
|
| `utils/result-schema-rebuild.js` | | `buildRunResultFromSession()`, `buildWaveResultFromSession()` |
|
|
@@ -757,7 +777,7 @@ evals/
|
|
|
757
777
|
| `utils/shared-server.js` | | `SharedServerManager()` |
|
|
758
778
|
| `utils/spend-ledger.js` | | `appendSpend()`, `readSpendRows()`, `SPEND_LEDGER_FILE()`, `SPEND_LEDGER_SCHEMA_VERSION()` |
|
|
759
779
|
| `utils/start-helpers.js` | Start Command Helpers | `resolveLaunchModel()`, `deriveAlias()`, `maybeOfferProviderDefaults()` |
|
|
760
|
-
| `utils/text-sanitize.js` | One third-party string, safe to render: no escapes, no bidi, one short line. | `collapseExcerpt()`, `MAX_EXCERPT_CHARS()` |
|
|
780
|
+
| `utils/text-sanitize.js` | One third-party string, safe to render: no escapes, no bidi, one short line. | `collapseExcerpt()`, `MAX_EXCERPT_CHARS()`, `safeFragment()`, `MAX_FRAGMENT_CHARS()` |
|
|
761
781
|
| `utils/thinking-validators.js` | Thinking Level Validators | `VARIANT_LEVELS()`, `validateThinkingLevel()` |
|
|
762
782
|
| `utils/ttft.js` | The one honesty predicate for the time-to-first-token probe (v4.9 W13). | `isMeasuredTtft()` |
|
|
763
783
|
| `utils/untrusted-fence.js` | Untrusted sidecar output fence. | `fenceSidecarOutput()`, `defangOutboundFenceTags()`, `OUTBOUND_FENCE_TAGS()` |
|
package/docs/configuration.md
CHANGED
|
@@ -540,7 +540,7 @@ amicus models --check && echo "aliases ok"
|
|
|
540
540
|
|
|
541
541
|
## Model Aliases
|
|
542
542
|
|
|
543
|
-
Aliases are short names that resolve to full provider-prefixed model IDs.
|
|
543
|
+
Aliases are short names that resolve to full provider-prefixed model IDs. Amicus ships a curated set (`gemini`, `gpt`, `opus`, `deepseek`, `claude`, `glm`, …) that resolves to the pins the package ships — an alias you have not pinned FOLLOWS those pins and moves with each release. `amicus setup` no longer copies them into your config; it pins only the default alias you chose, and only when its live flagship differs from the shipped pin. See `amicus aliases` for what resolves on your machine. You add or override aliases with:
|
|
544
544
|
|
|
545
545
|
```bash
|
|
546
546
|
amicus setup --add-alias fast=google/gemini-3.1-flash-lite-preview
|
|
@@ -671,14 +671,14 @@ level includes everything above it.
|
|
|
671
671
|
// Default alias, resolved via `aliases` below when --model is omitted.
|
|
672
672
|
"default": "gemini",
|
|
673
673
|
|
|
674
|
-
// Short name -> full model id
|
|
675
|
-
//
|
|
676
|
-
//
|
|
674
|
+
// Short name -> full model id, for the aliases YOU pinned. A curated alias
|
|
675
|
+
// (gemini, gpt, opus, …) that is ABSENT here FOLLOWS the pin amicus ships and
|
|
676
|
+
// moves with each release; a present key is pinned and reviewable with
|
|
677
|
+
// `amicus aliases --review`. A key equal to the shipped pin is dropped on save
|
|
678
|
+
// (with a Notice) — it is the same as following.
|
|
677
679
|
"aliases": {
|
|
678
|
-
"
|
|
679
|
-
"
|
|
680
|
-
"opus": "anthropic/claude-opus-4",
|
|
681
|
-
"deepseek": "deepseek/deepseek-v3"
|
|
680
|
+
"glm": "openrouter/z-ai/glm-5.4",
|
|
681
|
+
"fast": "google/gemini-3.6-flash-lite"
|
|
682
682
|
},
|
|
683
683
|
|
|
684
684
|
// Named council member lists, e.g. seeded by the Free OpenRouter council
|
|
@@ -700,6 +700,12 @@ level includes everything above it.
|
|
|
700
700
|
"tier_onboarded": true
|
|
701
701
|
},
|
|
702
702
|
|
|
703
|
+
// Written by `amicus aliases --review`'s "never ask again" — keyed
|
|
704
|
+
// `alias@proposedId`; hand-delete a key here to be asked again.
|
|
705
|
+
"aliasReview": {
|
|
706
|
+
"dismissed": { "glm@openrouter/z-ai/glm-5.4": "2026-09-14T00:00:00.000Z" }
|
|
707
|
+
},
|
|
708
|
+
|
|
703
709
|
// User-defined local / OpenAI-compatible providers (v4.2) — written by
|
|
704
710
|
// `amicus provider add`, or hand-edited. id -> normalized entry; `pricing`
|
|
705
711
|
// defaults to {prompt: 0, completion: 0} (the $0 tier) when omitted.
|
package/docs/usage.md
CHANGED
|
@@ -21,6 +21,8 @@ amicus abort --all [--json]
|
|
|
21
21
|
# Setup & maintenance
|
|
22
22
|
amicus setup # Full wizard: keys, default model, aliases
|
|
23
23
|
amicus setup --api-keys # Open just the API-key step
|
|
24
|
+
amicus aliases # your aliases: following (shipped pin) / pinned (yours)
|
|
25
|
+
amicus aliases --review # walk the proposals — no copy-paste
|
|
24
26
|
amicus setup --add-alias fast=google/gemini-3.1-flash-lite-preview # bare canonical, direct-first
|
|
25
27
|
amicus models # List the live catalog
|
|
26
28
|
amicus models --search gemini # Filter by substring
|
|
@@ -397,7 +399,24 @@ direct namespace while the OpenRouter route still serves — a gateway-only rout
|
|
|
397
399
|
no direct sibling is a routing choice, not staleness. Deliberately gateway-only
|
|
398
400
|
entries (e.g. `gpt-pro`) are annotated as such and are never offered a retarget.
|
|
399
401
|
|
|
400
|
-
|
|
402
|
+
### Aliases: following vs pinned
|
|
403
|
+
|
|
404
|
+
A curated alias (`gemini`, `gpt`, `opus`, `deepseek`, …) absent from `config.aliases` **follows** the pin Amicus ships and moves to the new target on every release; a present key **pins** it to that exact id and makes it reviewable.
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
amicus aliases # every alias: following (shipped pin) / pinned (yours), by vendor
|
|
408
|
+
amicus aliases --review # walk outdated pins in a numbered picker — no copy-paste
|
|
409
|
+
amicus aliases --unpin <name> # remove a pin: a curated name goes back to following, a custom name is deleted
|
|
410
|
+
amicus aliases --json # the same document, for scripts
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`--review` offers, per outdated pin: accept a newer same-tier sibling, follow the shipped pin again, choose another catalog id, skip, or never ask again for that pairing — it needs a terminal (without one it prints the list and exits 1). Picking "follow the shipped pin" removes the key from `config.aliases`, and typing that SAME shipped id into "choose another" follows too — unconditionally, whether or not the catalog carries it or is fresh, since it is the same `follow` action either way. A newer same-tier sibling is judged by vendor path, prefix and suffix around the version number; a size or variant token glued to a number (`20b`, `8x22b`) is never read as part of that version, so a differently-sized variant of a model is never offered as a "newer" sibling of it. `amicus setup` no longer seeds all 21 curated ids — it pins only the default alias you chose, and only when its live flagship differs from the shipped pin, and says so.
|
|
414
|
+
|
|
415
|
+
Accepting a catalog-vouched id needs a catalog fresher than 24 hours — the picker refreshes it inline first (an authenticated model-list call to each provider you hold a key for, the same call `amicus models --refresh` makes); `follow` never needs the catalog.
|
|
416
|
+
|
|
417
|
+
`--unpin <name>` refuses instead of writing when `name` is also your `config.default` and is not a curated alias — deleting it would leave the default pointing at a key that no longer resolves, so pick a new default first (`amicus setup`) or unpin a different alias. A curated default is unaffected: it keeps resolving from the shipped table after the unpin, same as any other curated unpin.
|
|
418
|
+
|
|
419
|
+
**Drifted aliases.** `--check` (and the `doctor` aliases row) also flags **`DRIFTED:`** stored aliases — a stored alias whose target is still catalog-listed but no longer matches any route its family currently resolves to (the v4.6.1 `gemini` release-gate class, where `doctor` stayed green while the model behind it had moved on). Each drift line points at `amicus aliases --review`. Drift is informational only by default and does not change the exit code — pass `--strict` alongside `--check` to make curated per-gateway drift (stale or divergent direct/OpenRouter forms) exit non-zero too.
|
|
401
420
|
|
|
402
421
|
**Live probe (`--check --live`).** Presence in the catalog is not proof of service — a stored alias can point at a model id the catalog still lists but the provider has quietly stopped serving (the v4.6.1 `gemini` incident). `--check` alone can't see that; `--live` can, by actually asking. Scope is **stored aliases only** (`amicus setup --add-alias`) — curated defaults follow the catalog by construction and have no "was it actually served" question for a live probe to answer. **This spends real money — one tiny leg per stored alias** — every probed alias gets one ordinary engine leg on a single quiet fan-out wave, with a real session dir and a real spend-ledger row, exactly as if you'd run it yourself.
|
|
403
422
|
|
|
@@ -417,7 +436,7 @@ Each stored alias resolves to one of three outcomes:
|
|
|
417
436
|
|
|
418
437
|
**Validation on launch.** `start` and `fanout` validate the model against the catalog before launching. For an explicit `--model` on `continue`/`resume` this is **blocking** (a typo'd model fails fast with suggestions); for a model *inherited* from a prior session it's **advisory**. Skip it any time with `--no-validate-model`, or fix the catalog with `amicus models --refresh`.
|
|
419
438
|
|
|
420
|
-
**
|
|
439
|
+
**Curated aliases follow the shipped pins unless you pin them.** The curated aliases (`gemini`, `gpt`, `opus`, …) are shipped and follow the package's pins unless you pin them — pin one, or add an entirely new one, with `amicus setup --add-alias name=provider/model`. To see exactly what resolves on *your* machine, run `amicus aliases` — that is the source of truth.
|
|
421
440
|
|
|
422
441
|
**Full-id passthrough.** You can always bypass aliases and name a model directly. Bare `provider/model` is the canonical, policy-routed form; `openrouter/provider/model` is an explicit override. See [Routing](../README.md#routing) for the full explanation — summary:
|
|
423
442
|
|
|
@@ -542,7 +561,7 @@ $ amicus status demo123 --json
|
|
|
542
561
|
"taskId": "demo123",
|
|
543
562
|
"status": "complete",
|
|
544
563
|
"elapsed": "5m 0s",
|
|
545
|
-
"version": "4.
|
|
564
|
+
"version": "4.10.0",
|
|
546
565
|
"model": "google/gemini-2.5-flash",
|
|
547
566
|
"phase": "terminal"
|
|
548
567
|
}
|
package/electron/ipc-setup.js
CHANGED
|
@@ -176,21 +176,18 @@ function registerSetupHandlers(getMainWindow, { ipcMain = require('electron').ip
|
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
// Read-modify-write: never rewrite an alias the renderer didn't send.
|
|
179
|
-
// aliasWrites values: string = set, null = delete. First run
|
|
179
|
+
// aliasWrites values: string = set, null = delete. First run starts from
|
|
180
|
+
// an empty alias map (issue 238 Q9).
|
|
180
181
|
// councilPicks (optional): when length >= 2, seeds the free council via seedFreeCouncil.
|
|
181
182
|
ipcMain.handle('sidecar:save-config', async (_event, defaultModel, aliasWrites, councilPicks) => {
|
|
182
183
|
try {
|
|
183
184
|
const { loadConfig, saveConfig } = require('../src/utils/config');
|
|
184
185
|
let cfg = loadConfig();
|
|
185
186
|
if (!cfg) {
|
|
186
|
-
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
catalogInfo = await require('../src/utils/model-catalog').getCatalogInfo();
|
|
192
|
-
} catch (_err) { /* offline: pinned seeds */ }
|
|
193
|
-
cfg = { aliases: toLiveSeedAliases(catalogInfo) };
|
|
187
|
+
// issue 238 Q9: nothing is seeded — a curated alias follows the
|
|
188
|
+
// shipped pin by being absent (D1). Only the renderer's explicit
|
|
189
|
+
// writes land.
|
|
190
|
+
cfg = { aliases: {} };
|
|
194
191
|
}
|
|
195
192
|
if (!cfg.aliases) { cfg.aliases = {}; }
|
|
196
193
|
if (defaultModel) { cfg.default = defaultModel; }
|
|
@@ -1,138 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Setup UI - Alias grouping rule (issue 213)
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The grouping rule itself (groupAliases / aliasVendorOf / vendorLabel /
|
|
5
|
+
* titleCaseVendor / PREFERRED_VENDOR_ORDER) now lives in
|
|
6
|
+
* src/utils/alias-groups.js (issue 238 PR1 fix wave F5): `src/sidecar/aliases.js`
|
|
7
|
+
* (the CLI `amicus aliases` list) needed the same grouping, and `src/`
|
|
8
|
+
* requiring from `electron/` is a layering violation the whole-branch review
|
|
9
|
+
* caught. This file re-exports exactly those five symbols -- groupAliases,
|
|
10
|
+
* aliasVendorOf, vendorLabel, titleCaseVendor, PREFERRED_VENDOR_ORDER -- so
|
|
11
|
+
* electron/setup-ui-aliases.js, electron/setup-ui-alias-script.js and every
|
|
12
|
+
* existing test keep working untouched, and keeps its own genuinely UI-only
|
|
13
|
+
* export (NEW_ROUTES_GROUP_LABEL) defined here. `ALIAS_VENDOR_LABELS` (the
|
|
14
|
+
* vendor -> display-name table `vendorLabel` looks up) stayed
|
|
15
|
+
* module-private to alias-groups.js -- it is not one of the five and
|
|
16
|
+
* nothing imports it directly (issue 249 review round 1, finding D2).
|
|
8
17
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* .eslintrc.js. `groupModelsByFamily` (src/utils/model-fetcher.js) is
|
|
16
|
-
* deliberately NOT reused: it keys on `id.split('/')[0]`, so every
|
|
17
|
-
* `openrouter/...` alias would collapse into a single "OpenRouter" bucket —
|
|
18
|
-
* exactly the grouping this file exists to avoid. Its DISPLAY half
|
|
19
|
-
* (PROVIDER_FAMILY_NAMES) is reused below.
|
|
20
|
-
*
|
|
21
|
-
* SHARED-WITH-THE-BROWSER NOTE — deliberately NOT shared. The wizard's inline
|
|
22
|
-
* script cannot `require`, so the browser could only get this rule as a copy:
|
|
23
|
-
* hand-written (silent divergence — a 3-segment direct id like `a/b/c` already
|
|
24
|
-
* splits differently under the two obvious spellings) or serialised from the
|
|
25
|
-
* source below (which would put `slice('openrouter/'.length)` back into the
|
|
18
|
+
* SHARED-WITH-THE-BROWSER NOTE (still applies to the grouping rule, wherever
|
|
19
|
+
* it lives) — deliberately NOT shared. The wizard's inline script cannot
|
|
20
|
+
* `require`, so the browser could only get this rule as a copy: hand-written
|
|
21
|
+
* (silent divergence — a 3-segment direct id like `a/b/c` already splits
|
|
22
|
+
* differently under the two obvious spellings) or serialised from the
|
|
23
|
+
* source (which would put `slice('openrouter/'.length)` back into the
|
|
26
24
|
* page). The page carrying its own gateway-prefix strip is the exact shape
|
|
27
25
|
* issue 214 removed and that tests/setup-ui.test.js still guards
|
|
28
26
|
* ("ships no routing policy to the page: ... no prefix derivation"), because
|
|
29
27
|
* that copy is how a direct id gets fabricated for a namespace that never
|
|
30
28
|
* served it.
|
|
31
29
|
*
|
|
32
|
-
* So there is ONE grouping rule and it lives
|
|
33
|
-
* (setup-ui-alias-script.js) never
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const { vendorOf } = require('../src/sidecar/fallback-chains');
|
|
39
|
-
const { PROVIDER_FAMILY_NAMES, listDirectProviders } = require('../src/utils/provider-registry');
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Display names for vendors seen in alias routes.
|
|
43
|
-
*
|
|
44
|
-
* DISPLAY ONLY — deliberately not folded into provider-registry's PROVIDERS,
|
|
45
|
-
* which is a *capability* registry (env var, direct-vs-gateway, live fetch).
|
|
46
|
-
* KNOWN_PROVIDERS / PROVIDER_ENV_MAP are derived from that list, so adding
|
|
47
|
-
* `z-ai` there would claim Amicus can hold a z-ai API key. The five real
|
|
48
|
-
* providers keep their single source of truth via PROVIDER_FAMILY_NAMES.
|
|
49
|
-
*/
|
|
50
|
-
const ALIAS_VENDOR_LABELS = {
|
|
51
|
-
...PROVIDER_FAMILY_NAMES,
|
|
52
|
-
// Vendors reachable through the gateway (curated + commonly pinned)
|
|
53
|
-
'qwen': 'Qwen',
|
|
54
|
-
'mistralai': 'Mistral AI',
|
|
55
|
-
'z-ai': 'Z.AI',
|
|
56
|
-
'minimax': 'MiniMax',
|
|
57
|
-
'x-ai': 'xAI',
|
|
58
|
-
'moonshotai': 'Moonshot AI',
|
|
59
|
-
'bytedance-seed': 'ByteDance Seed',
|
|
60
|
-
'thinkingmachines': 'Thinking Machines',
|
|
61
|
-
'cognitivecomputations': 'Cognitive Computations',
|
|
62
|
-
'inclusionai': 'InclusionAI',
|
|
63
|
-
'nvidia': 'NVIDIA',
|
|
64
|
-
'cohere': 'Cohere',
|
|
65
|
-
'meta-llama': 'Meta Llama',
|
|
66
|
-
'nousresearch': 'Nous Research',
|
|
67
|
-
'perplexity': 'Perplexity',
|
|
68
|
-
'microsoft': 'Microsoft',
|
|
69
|
-
'ai21': 'AI21',
|
|
70
|
-
'amazon': 'Amazon',
|
|
71
|
-
// Local providers (src/utils/local-providers.js PRESETS / VALID_FLAVORS)
|
|
72
|
-
'ollama': 'Ollama',
|
|
73
|
-
'lmstudio': 'LM Studio',
|
|
74
|
-
'vllm': 'vLLM',
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/** `some-new-vendor` -> `Some New Vendor`, so an unmapped vendor is not a raw slug. */
|
|
78
|
-
function titleCaseVendor(vendor) {
|
|
79
|
-
return String(vendor).split(/[-_]/).filter(Boolean)
|
|
80
|
-
.map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Vendor key for an alias route. Wraps the shared `vendorOf` with the two
|
|
85
|
-
* normalisations issue 213 flagged: case, and the leading `~` of a floating
|
|
86
|
-
* OpenRouter id (`openrouter/~z-ai/glm-latest` must not form a second group
|
|
87
|
-
* next to `z-ai`).
|
|
88
|
-
* @param {string} route @returns {string} '' when there is no usable route
|
|
30
|
+
* So there is ONE grouping rule and it lives server-side (now in
|
|
31
|
+
* src/utils/alias-groups.js). The client (setup-ui-alias-script.js) never
|
|
32
|
+
* derives a vendor: a route added during the session goes into its own
|
|
33
|
+
* clearly-labelled "New routes" group, and vendor filing happens when the
|
|
34
|
+
* server next renders the editor.
|
|
89
35
|
*/
|
|
90
|
-
function aliasVendorOf(route) {
|
|
91
|
-
const v = vendorOf(route).toLowerCase();
|
|
92
|
-
return v.charAt(0) === '~' ? v.slice(1) : v;
|
|
93
|
-
}
|
|
94
36
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* route, and `__proto__`/`constructor` would otherwise return prototype junk.
|
|
99
|
-
* @param {string} vendor @returns {string}
|
|
100
|
-
*/
|
|
101
|
-
function vendorLabel(vendor) {
|
|
102
|
-
if (!vendor) { return 'Other'; }
|
|
103
|
-
const hit = Object.prototype.hasOwnProperty.call(ALIAS_VENDOR_LABELS, vendor)
|
|
104
|
-
? ALIAS_VENDOR_LABELS[vendor] : null;
|
|
105
|
-
return hit || titleCaseVendor(vendor);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** Direct-route vendors render first; everything else sorts by label. */
|
|
109
|
-
const PREFERRED_VENDOR_ORDER = listDirectProviders();
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Bucket an alias map by route vendor.
|
|
113
|
-
* INVARIANT: every own key of `aliases` lands in exactly one returned group —
|
|
114
|
-
* there is no whitelist to miss, and the empty vendor is a real catch-all.
|
|
115
|
-
* Order within a group follows the config's own key order.
|
|
116
|
-
* @param {Object<string,string>} aliases
|
|
117
|
-
* @returns {Array<{vendor: string, label: string, keys: string[]}>}
|
|
118
|
-
*/
|
|
119
|
-
function groupAliases(aliases) {
|
|
120
|
-
const byVendor = new Map();
|
|
121
|
-
for (const key of Object.keys(aliases || {})) {
|
|
122
|
-
const vendor = aliasVendorOf(aliases[key]);
|
|
123
|
-
if (!byVendor.has(vendor)) { byVendor.set(vendor, []); }
|
|
124
|
-
byVendor.get(vendor).push(key);
|
|
125
|
-
}
|
|
126
|
-
const rank = (vendor) => {
|
|
127
|
-
if (!vendor) { return Number.MAX_SAFE_INTEGER; } // catch-all group last
|
|
128
|
-
const i = PREFERRED_VENDOR_ORDER.indexOf(vendor);
|
|
129
|
-
return i === -1 ? PREFERRED_VENDOR_ORDER.length : i;
|
|
130
|
-
};
|
|
131
|
-
return Array.from(byVendor.entries())
|
|
132
|
-
.map(([vendor, keys]) => ({ vendor, label: vendorLabel(vendor), keys }))
|
|
133
|
-
.sort((a, b) => rank(a.vendor) - rank(b.vendor) ||
|
|
134
|
-
a.label.toLowerCase().localeCompare(b.label.toLowerCase()));
|
|
135
|
-
}
|
|
37
|
+
const {
|
|
38
|
+
groupAliases, aliasVendorOf, vendorLabel, titleCaseVendor, PREFERRED_VENDOR_ORDER,
|
|
39
|
+
} = require('../src/utils/alias-groups');
|
|
136
40
|
|
|
137
41
|
/**
|
|
138
42
|
* Heading for the client-side group that holds routes added during THIS
|
|
@@ -153,9 +57,10 @@ function groupAliases(aliases) {
|
|
|
153
57
|
const NEW_ROUTES_GROUP_LABEL = 'New routes (this session)';
|
|
154
58
|
|
|
155
59
|
module.exports = {
|
|
156
|
-
ALIAS_VENDOR_LABELS,
|
|
157
60
|
NEW_ROUTES_GROUP_LABEL,
|
|
158
61
|
aliasVendorOf,
|
|
159
62
|
vendorLabel,
|
|
160
63
|
groupAliases,
|
|
64
|
+
titleCaseVendor,
|
|
65
|
+
PREFERRED_VENDOR_ORDER,
|
|
161
66
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"mcpName": "io.github.BourbonDog/amicus",
|
|
5
5
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
6
6
|
"keywords": [
|
package/src/cli-handlers.js
CHANGED
|
@@ -47,7 +47,14 @@ async function handleSetup(args) {
|
|
|
47
47
|
process.exit(1);
|
|
48
48
|
}
|
|
49
49
|
addAlias(name, model);
|
|
50
|
-
|
|
50
|
+
// Minor (#238 D1 vocabulary): writing the shipped id verbatim is the
|
|
51
|
+
// same as never having pinned at all -- say so, not "added".
|
|
52
|
+
const { getDefaultAliases } = require('./utils/config');
|
|
53
|
+
if (getDefaultAliases()[name] === model) {
|
|
54
|
+
console.log(`Alias '${name}' now follows the shipped recommendation (${model})`);
|
|
55
|
+
} else {
|
|
56
|
+
console.log(`Alias '${name}' added: ${model}`);
|
|
57
|
+
}
|
|
51
58
|
// F5: warn (never block) when the model is absent from a checkable catalog.
|
|
52
59
|
try {
|
|
53
60
|
const { getCatalog } = require('./utils/model-catalog');
|
package/src/cli.js
CHANGED
|
@@ -154,6 +154,7 @@ const BOOLEAN_FLAGS = [
|
|
|
154
154
|
'fix', // doctor: self-heal fixable checks in place (#56)
|
|
155
155
|
'strict', // models --check: exit non-zero on curated per-gateway drift (#gwid Task 6)
|
|
156
156
|
'live', // models --check: opt-in probe of stored aliases with real engine legs (v4.6.2 PR3, spec §6)
|
|
157
|
+
'review', // aliases: interactive picker over the review proposals (#238 D4)
|
|
157
158
|
'render', // council verdict: also refresh report.html next to the decided verdict
|
|
158
159
|
'claude', // init: register for Claude Code only (Task 15)
|
|
159
160
|
'desktop', // init: register for Claude Desktop only (Task 15)
|
|
@@ -391,6 +392,7 @@ Commands:
|
|
|
391
392
|
continue New session building on previous
|
|
392
393
|
read Output session summary/conversation
|
|
393
394
|
models List/search the model catalog, refresh it, audit aliases
|
|
395
|
+
aliases Your model aliases — following / pinned (--review walks the proposals)
|
|
394
396
|
council run <briefing.md> (--models a,b,c | --council <name>) Headless council: reviews → cross-review → tally → chair → verdict
|
|
395
397
|
council tally <input.json> [--json] Tally council findings → tiers/street-cred
|
|
396
398
|
council stats [--json] Reviewer-reliability from the ledger
|
|
@@ -511,6 +513,15 @@ Options for 'models':
|
|
|
511
513
|
engine leg (spends) — served / accepted-but-silent /
|
|
512
514
|
error. Requires --check.
|
|
513
515
|
--json Machine-readable output
|
|
516
|
+
`,
|
|
517
|
+
aliases: `
|
|
518
|
+
Options for 'aliases':
|
|
519
|
+
--review Interactive picker: accept, choose, skip or dismiss each
|
|
520
|
+
proposal (stale pin, newer sibling, differs from the
|
|
521
|
+
shipped pin). Needs a terminal.
|
|
522
|
+
--unpin <alias> Remove a pin: a curated name goes back to following, a
|
|
523
|
+
custom name is deleted
|
|
524
|
+
--json Machine-readable document (aliases + proposals)
|
|
514
525
|
`,
|
|
515
526
|
list: `
|
|
516
527
|
Options for 'list':
|