amicus 4.5.4 → 4.6.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 +71 -0
- package/README.md +1 -1
- package/commands/council.md +1 -1
- package/docs/DISTRIBUTION.md +38 -11
- package/docs/usage.md +1 -1
- package/package.json +3 -2
- package/schemas/council-run.schema.json +20 -0
- package/schemas/council-verdict.schema.json +20 -0
- package/schemas/doctor.schema.json +23 -1
- package/src/cli-council-run-render.js +51 -0
- package/src/cli-handlers-council-run.js +45 -44
- package/src/cli-handlers-council.js +9 -3
- package/src/cli-handlers-doctor.js +16 -37
- package/src/cli-handlers-watch.js +1 -1
- package/src/cli.js +1 -1
- package/src/council/ledger.js +5 -1
- package/src/council/report-html.js +16 -1
- package/src/council/report.js +25 -1
- package/src/council/run-assemble.js +25 -7
- package/src/council/run-budget.js +14 -8
- package/src/council/run-chair.js +21 -4
- package/src/council/run-debate-stage.js +115 -0
- package/src/council/run-degrade.js +44 -0
- package/src/council/run-finalize.js +18 -3
- package/src/council/run-server.js +24 -7
- package/src/council/run-stage2.js +10 -2
- package/src/council/run-stages.js +23 -21
- package/src/council/run.js +39 -67
- package/src/council/verdict.js +74 -8
- package/src/mcp-council-bench.js +45 -0
- package/src/mcp-council-run.js +11 -28
- package/src/mcp-server.js +5 -1
- package/src/mcp-tools.js +8 -0
- package/src/utils/degrade.js +68 -0
- package/src/utils/doctor-degrade.js +51 -0
- package/src/utils/doctor-electron-mcp-check.js +64 -5
- package/src/utils/doctor-engine-check.js +14 -3
- package/src/utils/doctor-mcp-checks.js +10 -3
- package/src/utils/known-flags.js +2 -1
- package/src/utils/remediation-hints.js +5 -3
- package/src/utils/result-schema.js +6 -2
- package/src/utils/session-index-tmp-sweep.js +2 -1
- package/src/workspace/run-scan.js +5 -1
|
@@ -73,7 +73,7 @@ function evaluateEngineInstalls(d) {
|
|
|
73
73
|
* the engine into each via d.repairEngine, then re-report from a fresh scan.
|
|
74
74
|
* Without d.fix — or when nothing is copy-fixable — returns the plain verdict.
|
|
75
75
|
* @param {object} d doctor deps (scanEngineInstalls, fix?, repairEngine?)
|
|
76
|
-
* @returns {Promise<{id,name,status,message,hint}>}
|
|
76
|
+
* @returns {Promise<{id,name,status,message,hint,fixed?,fixDetail?}>}
|
|
77
77
|
*/
|
|
78
78
|
async function evaluateEngineMcp(d) {
|
|
79
79
|
const verdict = evaluateEngineInstalls(d);
|
|
@@ -94,11 +94,22 @@ async function evaluateEngineMcp(d) {
|
|
|
94
94
|
const after = evaluateEngineInstalls(d); // fresh scan reflects the copies
|
|
95
95
|
if (after.status === 'ok') {
|
|
96
96
|
const n = results.length;
|
|
97
|
-
return {
|
|
97
|
+
return {
|
|
98
|
+
...after,
|
|
99
|
+
message: `${after.message} (self-healed ${n} npx-cache ${plural(n, 'copy', 'copies')})`,
|
|
100
|
+
fixed: true,
|
|
101
|
+
fixDetail: `copied the engine into ${n} npx-cache ${plural(n, 'copy', 'copies')}`,
|
|
102
|
+
};
|
|
98
103
|
}
|
|
99
104
|
const failed = results.filter((r) => !r.repaired)
|
|
100
105
|
.map((r) => `${r.pkgDir}${r.reason ? ` — ${r.reason}` : ''}`).join('; ');
|
|
101
|
-
|
|
106
|
+
// Partial credit: some copies healed even though the check overall is still
|
|
107
|
+
// not 'ok' — flag it ONLY when >=1 repair actually succeeded (#84-style rule).
|
|
108
|
+
const healed = results.filter((r) => r.repaired).length;
|
|
109
|
+
const fixFields = healed > 0
|
|
110
|
+
? { fixed: true, fixDetail: `copied the engine into ${healed} npx-cache ${plural(healed, 'copy', 'copies')}` }
|
|
111
|
+
: {};
|
|
112
|
+
return { ...after, message: `${after.message}; self-heal incomplete: ${failed}`, ...fixFields };
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
module.exports = { evaluateEngineInstalls, evaluateEngineMcp };
|
|
@@ -68,14 +68,21 @@ function evaluateLegacyMcpEntry(d) {
|
|
|
68
68
|
}
|
|
69
69
|
if (d.fix) {
|
|
70
70
|
const removed = (d.migrateLegacyMcpEntries() || []).filter(r => r.result === 'removed');
|
|
71
|
+
// Structured fix outcome (v4.6 Plan 3 Task 3): a repaired row carries
|
|
72
|
+
// fixed/fixDetail whenever ANY entry was actually removed, even on the
|
|
73
|
+
// partial-failure path below — a genuine no-op (removed.length === 0)
|
|
74
|
+
// stays unflagged.
|
|
75
|
+
const fixFields = removed.length > 0
|
|
76
|
+
? { fixed: true, fixDetail: `removed the duplicate legacy 'sidecar' entry from ${removed.map(r => r.target).join(', ')}` }
|
|
77
|
+
: {};
|
|
71
78
|
if (removed.length >= dupes.length) {
|
|
72
79
|
const message = `removed legacy entry from: ${removed.map(r => r.target).join(', ')}`;
|
|
73
80
|
return unreadableNote
|
|
74
|
-
? { id, name, status: 'warn', message: `${message}; ${unreadableNote}`, hint: HINTS.removeLegacySidecar }
|
|
75
|
-
: { id, name, status: 'ok', message, hint: null };
|
|
81
|
+
? { id, name, status: 'warn', message: `${message}; ${unreadableNote}`, hint: HINTS.removeLegacySidecar, ...fixFields }
|
|
82
|
+
: { id, name, status: 'ok', message, hint: null, ...fixFields };
|
|
76
83
|
}
|
|
77
84
|
const message = `removed ${removed.length}/${dupes.length} duplicate(s) — could not update every config`;
|
|
78
|
-
return { id, name, status: 'warn', message: unreadableNote ? `${message}; ${unreadableNote}` : message, hint: HINTS.removeLegacySidecar };
|
|
85
|
+
return { id, name, status: 'warn', message: unreadableNote ? `${message}; ${unreadableNote}` : message, hint: HINTS.removeLegacySidecar, ...fixFields };
|
|
79
86
|
}
|
|
80
87
|
const message = `duplicate 'sidecar' entry in ${dupes.map(e => e.target).join(', ')} — doubles the MCP tool list`;
|
|
81
88
|
return { id, name, status: 'warn', message: unreadableNote ? `${message}; ${unreadableNote}` : message, hint: HINTS.removeLegacySidecar };
|
package/src/utils/known-flags.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* Real flags that appear in NO usage block. Rejecting any of these would break
|
|
30
30
|
* working callers, so they are enumerated deliberately rather than derived.
|
|
31
31
|
*
|
|
32
|
-
* ⚠️
|
|
32
|
+
* ⚠️ These are spawned by the MCP server onto its own CLI children
|
|
33
33
|
* (src/mcp-server.js, src/mcp-council-run.js). They are not user-facing and are
|
|
34
34
|
* intentionally undocumented — but they are on the argv of every MCP-launched
|
|
35
35
|
* run, so rejecting them would break the entire MCP surface.
|
|
@@ -39,6 +39,7 @@ const INTERNAL_FLAGS = new Set([
|
|
|
39
39
|
'run-id', // MCP → `council run`: pins the child's run id
|
|
40
40
|
'council-name', // MCP → `council run`: preset name for ledger attribution
|
|
41
41
|
'cowork-process', // MCP → `start`: Cowork process handle for context capture
|
|
42
|
+
'dropped-members', // MCP → 'council run': per-member preset drops as JSON (v4.6 Plan 4)
|
|
42
43
|
|
|
43
44
|
// User-facing but undocumented, and read by real handlers today. Listed so the
|
|
44
45
|
// rejection is a bug fix and not a silent removal of working behaviour; if any
|
|
@@ -33,7 +33,7 @@ const REMEDIATION_HINTS = Object.freeze({
|
|
|
33
33
|
*/
|
|
34
34
|
reinstallEngineAv:
|
|
35
35
|
'npm install -g amicus (a transient install error can roll back the engine binaries — re-run, or: npm cache clean --force && npm install -g amicus). '
|
|
36
|
-
+ 'If your antivirus (e.g. Windows Defender) quarantined opencode.exe, allow-list it first, then reinstall.',
|
|
36
|
+
+ 'If your antivirus (e.g. Windows Defender) quarantined opencode.exe — unverified, but a known cause — allow-list it first, then reinstall.',
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Runtime server-start failure when the opencode engine binary does not
|
|
@@ -41,8 +41,10 @@ const REMEDIATION_HINTS = Object.freeze({
|
|
|
41
41
|
* spawn ENOENT — surfaced by startServer (the missing-binary boundary).
|
|
42
42
|
*/
|
|
43
43
|
engineMissing:
|
|
44
|
-
'OpenCode engine binary not found —
|
|
45
|
-
+ '
|
|
44
|
+
'OpenCode engine binary not found — the cause was not verified. Common causes (unverified): '
|
|
45
|
+
+ 'an install that skipped or rolled back the engine packages, or antivirus quarantine of opencode.exe. '
|
|
46
|
+
+ 'Run "amicus doctor" to check the actual install, reinstall with "npm i -g amicus", '
|
|
47
|
+
+ 'and allow-list opencode.exe in your antivirus if quarantine was the cause.',
|
|
46
48
|
|
|
47
49
|
/** Electron absent — reinstall to add the interactive GUI (headless still works). */
|
|
48
50
|
reinstallElectron: 'npm install -g amicus (reinstall to add Electron)',
|
|
@@ -209,9 +209,10 @@ function buildAuditDoc({ stale, catalogAvailable, gatewayFindings = [] }) {
|
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
211
|
* Build a doctor health-check document (`doctor --json`).
|
|
212
|
-
* @param {{version: string, timestamp: string, checks: Array<{id,name,status,message,hint}
|
|
212
|
+
* @param {{version: string, timestamp: string, checks: Array<{id,name,status,message,hint}>,
|
|
213
|
+
* degrades?: Array<{kind,channel,what,why,effect,remedy?,data?}>}} opts
|
|
213
214
|
*/
|
|
214
|
-
function buildDoctorDoc({ version, timestamp, checks }) {
|
|
215
|
+
function buildDoctorDoc({ version, timestamp, checks, degrades }) {
|
|
215
216
|
return {
|
|
216
217
|
schemaVersion: SCHEMA_VERSION,
|
|
217
218
|
type: 'doctor',
|
|
@@ -219,6 +220,9 @@ function buildDoctorDoc({ version, timestamp, checks }) {
|
|
|
219
220
|
version,
|
|
220
221
|
timestamp,
|
|
221
222
|
checks,
|
|
223
|
+
// v4.6 Plan 3 (spec §4/§6): the shared-vocabulary surface. Additive and
|
|
224
|
+
// OPTIONAL — present only when a check failed or --fix repaired something.
|
|
225
|
+
...(degrades && degrades.length ? { degrades } : {}),
|
|
222
226
|
};
|
|
223
227
|
}
|
|
224
228
|
|
|
@@ -70,7 +70,8 @@ function evaluateSessionIndexTmpSweep(d) {
|
|
|
70
70
|
}
|
|
71
71
|
const remaining = files.length - swept;
|
|
72
72
|
if (remaining === 0) {
|
|
73
|
-
|
|
73
|
+
const fixFields = swept > 0 ? { fixed: true, fixDetail: `swept ${swept} orphaned session-index tmp file(s)` } : {};
|
|
74
|
+
return { id, name, status: 'ok', message: `swept ${swept} orphaned tmp file(s)`, hint: null, ...fixFields };
|
|
74
75
|
}
|
|
75
76
|
return { id, name, status: 'warn', message: `swept ${swept}, ${remaining} remaining (too fresh or unremovable)`, hint: HINTS.sweepSessionIndexTmp };
|
|
76
77
|
}
|
|
@@ -70,7 +70,11 @@ function readPointer(project, runId) {
|
|
|
70
70
|
const id = String(runId).replace(/^council-/, '');
|
|
71
71
|
if (!RUN_ID_RE.test(id)) { return { runId: id, error: 'invalid runId' }; }
|
|
72
72
|
const ptr = runState.readPointer(project, id);
|
|
73
|
-
if (!ptr) {
|
|
73
|
+
if (!ptr) {
|
|
74
|
+
return { runId: id, error: 'pointer missing — run pointers live under the LAUNCH directory '
|
|
75
|
+
+ '(where `council run` was invoked), not --out-dir. If this run used --out-dir, point '
|
|
76
|
+
+ '--project at the launch directory instead.' };
|
|
77
|
+
}
|
|
74
78
|
return { runId: id, runDir: ptr.runDir };
|
|
75
79
|
}
|
|
76
80
|
|