neohive 6.0.2 → 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/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.0.0
24
+ Neohive v6.1.0
13
25
  The MCP collaboration layer for AI CLI tools.
14
26
 
15
27
  Usage:
@@ -17,9 +29,15 @@ function printUsage() {
17
29
  npx neohive init --claude Configure for Claude Code only
18
30
  npx neohive init --gemini Configure for Gemini CLI only
19
31
  npx neohive init --codex Configure for Codex CLI only
20
- npx neohive init --all Configure for all detected CLIs
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
35
+ npx neohive init --all Configure for all supported CLIs
36
+ npx neohive mcp Start MCP stdio server (used internally by IDE configs)
21
37
  npx neohive init --ollama Setup Ollama local LLM bridge
22
38
  npx neohive init --template T Initialize with a team template
39
+ npx neohive serve Run MCP server in HTTP mode (port 4321)
40
+ npx neohive serve --port 8080 Custom port for HTTP server
23
41
  npx neohive dashboard Launch web dashboard (http://localhost:3000)
24
42
  npx neohive dashboard --lan Dashboard accessible on LAN
25
43
  npx neohive status Show active agents and tasks
@@ -55,13 +73,18 @@ function detectCLIs() {
55
73
  detected.push('codex');
56
74
  }
57
75
 
76
+ // Cursor IDE: ~/.cursor/ (app support) exists
77
+ if (fs.existsSync(path.join(home, '.cursor'))) {
78
+ detected.push('cursor');
79
+ }
80
+
58
81
  return detected;
59
82
  }
60
83
 
61
84
  // Detect Ollama installation
62
85
  function detectOllama() {
63
86
  try {
64
- const version = execSync('ollama --version', { encoding: 'utf8', timeout: 5000 }).trim();
87
+ const version = execSync('ollama --version', { encoding: 'utf8', timeout: CLI_CONFIG.OLLAMA_DETECT_TIMEOUT_MS }).trim();
65
88
  return { installed: true, version };
66
89
  } catch {
67
90
  return { installed: false };
@@ -73,6 +96,16 @@ function dataDir(cwd) {
73
96
  return path.join(cwd, '.neohive');
74
97
  }
75
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
+
76
109
  // Configure for Claude Code (.mcp.json in project root)
77
110
  function setupClaude(serverPath, cwd) {
78
111
  const mcpConfigPath = path.join(cwd, '.mcp.json');
@@ -90,9 +123,9 @@ function setupClaude(serverPath, cwd) {
90
123
  }
91
124
 
92
125
  mcpConfig.mcpServers['neohive'] = {
93
- command: 'node',
126
+ command: mcpNodeCommand(),
94
127
  args: [serverPath],
95
- timeout: 300,
128
+ timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
96
129
  };
97
130
 
98
131
  fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
@@ -122,14 +155,199 @@ function setupGemini(serverPath, cwd) {
122
155
  }
123
156
 
124
157
  settings.mcpServers['neohive'] = {
125
- command: 'node',
158
+ command: mcpNodeCommand(),
126
159
  args: [serverPath],
127
- timeout: 300,
160
+ timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
128
161
  trust: true,
129
162
  };
130
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
+
131
170
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
132
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
+ `;
133
351
  }
134
352
 
135
353
  // Configure for Codex CLI (uses .codex/config.toml)
@@ -152,21 +370,56 @@ function setupCodex(serverPath, cwd) {
152
370
  fs.copyFileSync(configPath, configPath + '.backup');
153
371
  }
154
372
 
155
- // Only add if not already present
156
- if (!config.includes('[mcp_servers.neohive]')) {
157
- const tomlBlock = `
158
- [mcp_servers.neohive]
159
- command = "node"
160
- args = [${JSON.stringify(serverPath)}]
161
- timeout = 300
162
- `;
163
- config += tomlBlock;
164
- fs.writeFileSync(configPath, config);
165
- }
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);
166
384
 
167
385
  console.log(' [ok] Codex CLI: .codex/config.toml updated');
168
386
  }
169
387
 
388
+ // Configure for Cursor IDE — absolute NEOHIVE_DATA_DIR so Node never sees unexpanded ${workspaceFolder}.
389
+ // If the IDE omits env on spawn, server startup still resolves the hive via lib/resolve-server-data-dir.js
390
+ // (walks cwd ancestors for the same MCP files).
391
+ function setupCursor(serverPath, cwd) {
392
+ const cursorDir = path.join(cwd, '.cursor');
393
+ const mcpConfigPath = path.join(cursorDir, 'mcp.json');
394
+ const abDataDir = path.join(path.resolve(cwd), '.neohive').replace(/\\/g, '/');
395
+
396
+ if (!fs.existsSync(cursorDir)) {
397
+ fs.mkdirSync(cursorDir, { recursive: true });
398
+ }
399
+
400
+ let mcpConfig = { mcpServers: {} };
401
+ if (fs.existsSync(mcpConfigPath)) {
402
+ try {
403
+ mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
404
+ if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
405
+ } catch {
406
+ const backup = mcpConfigPath + '.backup';
407
+ fs.copyFileSync(mcpConfigPath, backup);
408
+ console.log(' [warn] Existing .cursor/mcp.json was invalid — backed up to mcp.json.backup');
409
+ }
410
+ }
411
+
412
+ mcpConfig.mcpServers['neohive'] = {
413
+ command: mcpNodeCommand(),
414
+ args: [serverPath],
415
+ env: { NEOHIVE_DATA_DIR: abDataDir },
416
+ timeout: CLI_CONFIG.MCP_TOOL_TIMEOUT_S,
417
+ };
418
+
419
+ fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + '\n');
420
+ console.log(' [ok] Cursor IDE: .cursor/mcp.json updated');
421
+ }
422
+
170
423
  // Setup Ollama agent bridge script
171
424
  function setupOllama(serverPath, cwd) {
172
425
  const dir = dataDir(cwd);
@@ -272,10 +525,10 @@ async function processMessages() {
272
525
 
273
526
  // Main loop
274
527
  register();
275
- const hb = setInterval(heartbeat, 10000);
528
+ const hb = setInterval(heartbeat, CLI_CONFIG.OLLAMA_HEARTBEAT_MS);
276
529
  hb.unref();
277
530
  console.log('[' + name + '] Listening for messages... (Ctrl+C to stop)');
278
- setInterval(processMessages, 2000);
531
+ setInterval(processMessages, CLI_CONFIG.OLLAMA_POLL_MS);
279
532
 
280
533
  // Cleanup on exit
281
534
  process.on('SIGINT', function() { console.log('\\n[' + name + '] Shutting down.'); process.exit(0); });
@@ -314,8 +567,14 @@ function init() {
314
567
  targets = ['gemini'];
315
568
  } else if (flag === '--codex') {
316
569
  targets = ['codex'];
570
+ } else if (flag === '--cursor') {
571
+ targets = ['cursor'];
572
+ } else if (flag === '--vscode') {
573
+ targets = ['vscode'];
574
+ } else if (flag === '--antigravity') {
575
+ targets = ['antigravity'];
317
576
  } else if (flag === '--all') {
318
- targets = ['claude', 'gemini', 'codex'];
577
+ targets = ['claude', 'gemini', 'codex', 'cursor', 'vscode', 'antigravity'];
319
578
  } else if (flag === '--ollama') {
320
579
  const ollama = detectOllama();
321
580
  if (!ollama.installed) {
@@ -344,14 +603,17 @@ function init() {
344
603
 
345
604
  for (const target of targets) {
346
605
  switch (target) {
347
- case 'claude': setupClaude(serverPath, cwd); break;
348
- case 'gemini': setupGemini(serverPath, cwd); break;
349
- case 'codex': setupCodex(serverPath, cwd); break;
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;
350
612
  }
351
613
  }
352
614
 
353
615
  // Add .neohive/ and MCP config files to .gitignore
354
- const gitignoreEntries = ['.neohive/', '.mcp.json', '.codex/', '.gemini/'];
616
+ const gitignoreEntries = ['.neohive/', '.mcp.json', '.cursor/mcp.json', '.codex/', '.gemini/'];
355
617
  if (fs.existsSync(gitignorePath)) {
356
618
  let content = fs.readFileSync(gitignorePath, 'utf8');
357
619
  const missing = gitignoreEntries.filter(e => !content.includes(e));
@@ -367,8 +629,16 @@ function init() {
367
629
  console.log(' [ok] .gitignore created');
368
630
  }
369
631
 
632
+ const configuredCursor = targets.includes('cursor');
370
633
  console.log('');
371
- console.log(' Neohive is ready! Restart your CLI to pick up the MCP tools.');
634
+ console.log(
635
+ configuredCursor
636
+ ? ' Neohive is ready! Restart Cursor (or reload MCP tools) and restart any terminal CLIs you use.'
637
+ : ' Neohive is ready! Restart your CLI to pick up the MCP tools.'
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.');
372
642
  console.log('');
373
643
 
374
644
  // Show template if --template was provided
@@ -383,11 +653,15 @@ function init() {
383
653
  if (templateFlag) {
384
654
  showTemplate(templateFlag);
385
655
  } else {
386
- console.log(' Open two terminals and start a conversation between agents.');
656
+ if (configuredCursor) {
657
+ console.log(' Cursor: use register + listen() from chat (neohive MCP). Terminal CLIs: one session per agent.');
658
+ } else {
659
+ console.log(' Open two terminals and start a conversation between agents.');
660
+ }
387
661
  console.log(' Tip: Use "npx neohive init --template pair" for ready-made prompts.');
388
662
  console.log('');
389
663
  console.log(' \x1b[1m Monitor:\x1b[0m');
390
- console.log(' npx neohive dashboard');
664
+ console.log(' npx neohive dashboard → http://localhost:3000 (set NEOHIVE_PORT to change)');
391
665
  console.log(' npx neohive status');
392
666
  console.log(' npx neohive doctor');
393
667
  console.log('');
@@ -502,6 +776,17 @@ function showTemplate(templateName) {
502
776
  }
503
777
  }
504
778
 
779
+ function serve() {
780
+ // Parse --port flag
781
+ const portIdx = process.argv.indexOf('--port');
782
+ if (portIdx !== -1 && process.argv[portIdx + 1]) {
783
+ process.env.NEOHIVE_SERVER_PORT = process.argv[portIdx + 1];
784
+ }
785
+ // Signal server.js to use HTTP transport
786
+ process.env.NEOHIVE_TRANSPORT = 'http';
787
+ require('./server.js');
788
+ }
789
+
505
790
  function dashboard() {
506
791
  if (process.argv.includes('--lan')) {
507
792
  process.env.NEOHIVE_LAN = 'true';
@@ -544,6 +829,14 @@ function cliMsg() {
544
829
  process.exit(1);
545
830
  }
546
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
+ }
547
840
  const dir = resolveDataDirCli();
548
841
  if (!fs.existsSync(dir)) {
549
842
  console.error(' No .neohive/ directory found. Run "npx neohive init" first.');
@@ -559,12 +852,16 @@ function cliMsg() {
559
852
  timestamp: new Date().toISOString(),
560
853
  };
561
854
 
562
- const messagesFile = path.join(dir, 'messages.jsonl');
563
- const historyFile = path.join(dir, 'history.jsonl');
564
- fs.appendFileSync(messagesFile, JSON.stringify(msg) + '\n');
565
- fs.appendFileSync(historyFile, JSON.stringify(msg) + '\n');
566
-
567
- console.log(' Message sent to ' + recipient + ': ' + text);
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
+ }
568
865
  }
569
866
 
570
867
  function cliStatus() {
@@ -847,6 +1144,44 @@ function uninstall() {
847
1144
  }
848
1145
  }
849
1146
 
1147
+ // 7. Remove from Cursor IDE project config (.cursor/mcp.json in cwd)
1148
+ const cursorMcpPath = path.join(cwd, '.cursor', 'mcp.json');
1149
+ if (fs.existsSync(cursorMcpPath)) {
1150
+ try {
1151
+ const mcpConfig = JSON.parse(fs.readFileSync(cursorMcpPath, 'utf8'));
1152
+ if (mcpConfig.mcpServers && mcpConfig.mcpServers['neohive']) {
1153
+ delete mcpConfig.mcpServers['neohive'];
1154
+ fs.writeFileSync(cursorMcpPath, JSON.stringify(mcpConfig, null, 2) + '\n');
1155
+ removed.push('Cursor IDE (project): ' + cursorMcpPath);
1156
+ } else {
1157
+ notFound.push('Cursor IDE (project): no neohive entry in .cursor/mcp.json');
1158
+ }
1159
+ } catch (e) {
1160
+ console.log(' [warn] Could not parse ' + cursorMcpPath + ': ' + e.message);
1161
+ }
1162
+ } else {
1163
+ notFound.push('Cursor IDE (project): .cursor/mcp.json not found');
1164
+ }
1165
+
1166
+ // 8. Remove from Cursor IDE user config (~/.cursor/mcp.json)
1167
+ const cursorGlobalPath = path.join(home, '.cursor', 'mcp.json');
1168
+ if (fs.existsSync(cursorGlobalPath)) {
1169
+ try {
1170
+ const mcpConfig = JSON.parse(fs.readFileSync(cursorGlobalPath, 'utf8'));
1171
+ if (mcpConfig.mcpServers && mcpConfig.mcpServers['neohive']) {
1172
+ delete mcpConfig.mcpServers['neohive'];
1173
+ fs.writeFileSync(cursorGlobalPath, JSON.stringify(mcpConfig, null, 2) + '\n');
1174
+ removed.push('Cursor IDE (global): ' + cursorGlobalPath);
1175
+ } else {
1176
+ notFound.push('Cursor IDE (global): no neohive entry');
1177
+ }
1178
+ } catch (e) {
1179
+ console.log(' [warn] Could not parse ' + cursorGlobalPath + ': ' + e.message);
1180
+ }
1181
+ } else {
1182
+ notFound.push('Cursor IDE (global): ~/.cursor/mcp.json not found');
1183
+ }
1184
+
850
1185
  // Print summary
851
1186
  if (removed.length > 0) {
852
1187
  console.log(' Removed neohive from:');
@@ -865,7 +1200,7 @@ function uninstall() {
865
1200
  }
866
1201
  }
867
1202
 
868
- // 7. Check for data directory
1203
+ // 9. Check for data directory
869
1204
  const dataPath = path.join(cwd, '.neohive');
870
1205
  if (fs.existsSync(dataPath)) {
871
1206
  console.log('');
@@ -887,6 +1222,13 @@ switch (command) {
887
1222
  case 'templates':
888
1223
  listTemplates();
889
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;
1229
+ case 'serve':
1230
+ serve();
1231
+ break;
890
1232
  case 'dashboard':
891
1233
  dashboard();
892
1234
  break;
@@ -2,11 +2,61 @@
2
2
  "id": "autonomous-feature",
3
3
  "name": "Autonomous Feature Build",
4
4
  "description": "Full autonomous feature build: architecture, parallel backend+frontend, integration testing. Zero human intervention.",
5
+ "category": "development",
6
+ "conversation_mode": "direct",
5
7
  "agents": [
6
- { "name": "Architect", "role": "Software Architect", "skills": ["design", "interfaces", "architecture"], "prompt": "You are a software architect. Design the feature architecture with clear interfaces. Call verify_and_advance() when done. Use get_work() to stay in the proactive loop." },
7
- { "name": "Backend", "role": "Backend Developer", "skills": ["api", "database", "server", "backend"], "prompt": "You are a backend developer. Implement server-side logic, APIs, and data models. Write unit tests. Call verify_and_advance() when done. Never wait for approval." },
8
- { "name": "Frontend", "role": "Frontend Developer", "skills": ["ui", "css", "components", "frontend"], "prompt": "You are a frontend developer. Implement UI components, pages, and client-side logic. Write component tests. Call verify_and_advance() when done. Never wait for approval." },
9
- { "name": "Tester", "role": "QA Engineer", "skills": ["testing", "qa", "integration", "verification"], "prompt": "You are a QA engineer. Write and run integration tests. Verify all components work together. Call verify_and_advance() when done." }
8
+ {
9
+ "name": "Architect",
10
+ "role": "lead",
11
+ "display_name": "Software Architect",
12
+ "skills": ["design", "interfaces", "architecture", "planning"],
13
+ "responsibilities": [
14
+ "Design feature architecture with clear interfaces",
15
+ "Define contracts between backend and frontend",
16
+ "Create implementation plan"
17
+ ],
18
+ "tools_focus": ["create_workflow", "advance_workflow", "kb_write", "share_file"],
19
+ "prompt": "You are a software architect. Design the feature architecture with clear interfaces. Call verify_and_advance() when done. Use get_work() to stay in the proactive loop."
20
+ },
21
+ {
22
+ "name": "Backend",
23
+ "role": "backend",
24
+ "display_name": "Backend Developer",
25
+ "skills": ["api", "database", "server", "backend", "nodejs"],
26
+ "responsibilities": [
27
+ "Implement server-side logic, APIs, and data models",
28
+ "Write unit tests for backend code",
29
+ "Lock files before editing"
30
+ ],
31
+ "tools_focus": ["lock_file", "unlock_file", "update_task", "advance_workflow", "share_file"],
32
+ "prompt": "You are a backend developer. Implement server-side logic, APIs, and data models. Write unit tests. Use lock_file() before editing. Call verify_and_advance() when done. Never wait for approval."
33
+ },
34
+ {
35
+ "name": "Frontend",
36
+ "role": "frontend",
37
+ "display_name": "Frontend Developer",
38
+ "skills": ["ui", "css", "components", "frontend", "html"],
39
+ "responsibilities": [
40
+ "Implement UI components, pages, and client-side logic",
41
+ "Write component tests",
42
+ "Lock files before editing"
43
+ ],
44
+ "tools_focus": ["lock_file", "unlock_file", "update_task", "advance_workflow", "share_file"],
45
+ "prompt": "You are a frontend developer. Implement UI components, pages, and client-side logic. Write component tests. Use lock_file() before editing. Call verify_and_advance() when done. Never wait for approval."
46
+ },
47
+ {
48
+ "name": "Tester",
49
+ "role": "quality",
50
+ "display_name": "QA Engineer",
51
+ "skills": ["testing", "qa", "integration", "verification"],
52
+ "responsibilities": [
53
+ "Write and run integration tests",
54
+ "Verify all components work together",
55
+ "Submit review results via submit_review()"
56
+ ],
57
+ "tools_focus": ["request_review", "submit_review", "advance_workflow", "get_reputation"],
58
+ "prompt": "You are a QA engineer. Write and run integration tests. Verify all components work together. Call verify_and_advance() when done."
59
+ }
10
60
  ],
11
61
  "workflow": {
12
62
  "name": "Autonomous Feature",