@yemi33/minions 0.1.851 → 0.1.853
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 +3 -1
- package/dashboard/js/command-center.js +34 -0
- package/dashboard.js +68 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.853 (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)
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
- stale PRD shows Regenerate PRD + Resume as-is buttons
|
|
13
14
|
|
|
14
15
|
### Fixes
|
|
16
|
+
- 3 bugs in server-side CC action execution
|
|
15
17
|
- PID file leak in indirect spawn + kb-sweep race condition (#821)
|
|
16
18
|
- KB create modal stays open on API error for retry
|
|
17
19
|
- steering resume failure no longer silently marked as SUCCESS
|
|
@@ -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,42 @@ 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
|
+
|
|
691
709
|
async function ccExecuteAction(action, targetTabId) {
|
|
692
710
|
var status = document.createElement('div');
|
|
693
711
|
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
712
|
|
|
713
|
+
// Server-executed actions: just show status, don't re-fire the API
|
|
714
|
+
if (action._serverExecuted) {
|
|
715
|
+
if (action._serverError) {
|
|
716
|
+
status.innerHTML = '✗ ' + escHtml(action.type) + ' failed: ' + escHtml(action._serverError);
|
|
717
|
+
status.style.color = 'var(--red)';
|
|
718
|
+
} else {
|
|
719
|
+
var label = action._serverId ? escHtml(action._serverId) : escHtml(action.title || action.type);
|
|
720
|
+
status.innerHTML = '✓ ' + escHtml(action.type) + ': <strong>' + label + '</strong>';
|
|
721
|
+
status.style.color = 'var(--green)';
|
|
722
|
+
}
|
|
723
|
+
ccAddMessage('assistant', status.outerHTML, false, targetTabId);
|
|
724
|
+
if (['dispatch','fix','implement','explore','review','test'].includes(action.type)) wakeEngine();
|
|
725
|
+
refresh();
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
695
729
|
try {
|
|
696
730
|
switch (action.type) {
|
|
697
731
|
case 'dispatch':
|
package/dashboard.js
CHANGED
|
@@ -572,6 +572,63 @@ 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: WI_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 validCategories = ['architecture', 'conventions', 'project-notes', 'build-reports', 'reviews'];
|
|
611
|
+
const category = action.category || 'project-notes';
|
|
612
|
+
if (!validCategories.includes(category)) { results.push({ type: 'knowledge', error: 'Invalid category: ' + category }); break; }
|
|
613
|
+
const slug = shared.slugify(action.title || 'entry');
|
|
614
|
+
const kbDir = path.join(MINIONS_DIR, 'knowledge', category);
|
|
615
|
+
if (!fs.existsSync(kbDir)) fs.mkdirSync(kbDir, { recursive: true });
|
|
616
|
+
shared.safeWrite(path.join(kbDir, slug + '.md'), `# ${action.title}\n\n${action.content || action.description || ''}`);
|
|
617
|
+
results.push({ type: 'knowledge', ok: true });
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
default:
|
|
621
|
+
// Server didn't handle — frontend must execute
|
|
622
|
+
results.push({ type: action.type });
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
} catch (e) {
|
|
626
|
+
results.push({ type: action.type, error: e.message });
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return results;
|
|
630
|
+
}
|
|
631
|
+
|
|
575
632
|
// ── Shared LLM call core — used by CC panel and doc modals ──────────────────
|
|
576
633
|
|
|
577
634
|
// Session store for doc modals — keyed by filePath or title, persisted to disk
|
|
@@ -3424,7 +3481,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3424
3481
|
teams.teamsPostCCResponse(body.message, result.text).catch(() => {});
|
|
3425
3482
|
}
|
|
3426
3483
|
|
|
3427
|
-
const
|
|
3484
|
+
const parsed = parseCCActions(result.text);
|
|
3485
|
+
if (parsed.actions.length > 0) {
|
|
3486
|
+
parsed.actionResults = await executeCCActions(parsed.actions);
|
|
3487
|
+
}
|
|
3488
|
+
const reply = { ...parsed, sessionId: ccSession.sessionId, newSession: !wasResume };
|
|
3428
3489
|
if (sessionReset) reply.sessionReset = true;
|
|
3429
3490
|
return jsonReply(res, 200, reply);
|
|
3430
3491
|
} finally {
|
|
@@ -3534,9 +3595,13 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3534
3595
|
} catch { /* non-critical */ }
|
|
3535
3596
|
}
|
|
3536
3597
|
|
|
3537
|
-
// Send final result with actions
|
|
3598
|
+
// Send final result with actions — execute server-side first
|
|
3538
3599
|
const { text: displayText, actions } = parseCCActions(result.text);
|
|
3539
|
-
|
|
3600
|
+
let actionResults;
|
|
3601
|
+
if (actions.length > 0) {
|
|
3602
|
+
actionResults = await executeCCActions(actions);
|
|
3603
|
+
}
|
|
3604
|
+
const donePayload = { type: 'done', text: displayText, actions, actionResults, sessionId: responseSessionId, newSession: !wasResume };
|
|
3540
3605
|
if (sessionReset) donePayload.sessionReset = true;
|
|
3541
3606
|
res.write('data: ' + JSON.stringify(donePayload) + '\n\n');
|
|
3542
3607
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.853",
|
|
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"
|