@yemi33/minions 0.1.851 → 0.1.852

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 CHANGED
@@ -1,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.851 (2026-04-11)
3
+ ## 0.1.852 (2026-04-11)
4
4
 
5
5
  ### Features
6
+ - execute CC actions server-side (dispatch, note, knowledge)
6
7
  - per-type heartbeat timeouts for read-heavy work types (#652)
7
8
  - Replace raw status strings with WI_STATUS constants in dashboard.js (#657)
8
9
  - fix dispatch.json race condition in CLI kill-all (#653)
@@ -579,6 +579,7 @@ async function _ccDoSend(message, skipUserMsg) {
579
579
  ccSaveState(); ccUpdateSessionIndicator();
580
580
  }
581
581
  if (evt.actions && evt.actions.length > 0) {
582
+ _tagServerExecuted(evt.actions, evt.actionResults);
582
583
  for (var ai = 0; ai < evt.actions.length; ai++) { await ccExecuteAction(evt.actions[ai], activeTabId); }
583
584
  }
584
585
  } else if (evt.type === 'error') {
@@ -609,6 +610,7 @@ async function _ccDoSend(message, skipUserMsg) {
609
610
  ccSaveState(); ccUpdateSessionIndicator();
610
611
  }
611
612
  if (revt.actions && revt.actions.length > 0) {
613
+ _tagServerExecuted(revt.actions, revt.actionResults);
612
614
  for (var ai2 = 0; ai2 < revt.actions.length; ai2++) { await ccExecuteAction(revt.actions[ai2], activeTabId); }
613
615
  }
614
616
  } else if (revt.type === 'chunk') {
@@ -688,10 +690,43 @@ async function _ccFetch(url, body) {
688
690
  return res;
689
691
  }
690
692
 
693
+ // Tag actions that the server already executed so ccExecuteAction skips the API call
694
+ function _tagServerExecuted(actions, actionResults) {
695
+ if (!actionResults || !Array.isArray(actionResults)) return;
696
+ for (var i = 0; i < actions.length && i < actionResults.length; i++) {
697
+ var r = actionResults[i];
698
+ if (r && r.ok) {
699
+ actions[i]._serverExecuted = true;
700
+ if (r.id) actions[i]._serverId = r.id;
701
+ } else if (r && r.error) {
702
+ actions[i]._serverExecuted = true;
703
+ actions[i]._serverError = r.error;
704
+ }
705
+ // clientExecuted: false means server didn't handle it — frontend must execute
706
+ }
707
+ }
708
+
709
+
691
710
  async function ccExecuteAction(action, targetTabId) {
692
711
  var status = document.createElement('div');
693
712
  status.style.cssText = 'padding:4px 10px;border-radius:4px;font-size:10px;align-self:flex-start;border:1px dashed var(--border);color:var(--muted)';
694
713
 
714
+ // Server-executed actions: just show status, don't re-fire the API
715
+ if (action._serverExecuted) {
716
+ if (action._serverError) {
717
+ status.innerHTML = '&#10007; ' + escHtml(action.type) + ' failed: ' + escHtml(action._serverError);
718
+ status.style.color = 'var(--red)';
719
+ } else {
720
+ var label = action._serverId ? escHtml(action._serverId) : escHtml(action.title || action.type);
721
+ status.innerHTML = '&#10003; ' + escHtml(action.type) + ': <strong>' + label + '</strong>';
722
+ status.style.color = 'var(--green)';
723
+ }
724
+ ccAddMessage('assistant', status.outerHTML, false, targetTabId);
725
+ if (['dispatch','fix','implement','explore','review','test'].includes(action.type)) wakeEngine();
726
+ refresh();
727
+ return;
728
+ }
729
+
695
730
  try {
696
731
  switch (action.type) {
697
732
  case 'dispatch':
package/dashboard.js CHANGED
@@ -572,6 +572,61 @@ function parseCCActions(text) {
572
572
  return { text: displayText, actions };
573
573
  }
574
574
 
575
+ // ── Server-side CC action execution ──────────────────────────────────────────
576
+ // Actions are executed server-side so all clients (frontend, curl, Teams) get the same behavior.
577
+ // The frontend still shows status toasts but no longer needs to fire the API calls.
578
+
579
+ async function executeCCActions(actions) {
580
+ const results = [];
581
+ for (const action of actions) {
582
+ try {
583
+ switch (action.type) {
584
+ case 'dispatch': case 'fix': case 'implement': case 'explore': case 'review': case 'test': {
585
+ const workType = action.workType || (action.type !== 'dispatch' ? action.type : 'implement');
586
+ const id = 'W-' + shared.uid();
587
+ const project = action.project || '';
588
+ const targetProject = project ? PROJECTS.find(p => p.name?.toLowerCase() === project.toLowerCase()) : PROJECTS[0];
589
+ const wiPath = targetProject ? shared.projectWorkItemsPath(targetProject) : path.join(MINIONS_DIR, 'work-items.json');
590
+ shared.mutateJsonFileLocked(wiPath, items => {
591
+ if (!Array.isArray(items)) items = [];
592
+ items.push({
593
+ id, title: action.title || 'Untitled', type: workType,
594
+ priority: action.priority || 'medium', description: action.description || '',
595
+ status: 'pending', created: new Date().toISOString(),
596
+ createdBy: 'command-center', project,
597
+ ...(action.agents?.length ? { preferred_agent: action.agents[0] } : {}),
598
+ });
599
+ return items;
600
+ }, { defaultValue: [] });
601
+ results.push({ type: action.type, id, ok: true });
602
+ break;
603
+ }
604
+ case 'note': {
605
+ shared.writeToInbox('command-center', shared.slugify(action.title || 'note'), `# ${action.title || 'Note'}\n\n${action.content || action.description || ''}`);
606
+ results.push({ type: 'note', ok: true });
607
+ break;
608
+ }
609
+ case 'knowledge': {
610
+ const category = action.category || 'project-notes';
611
+ const slug = shared.slugify(action.title || 'entry');
612
+ const kbDir = path.join(MINIONS_DIR, 'knowledge', category);
613
+ if (!fs.existsSync(kbDir)) fs.mkdirSync(kbDir, { recursive: true });
614
+ shared.safeWrite(path.join(kbDir, slug + '.md'), `# ${action.title}\n\n${action.content || action.description || ''}`);
615
+ results.push({ type: 'knowledge', ok: true });
616
+ break;
617
+ }
618
+ default:
619
+ // For action types that map 1:1 to API endpoints, flag as client-executed
620
+ results.push({ type: action.type, ok: true, clientExecuted: false });
621
+ break;
622
+ }
623
+ } catch (e) {
624
+ results.push({ type: action.type, error: e.message });
625
+ }
626
+ }
627
+ return results;
628
+ }
629
+
575
630
  // ── Shared LLM call core — used by CC panel and doc modals ──────────────────
576
631
 
577
632
  // Session store for doc modals — keyed by filePath or title, persisted to disk
@@ -3424,7 +3479,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3424
3479
  teams.teamsPostCCResponse(body.message, result.text).catch(() => {});
3425
3480
  }
3426
3481
 
3427
- const reply = { ...parseCCActions(result.text), sessionId: ccSession.sessionId, newSession: !wasResume };
3482
+ const parsed = parseCCActions(result.text);
3483
+ if (parsed.actions.length > 0) {
3484
+ parsed.actionResults = await executeCCActions(parsed.actions);
3485
+ }
3486
+ const reply = { ...parsed, sessionId: ccSession.sessionId, newSession: !wasResume };
3428
3487
  if (sessionReset) reply.sessionReset = true;
3429
3488
  return jsonReply(res, 200, reply);
3430
3489
  } finally {
@@ -3534,9 +3593,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
3534
3593
  } catch { /* non-critical */ }
3535
3594
  }
3536
3595
 
3537
- // Send final result with actions
3596
+ // Send final result with actions — execute server-side first
3538
3597
  const { text: displayText, actions } = parseCCActions(result.text);
3539
- const donePayload = { type: 'done', text: displayText, actions, sessionId: responseSessionId, newSession: !wasResume };
3598
+ let actionResults;
3599
+ if (actions.length > 0) {
3600
+ actionResults = await executeCCActions(actions);
3601
+ }
3602
+ const donePayload = { type: 'done', text: displayText, actions, actionResults, sessionId: responseSessionId, newSession: !wasResume };
3540
3603
  if (sessionReset) donePayload.sessionReset = true;
3541
3604
  res.write('data: ' + JSON.stringify(donePayload) + '\n\n');
3542
3605
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.851",
3
+ "version": "0.1.852",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"