nexus-prime 7.5.0 → 7.6.2
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.
|
@@ -962,8 +962,13 @@ export function buildMcpToolDefinitions() {
|
|
|
962
962
|
required: ['taskId', 'goal', 'findings'],
|
|
963
963
|
},
|
|
964
964
|
},
|
|
965
|
-
|
|
966
|
-
|
|
965
|
+
// When NEXUS_DISABLE_WORKFORCE=1 (the daemon default since v7.6.1),
|
|
966
|
+
// strip the 24 nexus_synapse_* / nexus_architects_* tools from the
|
|
967
|
+
// catalog so the model doesn't see surface area for engines that
|
|
968
|
+
// aren't initialized at runtime. Set NEXUS_DISABLE_WORKFORCE=0 to
|
|
969
|
+
// re-enable both engines + their tools.
|
|
970
|
+
...(process.env.NEXUS_DISABLE_WORKFORCE === '1' ? [] : synapseToolDefinitions),
|
|
971
|
+
...(process.env.NEXUS_DISABLE_WORKFORCE === '1' ? [] : architectsToolDefinitions),
|
|
967
972
|
// ── Workforce (unified worker+job layer) ──────────────────────────
|
|
968
973
|
{
|
|
969
974
|
name: 'nexus_work_enqueue',
|
package/dist/cli.js
CHANGED
|
@@ -788,6 +788,16 @@ program
|
|
|
788
788
|
if (process.env.NEXUS_DAEMON_FAST_START === undefined) {
|
|
789
789
|
process.env.NEXUS_DAEMON_FAST_START = '1';
|
|
790
790
|
}
|
|
791
|
+
// Default the daemon to "workforce-disabled" mode: Synapse + Architects
|
|
792
|
+
// engines stay in the source tree (tests still run, code is preserved)
|
|
793
|
+
// but they don't open their SQLite DBs, run schema migrations, or
|
|
794
|
+
// surface their 24 MCP tools to clients. Saves ~700ms boot, shrinks
|
|
795
|
+
// the model-visible tool catalog, and removes the empty Workforce
|
|
796
|
+
// surface from the dashboard. Re-enable with NEXUS_DISABLE_WORKFORCE=0
|
|
797
|
+
// for users who actually hire operatives + dispatch missions.
|
|
798
|
+
if (process.env.NEXUS_DISABLE_WORKFORCE === undefined) {
|
|
799
|
+
process.env.NEXUS_DISABLE_WORKFORCE = '1';
|
|
800
|
+
}
|
|
791
801
|
const workspaceContext = resolveWorkspaceContext({ workspaceRoot: getWorkspaceRoot() });
|
|
792
802
|
const daemon = new NexusDaemonServer(workspaceContext);
|
|
793
803
|
const { started, record } = await daemon.start();
|
|
@@ -1905,6 +1915,75 @@ program
|
|
|
1905
1915
|
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
1906
1916
|
});
|
|
1907
1917
|
});
|
|
1918
|
+
program
|
|
1919
|
+
.command('fix-claude-hooks')
|
|
1920
|
+
.description('Strip stale Nexus hook entries from ~/.claude/settings.json (use after downgrading from a newer version)')
|
|
1921
|
+
.option('--dry-run', 'Show what would change without writing')
|
|
1922
|
+
.option('--workspace', 'Also clean ./.claude/settings.json in the current workspace')
|
|
1923
|
+
.action(async (opts) => {
|
|
1924
|
+
const fs = await import('fs');
|
|
1925
|
+
const path = await import('path');
|
|
1926
|
+
const os = await import('os');
|
|
1927
|
+
const { NEXUS_HOOK_COMMAND_MARKER } = await import('./install/claude-code-hooks.js');
|
|
1928
|
+
const targets = [path.join(os.homedir(), '.claude', 'settings.json')];
|
|
1929
|
+
if (opts.workspace)
|
|
1930
|
+
targets.push(path.join(process.cwd(), '.claude', 'settings.json'));
|
|
1931
|
+
let totalRemoved = 0;
|
|
1932
|
+
for (const target of targets) {
|
|
1933
|
+
if (!fs.existsSync(target)) {
|
|
1934
|
+
console.log(` skip: ${target} (not found)`);
|
|
1935
|
+
continue;
|
|
1936
|
+
}
|
|
1937
|
+
let parsed;
|
|
1938
|
+
try {
|
|
1939
|
+
parsed = JSON.parse(fs.readFileSync(target, 'utf8'));
|
|
1940
|
+
}
|
|
1941
|
+
catch (err) {
|
|
1942
|
+
console.error(` error: ${target} (invalid JSON: ${err instanceof Error ? err.message : String(err)})`);
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
const hooks = parsed?.hooks;
|
|
1946
|
+
if (!hooks || typeof hooks !== 'object') {
|
|
1947
|
+
console.log(` ok: ${target} (no hooks block)`);
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
let removedHere = 0;
|
|
1951
|
+
for (const event of Object.keys(hooks)) {
|
|
1952
|
+
if (!Array.isArray(hooks[event]))
|
|
1953
|
+
continue;
|
|
1954
|
+
const before = hooks[event];
|
|
1955
|
+
const after = before.filter((entry) => {
|
|
1956
|
+
const entryHooks = entry?.hooks;
|
|
1957
|
+
if (!Array.isArray(entryHooks))
|
|
1958
|
+
return true;
|
|
1959
|
+
const hasNexus = entryHooks.some((h) => typeof h?.command === 'string' && h.command.trimStart().startsWith(NEXUS_HOOK_COMMAND_MARKER));
|
|
1960
|
+
if (hasNexus)
|
|
1961
|
+
removedHere += 1;
|
|
1962
|
+
return !hasNexus;
|
|
1963
|
+
});
|
|
1964
|
+
if (after.length === 0) {
|
|
1965
|
+
delete hooks[event];
|
|
1966
|
+
}
|
|
1967
|
+
else {
|
|
1968
|
+
hooks[event] = after;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
if (Object.keys(hooks).length === 0)
|
|
1972
|
+
delete parsed.hooks;
|
|
1973
|
+
totalRemoved += removedHere;
|
|
1974
|
+
if (removedHere === 0) {
|
|
1975
|
+
console.log(` ok: ${target} (no Nexus entries)`);
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (opts.dryRun) {
|
|
1979
|
+
console.log(` dry-run: ${target} would remove ${removedHere} Nexus hook entry/entries`);
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
fs.writeFileSync(target, JSON.stringify(parsed, null, 2) + '\n', 'utf8');
|
|
1983
|
+
console.log(` fixed: ${target} (removed ${removedHere} entry/entries)`);
|
|
1984
|
+
}
|
|
1985
|
+
console.log(opts.dryRun ? `\nDry-run total: ${totalRemoved} entry/entries.` : `\nDone. Removed ${totalRemoved} stale Nexus hook entry/entries.`);
|
|
1986
|
+
});
|
|
1908
1987
|
program
|
|
1909
1988
|
.command('watch')
|
|
1910
1989
|
.description('Tail live SSE events from the running daemon in a colored terminal feed')
|
|
@@ -1923,4 +2002,26 @@ program
|
|
|
1923
2002
|
if (process.argv.length === 2) {
|
|
1924
2003
|
process.argv.push('setup', 'auto');
|
|
1925
2004
|
}
|
|
1926
|
-
|
|
2005
|
+
// Graceful fallback for unknown `nexus-prime hook <subcommand>` invocations.
|
|
2006
|
+
// When users downgrade from a version that wrote hook entries to
|
|
2007
|
+
// ~/.claude/settings.json, the older binary may not have the referenced
|
|
2008
|
+
// subcommand. Exiting non-zero would fail every Claude Code session start.
|
|
2009
|
+
// Drain piped stdin (Claude pipes JSON into hooks) and exit 0 silently.
|
|
2010
|
+
const KNOWN_HOOK_SUBCOMMANDS = new Set([
|
|
2011
|
+
'bootstrap', 'memory', 'mindkit', 'ghost-pass', 'session-dna',
|
|
2012
|
+
'generate', 'deploy', 'undeploy', 'list', 'help', '--help', '-h',
|
|
2013
|
+
]);
|
|
2014
|
+
if (process.argv[2] === 'hook' && process.argv[3] && !KNOWN_HOOK_SUBCOMMANDS.has(process.argv[3])) {
|
|
2015
|
+
if (!process.stdin.isTTY) {
|
|
2016
|
+
process.stdin.resume();
|
|
2017
|
+
process.stdin.on('data', () => { });
|
|
2018
|
+
process.stdin.on('end', () => process.exit(0));
|
|
2019
|
+
setTimeout(() => process.exit(0), 200).unref();
|
|
2020
|
+
}
|
|
2021
|
+
else {
|
|
2022
|
+
process.exit(0);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
else {
|
|
2026
|
+
program.parse();
|
|
2027
|
+
}
|
|
@@ -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.2",
|
|
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",
|