neohive 6.3.0 → 6.4.1

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/dashboard.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Neohive</title>
6
+ <title>Neohive | Multi-Agent Coordination Dashboard</title>
7
7
  <link rel="icon" href="favicon.png" type="image/png" sizes="16x16">
8
8
  <link rel="icon" href="logo.svg" type="image/svg+xml">
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
@@ -183,7 +183,7 @@
183
183
  }
184
184
 
185
185
  .logo {
186
- font-size: 20px;
186
+ font-size: clamp(18px, 4vw, 22px);
187
187
  font-weight: 700;
188
188
  letter-spacing: -0.01em;
189
189
  color: var(--text);
@@ -3149,9 +3149,18 @@
3149
3149
  }
3150
3150
 
3151
3151
  .header { padding: 0 8px; }
3152
- .logo { font-size: 17px; }
3152
+ .logo { font-size: clamp(15px, 5vw, 18px); }
3153
3153
  .header-left { gap: 6px; }
3154
3154
 
3155
+ /* Increase touch targets for mobile buttons */
3156
+ .btn, .nh-btn {
3157
+ min-height: 44px;
3158
+ padding: 8px 16px;
3159
+ display: inline-flex;
3160
+ align-items: center;
3161
+ justify-content: center;
3162
+ }
3163
+
3155
3164
  /* Compact header buttons */
3156
3165
  .header-actions { gap: 2px; }
3157
3166
  .phone-btn { padding: 4px 6px; font-size: 13px; }
@@ -4361,12 +4370,29 @@
4361
4370
  <!-- Settings dropdown -->
4362
4371
  <div style="position:relative;display:inline-block">
4363
4372
  <button class="header-settings-btn" onclick="toggleSettingsMenu()" title="Settings">
4364
- <svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="2.5"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2M2.9 2.9l1.4 1.4M11.7 11.7l1.4 1.4M13.1 2.9l-1.4 1.4M4.3 11.7l-1.4 1.4"/></svg>
4373
+ <svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6.5 1.5h3l.5 1.5a5 5 0 011.2.7l1.6-.4 1.5 2.6-1.1 1.2a5 5 0 010 1.4l1.1 1.2-1.5 2.6-1.6-.4a5 5 0 01-1.2.7l-.5 1.5h-3l-.5-1.5A5 5 0 014.8 12l-1.6.4L1.7 9.8l1.1-1.2a5 5 0 010-1.4L1.7 6l1.5-2.6 1.6.4a5 5 0 011.2-.7z"/><circle cx="8" cy="8" r="2"/></svg>
4365
4374
  </button>
4366
4375
  <div id="settings-menu" style="display:none;position:absolute;right:0;top:100%;margin-top:6px;background:var(--surface);border:1px solid var(--border);border-radius:10px;overflow:hidden;z-index:300;min-width:180px;box-shadow:var(--shadow-lg)">
4367
4376
  <div class="settings-item" onclick="toggleTheme();toggleSettingsMenu()"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="4"/><path d="M8 1v2M8 13v2M1 8h2M13 8h2"/></svg> Theme</div>
4368
4377
  <div class="settings-item" id="settings-notif-item" onclick="toggleCombinedNotifications();"><svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6a4 4 0 018 0v3l2 2H2l2-2z"/><path d="M6 13a2 2 0 004 0"/></svg> <span id="settings-notif-label">Notifications</span></div>
4369
4378
  <div style="height:1px;background:var(--border);margin:4px 8px"></div>
4379
+ <div style="padding:8px 12px;display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-muted)">
4380
+ <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="8" r="6"/><path d="M8 5v3l2 2"/></svg>
4381
+ <span style="flex:1;white-space:nowrap">Idle poll (s)</span>
4382
+ <input id="settings-idle-poll-input" type="number" min="10" max="600" step="10" value="90"
4383
+ style="width:60px;background:var(--bg);border:1px solid var(--border);border-radius:5px;color:var(--text);padding:2px 6px;font-size:13px;text-align:right"
4384
+ onclick="event.stopPropagation()"
4385
+ onchange="setIdlePollInterval(this.value)">
4386
+ </div>
4387
+ <div style="padding:8px 12px;display:flex;align-items:center;gap:8px;font-size:13px;color:var(--text-muted)">
4388
+ <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 1v6l3 3"/><circle cx="8" cy="8" r="7"/></svg>
4389
+ <span style="flex:1;white-space:nowrap">Listen timeout (s)</span>
4390
+ <input id="settings-listen-poll-input" type="number" min="30" max="600" step="30" value="120"
4391
+ style="width:60px;background:var(--bg);border:1px solid var(--border);border-radius:5px;color:var(--text);padding:2px 6px;font-size:13px;text-align:right"
4392
+ onclick="event.stopPropagation()"
4393
+ onchange="setListenPollInterval(this.value)">
4394
+ </div>
4395
+ <div style="height:1px;background:var(--border);margin:4px 8px"></div>
4370
4396
  <div class="settings-item" onclick="exportShareableHTML();toggleSettingsMenu()">Export HTML</div>
4371
4397
  <div class="settings-item" onclick="exportJSON();toggleSettingsMenu()">Export JSON</div>
4372
4398
  <div class="settings-item" onclick="enterReplay();toggleSettingsMenu()">Replay</div>
@@ -4565,7 +4591,7 @@
4565
4591
  </div>
4566
4592
  </div>
4567
4593
  <div class="app-footer">
4568
- <span>Neohive v6.0.0</span>
4594
+ <span>Neohive v6.4.1</span>
4569
4595
  </div>
4570
4596
  <div class="profile-popup" id="profile-popup" onclick="event.stopPropagation()">
4571
4597
  <div class="profile-popup-header">
@@ -5256,10 +5282,10 @@ function respawnAgent(agentName) {
5256
5282
  }
5257
5283
 
5258
5284
  function generateFallbackRespawnPrompt(agentName) {
5259
- return 'Register as \'' + agentName + '\', then call get_briefing() and listen_group() to rejoin the conversation. ' +
5285
+ return 'Register as \'' + agentName + '\', then call get_briefing() and listen() to rejoin the conversation. ' +
5260
5286
  'You are resuming a previous session — call get_compressed_history() to catch up on what you missed. ' +
5261
5287
  'Check your workspace with workspace_read() for any saved state. ' +
5262
- 'Then call listen_group() and respond to any pending messages.';
5288
+ 'Then call listen() and respond to any pending messages.';
5263
5289
  }
5264
5290
 
5265
5291
  function showRespawnModal(agentName, prompt) {
@@ -5465,7 +5491,7 @@ function renderMessages(messages) {
5465
5491
  } else {
5466
5492
  el.innerHTML = '<div class="empty-state">' +
5467
5493
  '<div class="empty-icon" style="font-size:40px;opacity:0.2">--</div>' +
5468
- '<div class="empty-text">Neohive v6.0</div>' +
5494
+ '<div class="empty-text">Neohive v6.4</div>' +
5469
5495
  '<div class="empty-sub">Autonomous AI agent teams — one command, zero babysitting</div>' +
5470
5496
  '<div class="onboard-steps">' +
5471
5497
  '<div class="onboard-step" style="margin-bottom:12px"><span class="onboard-num" style="background:var(--accent)"></span><span style="font-weight:600">Quickest start — one command:</span></div>' +
@@ -9838,11 +9864,11 @@ var ROLE_SKILLS = {
9838
9864
  monitor: ['observability', 'logging', 'performance', 'health-checks']
9839
9865
  };
9840
9866
  var ROLE_PROMPTS = {
9841
- lead: function(name) { return 'You are ' + name + ', the Coordinator in a multi-agent team. Register as "' + name + '".\n\nYour job is to:\n1. Break the user\'s request into tasks and delegate to team agents via send_message\n2. Use create_task() and create_workflow() to formally track work\n3. Monitor progress with workflow_status() and list_tasks()\n4. Use consume_messages() to check agent updates without blocking\n5. Synthesize results and present to the user\n\nYou MUST NOT edit files or write code. Delegate ALL code work to other agents. Your tools: send_message, create_task, create_workflow, advance_workflow, workflow_status, list_tasks, consume_messages, broadcast.'; },
9842
- backend: function(name) { return 'You are ' + name + ', a Developer in a multi-agent team. Register as "' + name + '". Call listen() to wait for tasks.\n\nWhen you receive a task:\n1. Use lock_file() before editing shared code\n2. Implement the requested changes with clean, tested code\n3. Use unlock_file() when done editing\n4. Update your task with update_task(status="done")\n5. Send a summary to the Coordinator with file paths and key decisions\n6. Call listen() again for the next task\n\nFocus on production-quality code. Include file paths in reports.'; },
9843
- frontend: function(name) { return 'You are ' + name + ', a Frontend Developer in a multi-agent team. Register as "' + name + '". Call listen() to wait for tasks.\n\nWhen you receive a task:\n1. Use lock_file() before editing shared frontend code\n2. Implement UI/UX changes following design conventions\n3. Use unlock_file() when done\n4. Update your task with update_task(status="done")\n5. Send a summary to the Coordinator with file paths and screenshots if relevant\n6. Call listen() again for the next task\n\nFocus on clean UI, accessibility, and responsive design.'; },
9844
- quality: function(name) { return 'You are ' + name + ', a Reviewer in a multi-agent team. Register as "' + name + '". Call listen() to wait for review requests.\n\nWhen you receive work to review:\n1. Read the actual files that were changed\n2. Check for bugs, security issues, code style, edge cases\n3. Use submit_review() to formally approve or request changes\n4. Send structured feedback: blockers vs suggestions\n5. Call listen() again\n\nBe constructive and specific. Reference line numbers. Never let mediocre work pass.'; },
9845
- monitor: function(name) { return 'You are ' + name + ', a System Monitor in a multi-agent team. Register as "' + name + '". Call listen() to wait for events.\n\nYour job:\n1. Watch for idle agents, stuck tasks, and circular escalations\n2. Use send_message to nudge idle agents\n3. Use update_task to reassign stuck tasks\n4. Log interventions to your workspace via workspace_write\n5. Call listen() again\n\nNever stop monitoring. You ARE the system intelligence.'; }
9867
+ lead: function(name) { return 'You are ' + name + ', the Coordinator in a multi-agent team. Register as "' + name + '", call update_profile() to set your role, call get_briefing() for project context, then call listen() to receive the first request.\n\nYour loop:\n1. Receive request via listen()\n2. Break it into subtasks create_task() per item, assign to agents\n3. Create a workflow with create_workflow() for multi-step plans\n4. Delegate via send_message() to each assigned agent\n5. Monitor with workflow_status() and list_tasks()\n6. Check updates with messages(action="consume") without blocking\n7. Synthesize results and report back to the user\n8. Call listen(outcome="completed", summary="...") for the next task\n\nRules:\n- NEVER edit files or write code delegate ALL implementation to other agents\n- Always report synthesis back to the user via send_message()\n- If listen() returns retry: true, call listen() again immediately'; },
9868
+ backend: function(name) { return 'You are ' + name + ', a Backend Developer in a multi-agent team. Register as "' + name + '", call update_profile() to set your role, call get_briefing() for project context, then call listen() to wait for tasks.\n\nYour loop:\n1. Receive task via listen()\n2. Call update_task(status="in_progress", task_id=...) to claim it\n3. Call lock_file() before editing any shared file\n4. Implement the changes clean, production-quality code\n5. Call unlock_file() when done\n6. Call update_task(status="done", task_id=...)\n7. Report to Lead via send_message(): what you did, files changed, decisions made, any blockers\n8. Call listen(outcome="completed", task_id=..., summary="...") for the next task\n\nIf a lock is already held: notify Lead via send_message() and call listen() to wait.\nIf listen() returns retry: true, call listen() again immediately.'; },
9869
+ frontend: function(name) { return 'You are ' + name + ', a Frontend Developer in a multi-agent team. Register as "' + name + '", call update_profile() to set your role, call get_briefing() for project context, then call listen() to wait for tasks.\n\nYour loop:\n1. Receive task via listen()\n2. Call update_task(status="in_progress", task_id=...) to claim it\n3. Call lock_file() before editing any shared frontend file\n4. Implement UI/UX changes clean, accessible, responsive code\n5. Call unlock_file() when done\n6. Call update_task(status="done", task_id=...)\n7. Report to Lead via send_message(): files changed, design decisions, screenshots if relevant\n8. Call listen(outcome="completed", task_id=..., summary="...") for the next task\n\nIf a lock is already held: notify Lead via send_message() and call listen() to wait.\nIf listen() returns retry: true, call listen() again immediately.'; },
9870
+ quality: function(name) { return 'You are ' + name + ', a Code Reviewer in a multi-agent team. Register as "' + name + '", call update_profile() to set your role, call get_briefing() for project context, then call listen() to wait for review requests.\n\nYour loop:\n1. Receive review request via listen()\n2. Call update_task(status="in_progress", task_id=...) to claim it\n3. Read the actual files that were changed\n4. Check for: bugs, security issues, logic errors, code style, edge cases\n5. Call submit_review(approved=true/false, feedback="...") with structured feedback\n6. Report to Lead via send_message(): blockers vs suggestions with file:line references\n7. Call update_task(status="done", task_id=...)\n8. Call listen(outcome="completed", task_id=..., summary="...") for the next review\n\nBe specific: reference file paths and line numbers. Separate blockers from suggestions.\nIf listen() returns retry: true, call listen() again immediately.'; },
9871
+ monitor: function(name) { return 'You are ' + name + ', a System Monitor in a multi-agent team. Register as "' + name + '", call update_profile() to set your role, call get_briefing() for project context, then call listen() to begin monitoring.\n\nYour loop (runs continuously):\n1. Call list_agents() flag agents with last_activity > 5 min and status != offline\n2. Call list_tasks() — flag in_progress tasks whose assignee appears idle\n3. Nudge idle agents: send_message() asking them to resume their listen() loop\n4. Reassign stuck tasks (no progress > 10 min): update_task() to reset to pending\n5. For blocked_permanent tasks: send_message() to Lead immediately\n6. Log all interventions via workspace_write()\n7. Call listen(outcome="completed", summary="...") — repeat\n\nEscalation: if unresolved after 2 attempts, create_task() assigned to Lead describing the issue.\nIf listen() returns retry: true, call listen() again immediately.\nNever stop monitoring.'; }
9846
9872
  };
9847
9873
 
9848
9874
  function renderLaunchPanel() {
@@ -10275,7 +10301,7 @@ function doLaunch() {
10275
10301
  }
10276
10302
 
10277
10303
  // Use generated prompt if available, otherwise build a default
10278
- var launchPrompt = window._generatedLaunchPrompt || 'You are agent "' + agentName + '". Use the register tool to register as "' + agentName + '", then use listen_group() to join the conversation.';
10304
+ var launchPrompt = window._generatedLaunchPrompt || 'You are agent "' + agentName + '". Use the register tool to register as "' + agentName + '", then use listen() to join the conversation.';
10279
10305
  navigator.clipboard.writeText(launchPrompt).catch(function() {});
10280
10306
  selectedCli = cli;
10281
10307
 
@@ -10366,7 +10392,7 @@ function doLaunchAll() {
10366
10392
  w.state = 'launching';
10367
10393
  renderLaunchStatusTracker();
10368
10394
 
10369
- var agentPrompt = w.prompt || 'You are agent "' + w.name + '". Use the register tool to register as "' + w.name + '", then use listen_group() to join the conversation.';
10395
+ var agentPrompt = w.prompt || 'You are agent "' + w.name + '". Use the register tool to register as "' + w.name + '", then use listen() to join the conversation.';
10370
10396
 
10371
10397
  lttFetch('/api/launch', {
10372
10398
  method: 'POST',
@@ -10495,7 +10521,7 @@ function renderDocs() {
10495
10521
  el.innerHTML =
10496
10522
  '<div class="docs-container">' +
10497
10523
 
10498
- '<h2>Neohive v6.0</h2>' +
10524
+ '<h2>Neohive v6.4</h2>' +
10499
10525
  '<p class="docs-subtitle">True Autonomy Engine \u2014 AI agents that self-organize, self-verify, and never stop working. Works with Claude Code, Gemini CLI, Codex CLI, and Cursor IDE.</p>' +
10500
10526
 
10501
10527
  // Quick Start — One Command
@@ -10533,7 +10559,7 @@ function renderDocs() {
10533
10559
  '<p>Opens this web dashboard at <code>http://localhost:3777</code>. You can watch agents chat in real-time, send them messages, and manage your team.</p>' +
10534
10560
  '<h4>3. Start Your Agents</h4>' +
10535
10561
  '<p>Open two or more terminal windows in your project folder. In each one, start your AI CLI (e.g. type <code>claude</code>) and tell it to register:</p>' +
10536
- '<pre><code># Terminal 1\nRegister as "Alice" and use listen_group() to join the conversation.\n\n# Terminal 2\nRegister as "Bob" and use listen_group() to join the conversation.</code></pre>' +
10562
+ '<pre><code># Terminal 1\nRegister as "Alice" and use listen() to join the conversation.\n\n# Terminal 2\nRegister as "Bob" and use listen() to join the conversation.</code></pre>' +
10537
10563
  '<p>That\'s it! Your agents can now talk to each other. Or use the <strong>Launch</strong> tab to do this with one click.</p>' +
10538
10564
  '</div>' +
10539
10565
 
@@ -10617,7 +10643,7 @@ function renderDocs() {
10617
10643
  '<h4>Can I run multiple projects?</h4>' +
10618
10644
  '<p>Yes. Each project has its own <code>.neohive/</code> directory. The dashboard supports multiple projects \u2014 click the project selector in the header to switch between them.</p>' +
10619
10645
  '<h4>How do I send a message to agents from the dashboard?</h4>' +
10620
- '<p>Click any agent\'s avatar in the Messages tab. A dialog lets you type and send a message that agents will receive on their next <code>listen_group()</code> call.</p>' +
10646
+ '<p>Click any agent\'s avatar in the Messages tab. A dialog lets you type and send a message that agents will receive on their next <code>listen()</code> call.</p>' +
10621
10647
  '</div>' +
10622
10648
 
10623
10649
  '</div>';
@@ -10636,8 +10662,8 @@ function copyLaunchPrompt() {
10636
10662
  if (!prompt) {
10637
10663
  var agentName = document.getElementById('launch-name').value.trim();
10638
10664
  prompt = agentName
10639
- ? 'You are agent "' + agentName + '". Use the register tool to register as "' + agentName + '", then use listen_group() to join the conversation.'
10640
- : 'Register with the neohive MCP tools and use listen_group() to join the conversation.';
10665
+ ? 'You are agent "' + agentName + '". Use the register tool to register as "' + agentName + '", then use listen() to join the conversation.'
10666
+ : 'Register with the neohive MCP tools and use listen() to join the conversation.';
10641
10667
  }
10642
10668
  navigator.clipboard.writeText(prompt).then(function() {
10643
10669
  var resultEl = document.getElementById('launch-result');
@@ -10651,7 +10677,11 @@ function copyLaunchPrompt() {
10651
10677
 
10652
10678
  function toggleSettingsMenu() {
10653
10679
  var menu = document.getElementById('settings-menu');
10654
- if (menu) menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
10680
+ if (menu) {
10681
+ var opening = menu.style.display === 'none';
10682
+ menu.style.display = opening ? 'block' : 'none';
10683
+ if (opening) { initIdlePollInput(); initListenPollInput(); }
10684
+ }
10655
10685
  }
10656
10686
  // Close settings when clicking outside
10657
10687
  document.addEventListener('click', function(e) {
@@ -10947,6 +10977,8 @@ function setCoordinatorMode(mode) {
10947
10977
  showToast('!', 'Failed: ' + data.error);
10948
10978
  } else {
10949
10979
  showToast('&#x2713;', 'Coordinator mode: ' + (mode === 'responsive' ? 'Stay with me' : 'Run autonomously'));
10980
+ var listenInput = document.getElementById('settings-listen-poll-input');
10981
+ if (listenInput) listenInput.value = mode === 'responsive' ? 120 : 90;
10950
10982
  renderOverview();
10951
10983
  }
10952
10984
  }).catch(function() {
@@ -10954,6 +10986,60 @@ function setCoordinatorMode(mode) {
10954
10986
  });
10955
10987
  }
10956
10988
 
10989
+ // ==================== IDLE POLL INTERVAL ====================
10990
+
10991
+ function setIdlePollInterval(value) {
10992
+ var secs = parseInt(value, 10);
10993
+ if (isNaN(secs) || secs < 10) return;
10994
+ lttFetch('/api/config', {
10995
+ method: 'POST',
10996
+ headers: { 'Content-Type': 'application/json' },
10997
+ body: JSON.stringify({ idle_poll_interval: secs })
10998
+ }).then(function(r) { return r.json(); }).then(function(data) {
10999
+ if (data.error) {
11000
+ showToast('!', 'Failed: ' + data.error);
11001
+ } else {
11002
+ showToast('&#x2713;', 'Idle poll interval: ' + secs + 's');
11003
+ }
11004
+ }).catch(function() {
11005
+ showToast('!', 'Failed to save idle poll interval');
11006
+ });
11007
+ }
11008
+
11009
+ function initIdlePollInput() {
11010
+ lttFetch('/api/config').then(function(r) { return r.json(); }).then(function(cfg) {
11011
+ var input = document.getElementById('settings-idle-poll-input');
11012
+ if (input && cfg.idle_poll_interval) input.value = cfg.idle_poll_interval;
11013
+ }).catch(function() {});
11014
+ }
11015
+
11016
+ // ==================== LISTEN POLL INTERVAL ====================
11017
+
11018
+ function setListenPollInterval(value) {
11019
+ var secs = parseInt(value, 10);
11020
+ if (isNaN(secs) || secs < 30) return;
11021
+ lttFetch('/api/config', {
11022
+ method: 'POST',
11023
+ headers: { 'Content-Type': 'application/json' },
11024
+ body: JSON.stringify({ listen_poll_interval: secs })
11025
+ }).then(function(r) { return r.json(); }).then(function(data) {
11026
+ if (data.error) {
11027
+ showToast('!', 'Failed: ' + data.error);
11028
+ } else {
11029
+ showToast('&#x2713;', 'Listen timeout: ' + secs + 's');
11030
+ }
11031
+ }).catch(function() {
11032
+ showToast('!', 'Failed to save listen timeout');
11033
+ });
11034
+ }
11035
+
11036
+ function initListenPollInput() {
11037
+ lttFetch('/api/config').then(function(r) { return r.json(); }).then(function(cfg) {
11038
+ var input = document.getElementById('settings-listen-poll-input');
11039
+ if (input && cfg.listen_poll_interval) input.value = cfg.listen_poll_interval;
11040
+ }).catch(function() {});
11041
+ }
11042
+
10957
11043
  // ==================== TOAST NOTIFICATIONS ====================
10958
11044
 
10959
11045
  var toastQueue = [];
package/dashboard.js CHANGED
@@ -175,9 +175,15 @@ function resolveDashboardDefaultDataDir() {
175
175
  let s = String(envData).trim();
176
176
  if (/\$\{workspaceFolder\}/i.test(s)) {
177
177
  const root = findCursorProjectRootWithNeohive(process.cwd());
178
- if (root) s = s.replace(/\$\{workspaceFolder\}/gi, root);
178
+ if (!root) {
179
+ // Placeholder can't be expanded — fall through to the config/walk/cwd strategies
180
+ } else {
181
+ s = s.replace(/\$\{workspaceFolder\}/gi, root);
182
+ return { path: path.resolve(s), source: 'environment' };
183
+ }
184
+ } else {
185
+ return { path: path.resolve(s), source: 'environment' };
179
186
  }
180
- return { path: path.resolve(s), source: 'environment' };
181
187
  }
182
188
 
183
189
  // 2. Project MCP config — authoritative, written by `neohive init`
@@ -2656,9 +2662,9 @@ const server = http.createServer(async (req, res) => {
2656
2662
  prompt += `**Instructions:**\n`;
2657
2663
  prompt += `1. Register as "${agentName}" using the register tool\n`;
2658
2664
  prompt += `2. Call get_briefing() for full project context\n`;
2659
- prompt += `3. Call listen_group() to rejoin the conversation\n`;
2665
+ prompt += `3. Call listen() to rejoin the conversation\n`;
2660
2666
  prompt += `4. Announce you're back and pick up your active tasks\n`;
2661
- prompt += `5. Stay in listen_group() loop — never stop listening\n`;
2667
+ prompt += `5. Stay in listen() loop — never stop listening\n`;
2662
2668
 
2663
2669
  res.writeHead(200, { 'Content-Type': 'application/json' });
2664
2670
  res.end(JSON.stringify({
@@ -2711,7 +2717,7 @@ const server = http.createServer(async (req, res) => {
2711
2717
  try {
2712
2718
  const messagesFile = filePath('messages.jsonl', projectPath);
2713
2719
  const historyFile = filePath('history.jsonl', projectPath);
2714
- const modeText = newMode === 'responsive' ? 'Coordinator stays with human, uses consume_messages().' : 'Coordinator runs autonomously in listen() loop.';
2720
+ const modeText = newMode === 'responsive' ? 'Coordinator stays with human, uses messages(action="check").' : 'Coordinator runs autonomously in listen() loop.';
2715
2721
  const sysMsg = { id: Date.now().toString(36) + Math.random().toString(36).slice(2, 8), from: '__system__', to: '__group__', content: `[MODE] Coordinator mode changed to "${newMode}". ${modeText} Coordinator: call get_guide() to update your instructions.`, timestamp: new Date().toISOString(), system: true };
2716
2722
  fs.appendFileSync(messagesFile, JSON.stringify(sysMsg) + '\n');
2717
2723
  fs.appendFileSync(historyFile, JSON.stringify(sysMsg) + '\n');
@@ -2733,6 +2739,31 @@ const server = http.createServer(async (req, res) => {
2733
2739
  res.end(JSON.stringify({ error: 'Failed to set coordinator mode: ' + e.message }));
2734
2740
  }
2735
2741
  }
2742
+ else if (url.pathname === '/api/config' && req.method === 'GET') {
2743
+ const projectPath = url.searchParams.get('project') || null;
2744
+ const config = readJson(filePath('config.json', projectPath));
2745
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2746
+ res.end(JSON.stringify(config));
2747
+ }
2748
+ else if (url.pathname === '/api/config' && req.method === 'POST') {
2749
+ try {
2750
+ const body = await parseBody(req).catch(() => ({}));
2751
+ const projectPath = url.searchParams.get('project') || null;
2752
+ const dataDir = resolveDataDir(projectPath);
2753
+ if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
2754
+ const configFile = filePath('config.json', projectPath);
2755
+ await withFileLock(configFile, () => {
2756
+ const config = readJson(configFile);
2757
+ Object.assign(config, body);
2758
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
2759
+ });
2760
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2761
+ res.end(JSON.stringify({ success: true }));
2762
+ } catch (e) {
2763
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2764
+ res.end(JSON.stringify({ error: 'Failed to save config: ' + e.message }));
2765
+ }
2766
+ }
2736
2767
  else if (url.pathname === '/api/reset' && req.method === 'POST') {
2737
2768
  const body = await parseBody(req).catch(() => ({}));
2738
2769
  if (!body.confirm) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neohive",
3
- "version": "6.3.0",
3
+ "version": "6.4.1",
4
4
  "description": "The MCP collaboration layer for AI CLI tools. Turn Claude Code, Gemini CLI, and Codex CLI into a team.",
5
5
  "main": "server.js",
6
6
  "bin": {