nexus-prime 7.5.0 → 7.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/dist/cli.js
CHANGED
|
@@ -1905,6 +1905,75 @@ program
|
|
|
1905
1905
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
1906
1906
|
});
|
|
1907
1907
|
});
|
|
1908
|
+
program
|
|
1909
|
+
.command('fix-claude-hooks')
|
|
1910
|
+
.description('Strip stale Nexus hook entries from ~/.claude/settings.json (use after downgrading from a newer version)')
|
|
1911
|
+
.option('--dry-run', 'Show what would change without writing')
|
|
1912
|
+
.option('--workspace', 'Also clean ./.claude/settings.json in the current workspace')
|
|
1913
|
+
.action(async (opts) => {
|
|
1914
|
+
const fs = await import('fs');
|
|
1915
|
+
const path = await import('path');
|
|
1916
|
+
const os = await import('os');
|
|
1917
|
+
const { NEXUS_HOOK_COMMAND_MARKER } = await import('./install/claude-code-hooks.js');
|
|
1918
|
+
const targets = [path.join(os.homedir(), '.claude', 'settings.json')];
|
|
1919
|
+
if (opts.workspace)
|
|
1920
|
+
targets.push(path.join(process.cwd(), '.claude', 'settings.json'));
|
|
1921
|
+
let totalRemoved = 0;
|
|
1922
|
+
for (const target of targets) {
|
|
1923
|
+
if (!fs.existsSync(target)) {
|
|
1924
|
+
console.log(` skip: ${target} (not found)`);
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
let parsed;
|
|
1928
|
+
try {
|
|
1929
|
+
parsed = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
1930
|
+
}
|
|
1931
|
+
catch (err) {
|
|
1932
|
+
console.error(` error: ${target} (invalid JSON: ${err instanceof Error ? err.message : String(err)})`);
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
const hooks = parsed?.hooks;
|
|
1936
|
+
if (!hooks || typeof hooks !== 'object') {
|
|
1937
|
+
console.log(` ok: ${target} (no hooks block)`);
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
let removedHere = 0;
|
|
1941
|
+
for (const event of Object.keys(hooks)) {
|
|
1942
|
+
if (!Array.isArray(hooks[event]))
|
|
1943
|
+
continue;
|
|
1944
|
+
const before = hooks[event];
|
|
1945
|
+
const after = before.filter((entry) => {
|
|
1946
|
+
const entryHooks = entry?.hooks;
|
|
1947
|
+
if (!Array.isArray(entryHooks))
|
|
1948
|
+
return true;
|
|
1949
|
+
const hasNexus = entryHooks.some((h) => typeof h?.command === 'string' && h.command.trimStart().startsWith(NEXUS_HOOK_COMMAND_MARKER));
|
|
1950
|
+
if (hasNexus)
|
|
1951
|
+
removedHere += 1;
|
|
1952
|
+
return !hasNexus;
|
|
1953
|
+
});
|
|
1954
|
+
if (after.length === 0) {
|
|
1955
|
+
delete hooks[event];
|
|
1956
|
+
}
|
|
1957
|
+
else {
|
|
1958
|
+
hooks[event] = after;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
if (Object.keys(hooks).length === 0)
|
|
1962
|
+
delete parsed.hooks;
|
|
1963
|
+
totalRemoved += removedHere;
|
|
1964
|
+
if (removedHere === 0) {
|
|
1965
|
+
console.log(` ok: ${target} (no Nexus entries)`);
|
|
1966
|
+
continue;
|
|
1967
|
+
}
|
|
1968
|
+
if (opts.dryRun) {
|
|
1969
|
+
console.log(` dry-run: ${target} would remove ${removedHere} Nexus hook entry/entries`);
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
fs.writeFileSync(target, JSON.stringify(parsed, null, 2) + '\n', 'utf8');
|
|
1973
|
+
console.log(` fixed: ${target} (removed ${removedHere} entry/entries)`);
|
|
1974
|
+
}
|
|
1975
|
+
console.log(opts.dryRun ? `\nDry-run total: ${totalRemoved} entry/entries.` : `\nDone. Removed ${totalRemoved} stale Nexus hook entry/entries.`);
|
|
1976
|
+
});
|
|
1908
1977
|
program
|
|
1909
1978
|
.command('watch')
|
|
1910
1979
|
.description('Tail live SSE events from the running daemon in a colored terminal feed')
|
|
@@ -1923,4 +1992,26 @@ program
|
|
|
1923
1992
|
if (process.argv.length === 2) {
|
|
1924
1993
|
process.argv.push('setup', 'auto');
|
|
1925
1994
|
}
|
|
1926
|
-
|
|
1995
|
+
// Graceful fallback for unknown `nexus-prime hook <subcommand>` invocations.
|
|
1996
|
+
// When users downgrade from a version that wrote hook entries to
|
|
1997
|
+
// ~/.claude/settings.json, the older binary may not have the referenced
|
|
1998
|
+
// subcommand. Exiting non-zero would fail every Claude Code session start.
|
|
1999
|
+
// Drain piped stdin (Claude pipes JSON into hooks) and exit 0 silently.
|
|
2000
|
+
const KNOWN_HOOK_SUBCOMMANDS = new Set([
|
|
2001
|
+
'bootstrap', 'memory', 'mindkit', 'ghost-pass', 'session-dna',
|
|
2002
|
+
'generate', 'deploy', 'undeploy', 'list', 'help', '--help', '-h',
|
|
2003
|
+
]);
|
|
2004
|
+
if (process.argv[2] === 'hook' && process.argv[3] && !KNOWN_HOOK_SUBCOMMANDS.has(process.argv[3])) {
|
|
2005
|
+
if (!process.stdin.isTTY) {
|
|
2006
|
+
process.stdin.resume();
|
|
2007
|
+
process.stdin.on('data', () => { });
|
|
2008
|
+
process.stdin.on('end', () => process.exit(0));
|
|
2009
|
+
setTimeout(() => process.exit(0), 200).unref();
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
process.exit(0);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
else {
|
|
2016
|
+
program.parse();
|
|
2017
|
+
}
|
|
@@ -95,6 +95,12 @@ if(location.protocol==='file:'){
|
|
|
95
95
|
<div id="board-pyramid"></div>
|
|
96
96
|
</div>
|
|
97
97
|
|
|
98
|
+
<!-- Connected Ecosystem (per-client MCP status) -->
|
|
99
|
+
<div id="connected-ecosystem" class="card" style="margin-bottom:16px;display:none">
|
|
100
|
+
<div class="shd" style="margin-bottom:8px">Connected Ecosystem <span id="ecosystem-stamp" style="font-size:11px;opacity:.5;font-weight:400"></span></div>
|
|
101
|
+
<div id="ecosystem-list" style="display:flex;flex-wrap:wrap;gap:8px"></div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
98
104
|
<!-- Hero KPI row -->
|
|
99
105
|
<div id="hero-stats" role="region" aria-label="Key metrics">
|
|
100
106
|
<div class="hero-stat"><div class="metric-val" id="m-tokens">—</div><div class="metric-lbl">Tokens Saved</div><canvas id="spark-tokens" class="sparkline" width="80" height="24" aria-hidden="true"></canvas></div>
|
|
@@ -116,6 +122,16 @@ if(location.protocol==='file:'){
|
|
|
116
122
|
<!-- Orchestration pipeline (live decomposition + completion) -->
|
|
117
123
|
<div id="orchestration-pipeline" class="card" style="margin-bottom:16px;display:none"></div>
|
|
118
124
|
|
|
125
|
+
<!-- Neural Stream HUD (live SSE event feed strip) -->
|
|
126
|
+
<div id="neural-stream-card" class="card" style="margin-bottom:16px;display:none">
|
|
127
|
+
<div class="shd" style="margin-bottom:8px;display:flex;align-items:center;gap:8px">
|
|
128
|
+
<span>Neural Stream</span>
|
|
129
|
+
<span id="neural-stream-count" style="font-size:11px;opacity:.5;font-weight:400"></span>
|
|
130
|
+
<span style="margin-left:auto;display:inline-flex;align-items:center;gap:4px;font-size:11px;opacity:.6;font-weight:400"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--accent-good,#4ade80);animation:pulse 2s ease-in-out infinite"></span> live</span>
|
|
131
|
+
</div>
|
|
132
|
+
<div id="neural-stream-list" style="display:flex;flex-direction:column;gap:4px;max-height:200px;overflow-y:auto"></div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
119
135
|
<!-- Tool health strip -->
|
|
120
136
|
<div id="tool-health-card" class="card" style="margin-bottom:16px;display:none">
|
|
121
137
|
<div class="shd" style="margin-bottom:8px">Tool Health <span id="tool-health-stamp" style="font-size:11px;opacity:.5;font-weight:400"></span></div>
|
|
@@ -45,6 +45,9 @@ setOnEvent(evt => {
|
|
|
45
45
|
// Runtime tab ingests ALL events regardless of active tab (for live history)
|
|
46
46
|
Runtime.ingestEvent(evt);
|
|
47
47
|
|
|
48
|
+
// Neural Stream HUD ring buffer ingests ALL events too (board card auto-renders)
|
|
49
|
+
Board.pushNeuralEvent?.(evt);
|
|
50
|
+
|
|
48
51
|
const tab = S.tab;
|
|
49
52
|
if (tab === 'board') {
|
|
50
53
|
Board.render();
|
|
@@ -93,6 +93,7 @@ export async function load() {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export function render() {
|
|
96
|
+
renderConnectedEcosystem();
|
|
96
97
|
renderPyramidWidget();
|
|
97
98
|
renderHero();
|
|
98
99
|
renderFirstRunHero();
|
|
@@ -101,6 +102,126 @@ export function render() {
|
|
|
101
102
|
renderEvents();
|
|
102
103
|
renderPulse();
|
|
103
104
|
renderToolHealth();
|
|
105
|
+
renderNeuralStream();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ── Connected Ecosystem (per-client MCP status, restored from v3.8.0) ── */
|
|
109
|
+
const _CLIENT_LABEL = {
|
|
110
|
+
'claude-code': 'Claude Code',
|
|
111
|
+
claude: 'Claude',
|
|
112
|
+
'claude-desktop': 'Claude Desktop',
|
|
113
|
+
codex: 'Codex',
|
|
114
|
+
cursor: 'Cursor',
|
|
115
|
+
opencode: 'Opencode',
|
|
116
|
+
windsurf: 'Windsurf',
|
|
117
|
+
antigravity: 'Antigravity',
|
|
118
|
+
aider: 'Aider',
|
|
119
|
+
continue: 'Continue',
|
|
120
|
+
cline: 'Cline',
|
|
121
|
+
openclaw: 'OpenClaw',
|
|
122
|
+
mcp: 'MCP',
|
|
123
|
+
};
|
|
124
|
+
function _clientStateColor(state) {
|
|
125
|
+
const s = String(state || '').toLowerCase();
|
|
126
|
+
if (s === 'primaryactive' || s === 'active') return 'var(--accent-good, #4ade80)';
|
|
127
|
+
if (s === 'idle' || s === 'detected') return 'var(--accent-warn, #fbbf24)';
|
|
128
|
+
if (s === 'installed') return 'var(--accent, #60a5fa)';
|
|
129
|
+
return 'var(--muted, #6b7280)';
|
|
130
|
+
}
|
|
131
|
+
function renderConnectedEcosystem() {
|
|
132
|
+
const host = document.getElementById('connected-ecosystem');
|
|
133
|
+
if (!host) return;
|
|
134
|
+
const list = document.getElementById('ecosystem-list');
|
|
135
|
+
const stamp = document.getElementById('ecosystem-stamp');
|
|
136
|
+
const op = S.operateSurface;
|
|
137
|
+
const clients = (op?.clients?.detected || op?.clients || []);
|
|
138
|
+
const arr = Array.isArray(clients) ? clients : [];
|
|
139
|
+
if (arr.length === 0) {
|
|
140
|
+
host.style.display = 'none';
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
host.style.display = 'block';
|
|
144
|
+
const primaryId = op?.clients?.primary?.clientId || op?.primaryClient?.clientId || null;
|
|
145
|
+
const ordered = [...arr].sort((a, b) => {
|
|
146
|
+
const ap = a.clientId === primaryId ? 0 : 1;
|
|
147
|
+
const bp = b.clientId === primaryId ? 0 : 1;
|
|
148
|
+
if (ap !== bp) return ap - bp;
|
|
149
|
+
return String(a.clientId || '').localeCompare(String(b.clientId || ''));
|
|
150
|
+
});
|
|
151
|
+
const html = ordered.slice(0, 12).map((c) => {
|
|
152
|
+
const id = String(c.clientId || c.id || 'unknown');
|
|
153
|
+
const label = _CLIENT_LABEL[id] || id;
|
|
154
|
+
const isPrimary = id === primaryId;
|
|
155
|
+
const stateRaw = isPrimary ? 'primaryActive' : (c.state || c.status || 'idle');
|
|
156
|
+
const color = _clientStateColor(stateRaw);
|
|
157
|
+
const stateLabel = isPrimary ? 'PRIMARY·ACTIVE' : String(stateRaw).toUpperCase();
|
|
158
|
+
const lastSeen = c.lastHeartbeatAt || c.lastSeenAt;
|
|
159
|
+
const ago = lastSeen ? timeAgo(lastSeen) : '';
|
|
160
|
+
return `<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:6px;background:var(--surface2);font-size:12px;border-left:3px solid ${color}">
|
|
161
|
+
<span style="width:6px;height:6px;border-radius:50%;background:${color};flex-shrink:0"></span>
|
|
162
|
+
<span style="font-family:var(--font-mono);font-weight:600;color:var(--fg1)">${esc(label)}</span>
|
|
163
|
+
<span style="font-size:10px;color:${color};font-family:var(--font-mono);text-transform:uppercase;letter-spacing:0.5px">${esc(stateLabel)}</span>
|
|
164
|
+
${ago ? `<span style="margin-left:auto;font-size:10px;color:var(--muted)">${esc(ago)}</span>` : ''}
|
|
165
|
+
</div>`;
|
|
166
|
+
}).join('');
|
|
167
|
+
list.innerHTML = html;
|
|
168
|
+
if (stamp) stamp.textContent = `${ordered.length} client${ordered.length === 1 ? '' : 's'}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* ── Neural Stream HUD (live SSE event feed strip, restored from v3.8.0) ── */
|
|
172
|
+
const _NEURAL_MAX = 12;
|
|
173
|
+
function _neuralColor(category) {
|
|
174
|
+
switch (String(category || '').toLowerCase()) {
|
|
175
|
+
case 'memory': return 'oklch(68% 0.22 295)';
|
|
176
|
+
case 'token':
|
|
177
|
+
case 'tokens': return 'oklch(87% 0.19 152)';
|
|
178
|
+
case 'orchestration':
|
|
179
|
+
case 'planner': return 'oklch(83% 0.14 220)';
|
|
180
|
+
case 'synapse':
|
|
181
|
+
case 'operative':
|
|
182
|
+
case 'mission': return 'oklch(83% 0.17 55)';
|
|
183
|
+
case 'architects':
|
|
184
|
+
case 'dispatch':
|
|
185
|
+
case 'sentinel': return 'oklch(75% 0.20 30)';
|
|
186
|
+
case 'darwin':
|
|
187
|
+
case 'governance': return 'oklch(78% 0.18 340)';
|
|
188
|
+
case 'graph': return 'oklch(80% 0.15 180)';
|
|
189
|
+
default: return 'var(--muted, #6b7280)';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
export function pushNeuralEvent(evt) {
|
|
193
|
+
if (!evt || typeof evt !== 'object') return;
|
|
194
|
+
if (!Array.isArray(S.neuralStream)) S.neuralStream = [];
|
|
195
|
+
S.neuralStream.unshift({
|
|
196
|
+
type: String(evt.type || ''),
|
|
197
|
+
category: String(evt.category || ''),
|
|
198
|
+
time: Number(evt.time || Date.now()),
|
|
199
|
+
summary: typeof evt.summary === 'string' ? evt.summary : '',
|
|
200
|
+
});
|
|
201
|
+
if (S.neuralStream.length > _NEURAL_MAX) S.neuralStream.length = _NEURAL_MAX;
|
|
202
|
+
renderNeuralStream();
|
|
203
|
+
}
|
|
204
|
+
function renderNeuralStream() {
|
|
205
|
+
const host = document.getElementById('neural-stream-card');
|
|
206
|
+
if (!host) return;
|
|
207
|
+
const list = document.getElementById('neural-stream-list');
|
|
208
|
+
const count = document.getElementById('neural-stream-count');
|
|
209
|
+
const items = Array.isArray(S.neuralStream) ? S.neuralStream : [];
|
|
210
|
+
if (items.length === 0) {
|
|
211
|
+
host.style.display = 'none';
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
host.style.display = 'block';
|
|
215
|
+
if (count) count.textContent = `${items.length} signal${items.length === 1 ? '' : 's'}`;
|
|
216
|
+
list.innerHTML = items.map((e) => {
|
|
217
|
+
const color = _neuralColor(e.category);
|
|
218
|
+
const ago = timeAgo(e.time);
|
|
219
|
+
return `<div style="display:flex;align-items:baseline;gap:8px;padding:3px 6px;border-left:2px solid ${color};font-size:11px;font-family:var(--font-mono);line-height:1.4">
|
|
220
|
+
<span style="color:${color};font-weight:600;min-width:90px">${esc(e.type)}</span>
|
|
221
|
+
<span style="color:var(--fg1);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(e.summary || e.category || '')}</span>
|
|
222
|
+
<span style="color:var(--muted);font-size:10px;flex-shrink:0">${esc(ago)}</span>
|
|
223
|
+
</div>`;
|
|
224
|
+
}).join('');
|
|
104
225
|
}
|
|
105
226
|
|
|
106
227
|
/* ── Memory pyramid (above kanban) ── */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-prime",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.6.0",
|
|
4
4
|
"description": "Local-first MCP control plane for coding agents with bootstrap-orchestrate execution, memory fabric, token budgeting, and worktree-backed swarms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|