neohive 6.0.3 → 6.1.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/CHANGELOG.md +262 -77
- package/README.md +66 -63
- package/SECURITY.md +8 -6
- package/cli.js +268 -33
- package/dashboard.html +2269 -546
- package/dashboard.js +492 -105
- package/design-system.css +708 -0
- package/design-system.html +264 -0
- package/lib/agents.js +20 -6
- package/lib/audit.js +417 -0
- package/lib/codex-neohive-toml.js +34 -0
- package/lib/github-sync.js +291 -0
- package/lib/hooks.js +173 -0
- package/lib/ide-activity.js +121 -0
- package/logo.svg +1 -0
- package/package.json +11 -2
- package/scripts/check-portable-paths.mjs +74 -0
- package/server.js +1148 -743
- package/tools/channels.js +116 -0
- package/tools/governance.js +471 -0
- package/tools/hooks.js +65 -0
- package/tools/knowledge.js +301 -0
- package/tools/messaging.js +321 -0
- package/tools/safety.js +144 -0
- package/tools/system.js +198 -0
- package/tools/tasks.js +446 -0
- package/tools/workflows.js +286 -0
package/cli.js
CHANGED
|
@@ -4,12 +4,24 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const { execSync } = require('child_process');
|
|
7
|
+
const { upsertNeohiveMcpInToml } = require('./lib/codex-neohive-toml');
|
|
8
|
+
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// CLI_CONFIG — centralized constants for the Neohive CLI
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
const CLI_CONFIG = {
|
|
13
|
+
MCP_TOOL_TIMEOUT_S: 300, // MCP tool timeout written to IDE config files (seconds)
|
|
14
|
+
OLLAMA_DETECT_TIMEOUT_MS: 5000, // timeout for 'ollama --version' probe
|
|
15
|
+
OLLAMA_HEARTBEAT_MS: 10000, // how often Ollama agent writes heartbeat
|
|
16
|
+
OLLAMA_POLL_MS: 2000, // how often Ollama agent polls for messages
|
|
17
|
+
MSG_MAX_CHARS: 10000, // max length of --msg text argument
|
|
18
|
+
};
|
|
7
19
|
|
|
8
20
|
const command = process.argv[2];
|
|
9
21
|
|
|
10
22
|
function printUsage() {
|
|
11
23
|
console.log(`
|
|
12
|
-
Neohive v6.
|
|
24
|
+
Neohive v6.1.0
|
|
13
25
|
The MCP collaboration layer for AI CLI tools.
|
|
14
26
|
|
|
15
27
|
Usage:
|
|
@@ -18,7 +30,10 @@ function printUsage() {
|
|
|
18
30
|
npx neohive init --gemini Configure for Gemini CLI only
|
|
19
31
|
npx neohive init --codex Configure for Codex CLI only
|
|
20
32
|
npx neohive init --cursor Configure for Cursor IDE only (.cursor/mcp.json)
|
|
33
|
+
npx neohive init --vscode Configure for VS Code GitHub Copilot
|
|
34
|
+
npx neohive init --antigravity Configure for Antigravity IDE
|
|
21
35
|
npx neohive init --all Configure for all supported CLIs
|
|
36
|
+
npx neohive mcp Start MCP stdio server (used internally by IDE configs)
|
|
22
37
|
npx neohive init --ollama Setup Ollama local LLM bridge
|
|
23
38
|
npx neohive init --template T Initialize with a team template
|
|
24
39
|
npx neohive serve Run MCP server in HTTP mode (port 4321)
|
|
@@ -69,7 +84,7 @@ function detectCLIs() {
|
|
|
69
84
|
// Detect Ollama installation
|
|
70
85
|
function detectOllama() {
|
|
71
86
|
try {
|
|
72
|
-
const version = execSync('ollama --version', { encoding: 'utf8', timeout:
|
|
87
|
+
const version = execSync('ollama --version', { encoding: 'utf8', timeout: CLI_CONFIG.OLLAMA_DETECT_TIMEOUT_MS }).trim();
|
|
73
88
|
return { installed: true, version };
|
|
74
89
|
} catch {
|
|
75
90
|
return { installed: false };
|
|
@@ -81,6 +96,16 @@ function dataDir(cwd) {
|
|
|
81
96
|
return path.join(cwd, '.neohive');
|
|
82
97
|
}
|
|
83
98
|
|
|
99
|
+
// Absolute Node binary for MCP configs — CLIs often spawn without Volta/nvm PATH, so plain "node" fails.
|
|
100
|
+
function mcpNodeCommand() {
|
|
101
|
+
return process.execPath;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// MCP stdio "command" for npx — do not use /usr/bin/env (not portable on Windows).
|
|
105
|
+
function mcpNpxCommand() {
|
|
106
|
+
return process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
107
|
+
}
|
|
108
|
+
|
|
84
109
|
// Configure for Claude Code (.mcp.json in project root)
|
|
85
110
|
function setupClaude(serverPath, cwd) {
|
|
86
111
|
const mcpConfigPath = path.join(cwd, '.mcp.json');
|
|
@@ -98,9 +123,9 @@ function setupClaude(serverPath, cwd) {
|
|
|
98
123
|
}
|
|
99
124
|
|
|
100
125
|
mcpConfig.mcpServers['neohive'] = {
|
|
101
|
-
command:
|
|
126
|
+
command: mcpNodeCommand(),
|
|
102
127
|
args: [serverPath],
|
|
103
|
-
timeout:
|
|
128
|
+
timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
|
|
104
129
|
};
|
|
105
130
|
|
|
106
131
|
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
@@ -130,14 +155,199 @@ function setupGemini(serverPath, cwd) {
|
|
|
130
155
|
}
|
|
131
156
|
|
|
132
157
|
settings.mcpServers['neohive'] = {
|
|
133
|
-
command:
|
|
158
|
+
command: mcpNodeCommand(),
|
|
134
159
|
args: [serverPath],
|
|
135
|
-
timeout:
|
|
160
|
+
timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
|
|
136
161
|
trust: true,
|
|
137
162
|
};
|
|
138
163
|
|
|
164
|
+
if (!settings.context) settings.context = {};
|
|
165
|
+
if (!settings.context.files) settings.context.files = [];
|
|
166
|
+
if (!settings.context.files.includes('GEMINI.md')) {
|
|
167
|
+
settings.context.files.push('GEMINI.md');
|
|
168
|
+
}
|
|
169
|
+
|
|
139
170
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
140
171
|
console.log(' [ok] Gemini CLI: .gemini/settings.json updated');
|
|
172
|
+
|
|
173
|
+
// Write GEMINI.md agent rules if not already present
|
|
174
|
+
const geminiMdPath = path.join(cwd, 'GEMINI.md');
|
|
175
|
+
if (!fs.existsSync(geminiMdPath)) {
|
|
176
|
+
fs.writeFileSync(geminiMdPath, geminiMdTemplate());
|
|
177
|
+
console.log(' [ok] Gemini CLI: GEMINI.md created with agent rules');
|
|
178
|
+
} else {
|
|
179
|
+
console.log(' [skip] GEMINI.md already exists — not overwriting');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function geminiMdTemplate() {
|
|
184
|
+
return `# Neohive Agent — Gemini CLI
|
|
185
|
+
|
|
186
|
+
You are a Neohive team agent. Follow these rules exactly, every session, no exceptions.
|
|
187
|
+
|
|
188
|
+
## First thing to do — always
|
|
189
|
+
|
|
190
|
+
1. Call \`register\` with your assigned name (e.g. \`register(name="Gemini")\`)
|
|
191
|
+
2. Call \`get_briefing\` to load project context and current work
|
|
192
|
+
3. Call \`listen\` to wait for messages from the Coordinator
|
|
193
|
+
|
|
194
|
+
Do NOT explore the codebase, ask questions, or take initiative before completing these 3 steps.
|
|
195
|
+
|
|
196
|
+
## Core rules
|
|
197
|
+
|
|
198
|
+
- **After every action** — call \`listen()\`. This is how you receive your next task.
|
|
199
|
+
- **Before starting a task** — call \`update_task(id, status="in_progress")\`
|
|
200
|
+
- **After finishing a task** — call \`update_task(id, status="done")\`, then report to Coordinator
|
|
201
|
+
- **Before editing a file** — call \`lock_file(path)\`. Call \`unlock_file(path)\` when done.
|
|
202
|
+
- **Check tasks first** — call \`list_tasks()\` before starting anything new. Never work on another agent's task.
|
|
203
|
+
- **Keep messages short** — 2–3 paragraphs max. Lead with what changed, then files, then decisions.
|
|
204
|
+
|
|
205
|
+
## Workflow
|
|
206
|
+
|
|
207
|
+
\`\`\`
|
|
208
|
+
register → get_briefing → listen → [receive task] → update_task(in_progress)
|
|
209
|
+
→ do work → update_task(done) → send_message(Coordinator, summary) → listen
|
|
210
|
+
\`\`\`
|
|
211
|
+
|
|
212
|
+
Repeat the last 5 steps for every task. Never exit the listen loop.
|
|
213
|
+
|
|
214
|
+
## Available MCP tools
|
|
215
|
+
|
|
216
|
+
**Messaging:** \`register\`, \`send_message\`, \`broadcast\`, \`listen\`, \`check_messages\`, \`get_history\`, \`handoff\`
|
|
217
|
+
**Tasks:** \`create_task\`, \`update_task\`, \`list_tasks\`
|
|
218
|
+
**Workflows:** \`create_workflow\`, \`advance_workflow\`, \`workflow_status\`
|
|
219
|
+
**Workspaces:** \`workspace_write\`, \`workspace_read\`, \`workspace_list\`
|
|
220
|
+
**Branching:** \`fork_conversation\`, \`switch_branch\`, \`list_branches\`
|
|
221
|
+
|
|
222
|
+
## What NOT to do
|
|
223
|
+
|
|
224
|
+
- Do not self-assign tasks
|
|
225
|
+
- Do not modify files without a task assigned to you
|
|
226
|
+
- Do not skip \`listen()\` after responding
|
|
227
|
+
- Do not send long messages — be concise
|
|
228
|
+
- Do not ask the Coordinator for permission before starting an assigned task — just do it
|
|
229
|
+
`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Configure for VS Code GitHub Copilot (.vscode/mcp.json + copilot instructions)
|
|
233
|
+
function setupVSCode(cwd) {
|
|
234
|
+
const vscodeDir = path.join(cwd, '.vscode');
|
|
235
|
+
const mcpPath = path.join(vscodeDir, 'mcp.json');
|
|
236
|
+
if (!fs.existsSync(vscodeDir)) fs.mkdirSync(vscodeDir, { recursive: true });
|
|
237
|
+
|
|
238
|
+
let config = { servers: {} };
|
|
239
|
+
if (fs.existsSync(mcpPath)) {
|
|
240
|
+
try {
|
|
241
|
+
config = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
|
242
|
+
if (!config.servers) config.servers = {};
|
|
243
|
+
} catch {
|
|
244
|
+
fs.copyFileSync(mcpPath, mcpPath + '.backup');
|
|
245
|
+
console.log(' [warn] Existing .vscode/mcp.json was invalid — backed up');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
config.servers['neohive'] = {
|
|
250
|
+
type: 'stdio',
|
|
251
|
+
command: mcpNpxCommand(),
|
|
252
|
+
args: ['-y', 'neohive', 'mcp'],
|
|
253
|
+
env: {
|
|
254
|
+
NEOHIVE_DATA_DIR: '${workspaceFolder}/.neohive',
|
|
255
|
+
},
|
|
256
|
+
cwd: '${workspaceFolder}',
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\n');
|
|
260
|
+
console.log(' [ok] VS Code: .vscode/mcp.json updated');
|
|
261
|
+
|
|
262
|
+
// Write copilot instructions
|
|
263
|
+
const githubDir = path.join(cwd, '.github');
|
|
264
|
+
const instructionsPath = path.join(githubDir, 'copilot-instructions.md');
|
|
265
|
+
if (!fs.existsSync(githubDir)) fs.mkdirSync(githubDir, { recursive: true });
|
|
266
|
+
if (!fs.existsSync(instructionsPath)) {
|
|
267
|
+
fs.writeFileSync(instructionsPath, neohiveAgentRules('Copilot'));
|
|
268
|
+
console.log(' [ok] VS Code: .github/copilot-instructions.md created');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Configure for Antigravity (~/.gemini/antigravity/mcp_config.json + skill)
|
|
273
|
+
function setupAntigravity(cwd) {
|
|
274
|
+
const antigravityDir = path.join(os.homedir(), '.gemini', 'antigravity');
|
|
275
|
+
const mcpPath = path.join(antigravityDir, 'mcp_config.json');
|
|
276
|
+
if (!fs.existsSync(antigravityDir)) fs.mkdirSync(antigravityDir, { recursive: true });
|
|
277
|
+
|
|
278
|
+
let config = { mcpServers: {} };
|
|
279
|
+
if (fs.existsSync(mcpPath)) {
|
|
280
|
+
try {
|
|
281
|
+
// Strip JS-style comments before parsing (Antigravity writes JSONC)
|
|
282
|
+
const raw = fs.readFileSync(mcpPath, 'utf8').replace(/\/\/[^\n]*/g, '');
|
|
283
|
+
config = JSON.parse(raw);
|
|
284
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
285
|
+
} catch {
|
|
286
|
+
fs.copyFileSync(mcpPath, mcpPath + '.backup');
|
|
287
|
+
console.log(' [warn] Existing mcp_config.json was invalid — backed up');
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const abDataDir = path.join(path.resolve(cwd), '.neohive').replace(/\\/g, '/');
|
|
292
|
+
|
|
293
|
+
config.mcpServers['neohive'] = {
|
|
294
|
+
command: 'npx',
|
|
295
|
+
args: ['-y', 'neohive', 'mcp'],
|
|
296
|
+
cwd: cwd,
|
|
297
|
+
env: { NEOHIVE_DATA_DIR: abDataDir },
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + '\n');
|
|
301
|
+
console.log(' [ok] Antigravity: ~/.gemini/antigravity/mcp_config.json updated');
|
|
302
|
+
|
|
303
|
+
// Write skill
|
|
304
|
+
const skillDir = path.join(cwd, '.agent', 'skills', 'neohive');
|
|
305
|
+
if (!fs.existsSync(skillDir)) fs.mkdirSync(skillDir, { recursive: true });
|
|
306
|
+
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
307
|
+
if (!fs.existsSync(skillPath)) {
|
|
308
|
+
fs.writeFileSync(skillPath, neohiveAgentRules('Gemini'));
|
|
309
|
+
console.log(' [ok] Antigravity: .agent/skills/neohive/SKILL.md created');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function neohiveAgentRules(defaultName) {
|
|
314
|
+
return `# Neohive Agent
|
|
315
|
+
|
|
316
|
+
You are a Neohive team agent. Follow these rules every session.
|
|
317
|
+
|
|
318
|
+
## On session start — always do this first
|
|
319
|
+
|
|
320
|
+
1. Call \`register\` with your assigned name (e.g. \`register(name="${defaultName}")\`)
|
|
321
|
+
2. Call \`get_briefing\` to load project context and active work
|
|
322
|
+
3. Call \`listen\` to wait for messages from the Coordinator
|
|
323
|
+
|
|
324
|
+
Do NOT explore the codebase or take initiative before completing these 3 steps.
|
|
325
|
+
|
|
326
|
+
## Core rules
|
|
327
|
+
|
|
328
|
+
- **After every action** — call \`listen()\`. This is how you receive your next task.
|
|
329
|
+
- **Before starting a task** — call \`update_task(id, status="in_progress")\`
|
|
330
|
+
- **After finishing** — call \`update_task(id, status="done")\`, report to Coordinator
|
|
331
|
+
- **Before editing a file** — call \`lock_file(path)\`. Call \`unlock_file(path)\` when done.
|
|
332
|
+
- **Check tasks first** — call \`list_tasks()\` before starting anything. Never take another agent's task.
|
|
333
|
+
- **Keep messages short** — 2–3 paragraphs max. Lead with what changed, then files, then decisions.
|
|
334
|
+
|
|
335
|
+
## Workflow loop
|
|
336
|
+
|
|
337
|
+
\`\`\`
|
|
338
|
+
register → get_briefing → listen → [receive task] → update_task(in_progress)
|
|
339
|
+
→ do work → update_task(done) → send_message(Coordinator, summary) → listen
|
|
340
|
+
\`\`\`
|
|
341
|
+
|
|
342
|
+
Never exit the listen loop.
|
|
343
|
+
|
|
344
|
+
## Available MCP tools (neohive server)
|
|
345
|
+
|
|
346
|
+
**Messaging:** \`register\`, \`send_message\`, \`broadcast\`, \`listen\`, \`check_messages\`, \`get_history\`
|
|
347
|
+
**Tasks:** \`create_task\`, \`update_task\`, \`list_tasks\`
|
|
348
|
+
**Workflows:** \`create_workflow\`, \`advance_workflow\`, \`workflow_status\`
|
|
349
|
+
**Workspaces:** \`workspace_write\`, \`workspace_read\`, \`workspace_list\`
|
|
350
|
+
`;
|
|
141
351
|
}
|
|
142
352
|
|
|
143
353
|
// Configure for Codex CLI (uses .codex/config.toml)
|
|
@@ -160,17 +370,17 @@ function setupCodex(serverPath, cwd) {
|
|
|
160
370
|
fs.copyFileSync(configPath, configPath + '.backup');
|
|
161
371
|
}
|
|
162
372
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
[mcp_servers.neohive]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
373
|
+
const abDataDir = path.join(path.resolve(cwd), '.neohive').replace(/\\/g, '/');
|
|
374
|
+
const envSection =
|
|
375
|
+
`[mcp_servers.neohive.env]\nNEOHIVE_DATA_DIR = ${JSON.stringify(abDataDir)}\n`;
|
|
376
|
+
const hadNeohive = config.includes('[mcp_servers.neohive]');
|
|
377
|
+
config = upsertNeohiveMcpInToml(config, {
|
|
378
|
+
command: mcpNodeCommand(),
|
|
379
|
+
serverPath,
|
|
380
|
+
timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
|
|
381
|
+
envSection: hadNeohive ? undefined : envSection,
|
|
382
|
+
});
|
|
383
|
+
fs.writeFileSync(configPath, config);
|
|
174
384
|
|
|
175
385
|
console.log(' [ok] Codex CLI: .codex/config.toml updated');
|
|
176
386
|
}
|
|
@@ -200,10 +410,10 @@ function setupCursor(serverPath, cwd) {
|
|
|
200
410
|
}
|
|
201
411
|
|
|
202
412
|
mcpConfig.mcpServers['neohive'] = {
|
|
203
|
-
command:
|
|
413
|
+
command: mcpNodeCommand(),
|
|
204
414
|
args: [serverPath],
|
|
205
415
|
env: { NEOHIVE_DATA_DIR: abDataDir },
|
|
206
|
-
timeout:
|
|
416
|
+
timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
|
|
207
417
|
};
|
|
208
418
|
|
|
209
419
|
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
@@ -315,10 +525,10 @@ async function processMessages() {
|
|
|
315
525
|
|
|
316
526
|
// Main loop
|
|
317
527
|
register();
|
|
318
|
-
const hb = setInterval(heartbeat,
|
|
528
|
+
const hb = setInterval(heartbeat, CLI_CONFIG.OLLAMA_HEARTBEAT_MS);
|
|
319
529
|
hb.unref();
|
|
320
530
|
console.log('[' + name + '] Listening for messages... (Ctrl+C to stop)');
|
|
321
|
-
setInterval(processMessages,
|
|
531
|
+
setInterval(processMessages, CLI_CONFIG.OLLAMA_POLL_MS);
|
|
322
532
|
|
|
323
533
|
// Cleanup on exit
|
|
324
534
|
process.on('SIGINT', function() { console.log('\\n[' + name + '] Shutting down.'); process.exit(0); });
|
|
@@ -359,8 +569,12 @@ function init() {
|
|
|
359
569
|
targets = ['codex'];
|
|
360
570
|
} else if (flag === '--cursor') {
|
|
361
571
|
targets = ['cursor'];
|
|
572
|
+
} else if (flag === '--vscode') {
|
|
573
|
+
targets = ['vscode'];
|
|
574
|
+
} else if (flag === '--antigravity') {
|
|
575
|
+
targets = ['antigravity'];
|
|
362
576
|
} else if (flag === '--all') {
|
|
363
|
-
targets = ['claude', 'gemini', 'codex', 'cursor'];
|
|
577
|
+
targets = ['claude', 'gemini', 'codex', 'cursor', 'vscode', 'antigravity'];
|
|
364
578
|
} else if (flag === '--ollama') {
|
|
365
579
|
const ollama = detectOllama();
|
|
366
580
|
if (!ollama.installed) {
|
|
@@ -389,10 +603,12 @@ function init() {
|
|
|
389
603
|
|
|
390
604
|
for (const target of targets) {
|
|
391
605
|
switch (target) {
|
|
392
|
-
case 'claude':
|
|
393
|
-
case 'gemini':
|
|
394
|
-
case 'codex':
|
|
395
|
-
case 'cursor':
|
|
606
|
+
case 'claude': setupClaude(serverPath, cwd); break;
|
|
607
|
+
case 'gemini': setupGemini(serverPath, cwd); break;
|
|
608
|
+
case 'codex': setupCodex(serverPath, cwd); break;
|
|
609
|
+
case 'cursor': setupCursor(serverPath, cwd); break;
|
|
610
|
+
case 'vscode': setupVSCode(cwd); break;
|
|
611
|
+
case 'antigravity': setupAntigravity(cwd); break;
|
|
396
612
|
}
|
|
397
613
|
}
|
|
398
614
|
|
|
@@ -420,6 +636,9 @@ function init() {
|
|
|
420
636
|
? ' Neohive is ready! Restart Cursor (or reload MCP tools) and restart any terminal CLIs you use.'
|
|
421
637
|
: ' Neohive is ready! Restart your CLI to pick up the MCP tools.'
|
|
422
638
|
);
|
|
639
|
+
console.log(' MCP server command is your current Node binary (works when the IDE has no Volta/nvm in PATH):');
|
|
640
|
+
console.log(' ' + mcpNodeCommand());
|
|
641
|
+
console.log(' Re-run `npx neohive init` after switching machines or Node versions.');
|
|
423
642
|
console.log('');
|
|
424
643
|
|
|
425
644
|
// Show template if --template was provided
|
|
@@ -442,7 +661,7 @@ function init() {
|
|
|
442
661
|
console.log(' Tip: Use "npx neohive init --template pair" for ready-made prompts.');
|
|
443
662
|
console.log('');
|
|
444
663
|
console.log(' \x1b[1m Monitor:\x1b[0m');
|
|
445
|
-
console.log(' npx neohive dashboard');
|
|
664
|
+
console.log(' npx neohive dashboard → http://localhost:3000 (set NEOHIVE_PORT to change)');
|
|
446
665
|
console.log(' npx neohive status');
|
|
447
666
|
console.log(' npx neohive doctor');
|
|
448
667
|
console.log('');
|
|
@@ -610,6 +829,14 @@ function cliMsg() {
|
|
|
610
829
|
process.exit(1);
|
|
611
830
|
}
|
|
612
831
|
const text = textParts.join(' ');
|
|
832
|
+
if (text.length > CLI_CONFIG.MSG_MAX_CHARS) {
|
|
833
|
+
console.error(` Message text exceeds maximum length of ${CLI_CONFIG.MSG_MAX_CHARS} characters.`);
|
|
834
|
+
process.exit(1);
|
|
835
|
+
}
|
|
836
|
+
if (!text.trim().length) {
|
|
837
|
+
console.error(' Message text cannot be empty or whitespace only.');
|
|
838
|
+
process.exit(1);
|
|
839
|
+
}
|
|
613
840
|
const dir = resolveDataDirCli();
|
|
614
841
|
if (!fs.existsSync(dir)) {
|
|
615
842
|
console.error(' No .neohive/ directory found. Run "npx neohive init" first.');
|
|
@@ -625,12 +852,16 @@ function cliMsg() {
|
|
|
625
852
|
timestamp: new Date().toISOString(),
|
|
626
853
|
};
|
|
627
854
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
855
|
+
try {
|
|
856
|
+
const messagesFile = path.join(dir, 'messages.jsonl');
|
|
857
|
+
const historyFile = path.join(dir, 'history.jsonl');
|
|
858
|
+
fs.appendFileSync(messagesFile, JSON.stringify(msg) + '\n');
|
|
859
|
+
fs.appendFileSync(historyFile, JSON.stringify(msg) + '\n');
|
|
860
|
+
console.log(' Message sent to ' + recipient + ': ' + text);
|
|
861
|
+
} catch (e) {
|
|
862
|
+
console.error(' Failed to send message: ' + e.message);
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
634
865
|
}
|
|
635
866
|
|
|
636
867
|
function cliStatus() {
|
|
@@ -991,6 +1222,10 @@ switch (command) {
|
|
|
991
1222
|
case 'templates':
|
|
992
1223
|
listTemplates();
|
|
993
1224
|
break;
|
|
1225
|
+
case 'mcp':
|
|
1226
|
+
// Start stdio MCP server — used as the command in all MCP configs: npx neohive mcp
|
|
1227
|
+
require('./server.js');
|
|
1228
|
+
break;
|
|
994
1229
|
case 'serve':
|
|
995
1230
|
serve();
|
|
996
1231
|
break;
|