amicus 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +24 -0
- package/bin/amicus.js +11 -0
- package/electron/fold.js +17 -7
- package/electron/setup-ui-aliases.js +3 -3
- package/electron/setup-ui-styles.js +203 -143
- package/electron/setup-ui.js +2 -2
- package/electron/toolbar.js +29 -25
- package/package.json +1 -1
- package/skills/second-opinion/SKILL.md +1 -1
- package/src/cli-handlers-council.js +11 -4
- package/src/cli.js +1 -0
- package/src/council/report-html.js +39 -9
- package/src/design/fonts/IBMPlexMono-400.ttf +0 -0
- package/src/design/fonts/IBMPlexMono-500.ttf +0 -0
- package/src/design/fonts/IBMPlexMono-600.ttf +0 -0
- package/src/design/fonts/Outfit-300.ttf +0 -0
- package/src/design/fonts/Outfit-400.ttf +0 -0
- package/src/design/fonts/Outfit-500.ttf +0 -0
- package/src/design/fonts/Outfit-600.ttf +0 -0
- package/src/design/fonts/Outfit-700.ttf +0 -0
- package/src/design/fonts/Outfit-800.ttf +0 -0
- package/src/design/tokens.css +134 -0
- package/src/design/tokens.js +51 -0
- package/src/mcp-server.js +5 -1
- package/src/utils/config.js +29 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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": { "name": "Christian Wagner" },
|
|
6
6
|
"homepage": "https://bourbondog.github.io/amicus/",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to Amicus are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-06-29
|
|
9
|
+
|
|
10
|
+
A visual refresh plus a council-reliability fix and a config-dir consolidation.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Amicus design system**: the Electron app (setup wizard, toolbar, fold overlay), the council
|
|
14
|
+
HTML report, and the marketing site now render from one shared token layer (`src/design/tokens.css`
|
|
15
|
+
+ a `src/design/tokens.js` loader) — the clay/gold rail-yard brand on a neutral-black ramp, with
|
|
16
|
+
bundled Outfit + IBM Plex Mono fonts. Previously each surface defined its own colors independently;
|
|
17
|
+
the site is pixel-identical to before, now bound to the shared tokens by a drift-guard test.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **Council reliability ledger now persists.** `amicus council tally` (and the MCP
|
|
21
|
+
`amicus_council_tally`) computed the tally record but never wrote it, so `council-ledger.jsonl`
|
|
22
|
+
stayed empty and `amicus council stats` always reported "No council runs recorded yet." The tally
|
|
23
|
+
finalize step now auto-appends the row(s) — best-effort, so a ledger write failure never fails the
|
|
24
|
+
tally — and a new `--no-ledger` flag computes a record without recording it (e.g. a re-tally).
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Unified config directory.** On startup Amicus now migrates a legacy `~/.config/sidecar` directory
|
|
28
|
+
onto the canonical `~/.config/amicus` once, non-destructively (copy; the legacy dir is kept as a
|
|
29
|
+
backup). This collapses the two-directory split that could let config resolution flip between them
|
|
30
|
+
and orphan your config, catalog, and ledger. A `CONFIG_DIR` override opts out.
|
|
31
|
+
|
|
8
32
|
## [1.4.0] - 2026-06-28
|
|
9
33
|
|
|
10
34
|
### Added
|
package/bin/amicus.js
CHANGED
|
@@ -7,6 +7,17 @@
|
|
|
7
7
|
* Routes commands to appropriate handlers.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
// One-time, non-destructive migration of the legacy ~/.config/sidecar dir onto
|
|
11
|
+
// the canonical ~/.config/amicus (copy; legacy kept as a backup). Runs before
|
|
12
|
+
// any config/credential read so both resolve to the unified dir. Best-effort.
|
|
13
|
+
try {
|
|
14
|
+
const { migrateLegacyConfigDir } = require('../src/utils/config');
|
|
15
|
+
const _m = migrateLegacyConfigDir();
|
|
16
|
+
if (_m && _m.migrated) {
|
|
17
|
+
process.stderr.write(`[amicus] Migrated config ${_m.from} → ${_m.to} (legacy kept as a backup).\n`);
|
|
18
|
+
}
|
|
19
|
+
} catch { /* best-effort: never block startup on migration */ }
|
|
20
|
+
|
|
10
21
|
// Load API keys from all sources: process.env > sidecar .env > auth.json
|
|
11
22
|
const { loadCredentials } = require('../src/utils/env-loader');
|
|
12
23
|
loadCredentials();
|
package/electron/fold.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const { logger } = require('../src/utils/logger');
|
|
9
9
|
const { requestSummaryFromModel } = require('./summary');
|
|
10
10
|
const { getSummaryTemplate } = require('../src/prompt-builder');
|
|
11
|
+
const { tokenCss } = require('../src/design/tokens');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Create a fold handler bound to the window state
|
|
@@ -59,11 +60,11 @@ function createFoldHandler(state) {
|
|
|
59
60
|
if (contentView) {
|
|
60
61
|
await contentView.webContents.executeJavaScript(`
|
|
61
62
|
(function() {
|
|
62
|
-
var overlay = document.getElementById('
|
|
63
|
+
var overlay = document.getElementById('amicus-fold-overlay');
|
|
63
64
|
if (overlay) {
|
|
64
65
|
while (overlay.firstChild) { overlay.removeChild(overlay.firstChild); }
|
|
65
66
|
var msg = document.createElement('div');
|
|
66
|
-
msg.style.cssText = 'color
|
|
67
|
+
msg.style.cssText = 'color:var(--text-1);font-family:var(--font-sans),-apple-system,BlinkMacSystemFont,sans-serif;font-size:15px;font-weight:500;text-align:center;max-width:320px;';
|
|
67
68
|
msg.textContent = 'Summary saved. Tell Claude you\\u2019re done with the Amicus session so it can read the results.';
|
|
68
69
|
overlay.appendChild(msg);
|
|
69
70
|
}
|
|
@@ -110,7 +111,7 @@ function showFoldOverlay(mainWindow, contentView) {
|
|
|
110
111
|
var span = document.createElement('span');
|
|
111
112
|
span.style.cssText = 'display:inline-flex;align-items:center;gap:6px;';
|
|
112
113
|
var spinner = document.createElement('span');
|
|
113
|
-
spinner.style.cssText = 'width:12px;height:12px;border:2px solid rgba(255,255,255,0.3);border-top-color
|
|
114
|
+
spinner.style.cssText = 'width:12px;height:12px;border:2px solid rgba(255,255,255,0.3);border-top-color:var(--on-accent);border-radius:50%;animation:spin 0.8s linear infinite;display:inline-block;';
|
|
114
115
|
span.appendChild(spinner);
|
|
115
116
|
span.appendChild(document.createTextNode('Generating summary\\u2026'));
|
|
116
117
|
btn.appendChild(span);
|
|
@@ -128,6 +129,15 @@ function showFoldOverlay(mainWindow, contentView) {
|
|
|
128
129
|
`).catch(() => {});
|
|
129
130
|
}
|
|
130
131
|
if (contentView) {
|
|
132
|
+
// Scope token vars to the overlay container so var(--x) resolves without
|
|
133
|
+
// touching OpenCode's own :root (which would clobber its CSS variables).
|
|
134
|
+
const rawCss = tokenCss({ absoluteFontUrls: true });
|
|
135
|
+
// Replace only the :root selector (not occurrences inside comments) so the
|
|
136
|
+
// custom properties are defined on #amicus-fold-overlay and inherited by
|
|
137
|
+
// its descendants. @font-face blocks are left at global scope (no selector).
|
|
138
|
+
const scopedCss = rawCss.replace(/:root\s*\{/, '#amicus-fold-overlay {');
|
|
139
|
+
contentView.webContents.insertCSS(scopedCss).catch(() => {});
|
|
140
|
+
|
|
131
141
|
contentView.webContents.executeJavaScript(`
|
|
132
142
|
(function() {
|
|
133
143
|
if (!document.getElementById('fold-spin-style')) {
|
|
@@ -137,20 +147,20 @@ function showFoldOverlay(mainWindow, contentView) {
|
|
|
137
147
|
document.head.appendChild(style);
|
|
138
148
|
}
|
|
139
149
|
var overlay = document.createElement('div');
|
|
140
|
-
overlay.id = '
|
|
150
|
+
overlay.id = 'amicus-fold-overlay';
|
|
141
151
|
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.55);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:99999;';
|
|
142
152
|
|
|
143
153
|
var spinDiv = document.createElement('div');
|
|
144
|
-
spinDiv.style.cssText = 'width:32px;height:32px;border:3px solid
|
|
154
|
+
spinDiv.style.cssText = 'width:32px;height:32px;border:3px solid var(--accent-line);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite;margin-bottom:16px;';
|
|
145
155
|
overlay.appendChild(spinDiv);
|
|
146
156
|
|
|
147
157
|
var titleDiv = document.createElement('div');
|
|
148
|
-
titleDiv.style.cssText = 'color
|
|
158
|
+
titleDiv.style.cssText = 'color:var(--text-1);font-family:var(--font-sans),-apple-system,BlinkMacSystemFont,sans-serif;font-size:15px;font-weight:500;';
|
|
149
159
|
titleDiv.textContent = 'Generating summary\\u2026';
|
|
150
160
|
overlay.appendChild(titleDiv);
|
|
151
161
|
|
|
152
162
|
var subtitleDiv = document.createElement('div');
|
|
153
|
-
subtitleDiv.style.cssText = 'color
|
|
163
|
+
subtitleDiv.style.cssText = 'color:var(--text-3);font-family:var(--font-sans),-apple-system,BlinkMacSystemFont,sans-serif;font-size:12px;margin-top:6px;';
|
|
154
164
|
subtitleDiv.textContent = 'Folding session back to Claude Code';
|
|
155
165
|
overlay.appendChild(subtitleDiv);
|
|
156
166
|
|
|
@@ -51,9 +51,9 @@ function buildAliasEditorHTML(aliases) {
|
|
|
51
51
|
const exampleModel = aliases[exampleAlias] || 'openrouter/google/gemini-3.1-flash-lite-preview';
|
|
52
52
|
|
|
53
53
|
// SVG icons for the example box
|
|
54
|
-
const terminalIcon = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="2" width="14" height="12" rx="2" stroke
|
|
55
|
-
const arrowIcon = `<svg width="20" height="12" viewBox="0 0 20 12" fill="none"><path d="M2 6h14" stroke
|
|
56
|
-
const modelIcon = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="3" stroke
|
|
54
|
+
const terminalIcon = `<svg class="alias-icon-accent" width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="1" y="2" width="14" height="12" rx="2" stroke-width="1.5"/><path d="M4 6l2.5 2L4 10" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.5 10H11" class="alias-icon-faint-path" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
55
|
+
const arrowIcon = `<svg class="alias-icon-accent" width="20" height="12" viewBox="0 0 20 12" fill="none"><path d="M2 6h14" stroke-width="1.5" stroke-linecap="round"/><path d="M13 2l4 4-4 4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
56
|
+
const modelIcon = `<svg class="alias-icon-ok" width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="8" cy="5" r="3" stroke-width="1.5"/><path d="M8 8v3" stroke-width="1.5" stroke-linecap="round"/><circle cx="4" cy="13" r="1.5" stroke-width="1.2"/><circle cx="8" cy="13" r="1.5" stroke-width="1.2"/><circle cx="12" cy="13" r="1.5" stroke-width="1.2"/><path d="M4 11.5L8 11M8 11l4 .5" stroke-width="1" stroke-linecap="round"/></svg>`;
|
|
57
57
|
|
|
58
58
|
const exampleBox = `<div class="routing-example">
|
|
59
59
|
<div class="example-label">How it works</div>
|