@yemi33/minions 0.1.147 → 0.1.149
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 +15 -1
- package/dashboard/js/command-center.js +59 -0
- package/dashboard/layout.html +1 -0
- package/dashboard/styles.css +7 -0
- package/engine/meeting.js +34 -4
- package/engine/shared.js +1 -0
- package/engine.js +11 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.149 (2026-04-01)
|
|
4
4
|
|
|
5
5
|
### Engine
|
|
6
|
+
- engine.js
|
|
7
|
+
- engine/meeting.js
|
|
8
|
+
- engine/shared.js
|
|
9
|
+
|
|
10
|
+
### Other
|
|
11
|
+
- test/unit.test.js
|
|
12
|
+
|
|
13
|
+
## 0.1.148 (2026-04-01)
|
|
14
|
+
|
|
15
|
+
### Engine
|
|
16
|
+
- engine.js
|
|
6
17
|
- engine/lifecycle.js
|
|
7
18
|
- engine/shared.js
|
|
8
19
|
|
|
9
20
|
### Dashboard
|
|
21
|
+
- dashboard/js/command-center.js
|
|
10
22
|
- dashboard/js/render-work-items.js
|
|
23
|
+
- dashboard/layout.html
|
|
24
|
+
- dashboard/styles.css
|
|
11
25
|
|
|
12
26
|
### Other
|
|
13
27
|
- .github/workflows/pr-tests.yml
|
|
@@ -9,6 +9,7 @@ let _ccQueue = [];
|
|
|
9
9
|
function toggleCommandCenter() {
|
|
10
10
|
_ccOpen = !_ccOpen;
|
|
11
11
|
const drawer = document.getElementById('cc-drawer');
|
|
12
|
+
if (_ccOpen) ccApplySavedWidth();
|
|
12
13
|
drawer.style.display = _ccOpen ? 'flex' : 'none';
|
|
13
14
|
if (_ccOpen) {
|
|
14
15
|
clearNotifBadge(document.getElementById('cc-toggle-btn'));
|
|
@@ -462,4 +463,62 @@ async function ccExecuteAction(action) {
|
|
|
462
463
|
refresh();
|
|
463
464
|
}
|
|
464
465
|
|
|
466
|
+
// --- CC Resize Logic ---
|
|
467
|
+
const CC_MIN_WIDTH = 320;
|
|
468
|
+
const CC_MAX_WIDTH_RATIO = 0.8; // 80% of viewport
|
|
469
|
+
const CC_DEFAULT_WIDTH = 420;
|
|
470
|
+
const CC_WIDTH_KEY = 'cc-drawer-width';
|
|
471
|
+
|
|
472
|
+
function ccApplySavedWidth() {
|
|
473
|
+
const drawer = document.getElementById('cc-drawer');
|
|
474
|
+
if (!drawer) return;
|
|
475
|
+
const saved = parseInt(localStorage.getItem(CC_WIDTH_KEY), 10);
|
|
476
|
+
if (saved && saved >= CC_MIN_WIDTH) {
|
|
477
|
+
const maxW = Math.floor(window.innerWidth * CC_MAX_WIDTH_RATIO);
|
|
478
|
+
drawer.style.width = Math.min(saved, maxW) + 'px';
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function ccInitResize() {
|
|
483
|
+
const handle = document.getElementById('cc-resize-handle');
|
|
484
|
+
const drawer = document.getElementById('cc-drawer');
|
|
485
|
+
if (!handle || !drawer) return;
|
|
486
|
+
|
|
487
|
+
let startX = 0;
|
|
488
|
+
let startW = 0;
|
|
489
|
+
|
|
490
|
+
function onMouseMove(e) {
|
|
491
|
+
const maxW = Math.floor(window.innerWidth * CC_MAX_WIDTH_RATIO);
|
|
492
|
+
const delta = startX - e.clientX; // dragging left = wider
|
|
493
|
+
const newW = Math.max(CC_MIN_WIDTH, Math.min(startW + delta, maxW));
|
|
494
|
+
drawer.style.width = newW + 'px';
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function onMouseUp() {
|
|
498
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
499
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
500
|
+
document.body.classList.remove('cc-resizing');
|
|
501
|
+
handle.classList.remove('active');
|
|
502
|
+
// Persist
|
|
503
|
+
try { localStorage.setItem(CC_WIDTH_KEY, parseInt(drawer.style.width, 10)); } catch {}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
handle.addEventListener('mousedown', (e) => {
|
|
507
|
+
e.preventDefault();
|
|
508
|
+
startX = e.clientX;
|
|
509
|
+
startW = drawer.offsetWidth;
|
|
510
|
+
document.body.classList.add('cc-resizing');
|
|
511
|
+
handle.classList.add('active');
|
|
512
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
513
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Init resize on DOM ready
|
|
518
|
+
if (document.readyState === 'loading') {
|
|
519
|
+
document.addEventListener('DOMContentLoaded', ccInitResize);
|
|
520
|
+
} else {
|
|
521
|
+
ccInitResize();
|
|
522
|
+
}
|
|
523
|
+
|
|
465
524
|
window.MinionsCC = { toggleCommandCenter, ccNewSession, ccRestoreMessages, ccSaveState, ccUpdateSessionIndicator, ccAddMessage, ccSend, ccExecuteAction };
|
package/dashboard/layout.html
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<!-- Command Center Drawer -->
|
|
24
24
|
<div id="cc-drawer" style="display:none;position:fixed;top:0;right:0;bottom:0;width:420px;background:var(--surface);border-left:1px solid var(--border);z-index:300;flex-direction:column">
|
|
25
|
+
<div id="cc-resize-handle" class="cc-resize-handle"></div>
|
|
25
26
|
<div style="padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between">
|
|
26
27
|
<div style="display:flex;align-items:center;gap:8px">
|
|
27
28
|
<span style="font-weight:700;color:var(--blue);font-size:14px">Command Center</span>
|
package/dashboard/styles.css
CHANGED
|
@@ -581,6 +581,13 @@
|
|
|
581
581
|
.notif-badge.processing span:nth-child(2) { animation-delay: 0.2s; }
|
|
582
582
|
.notif-badge.processing span:nth-child(3) { animation-delay: 0.4s; }
|
|
583
583
|
@keyframes notifPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
584
|
+
|
|
585
|
+
/* Command Center resize handle */
|
|
586
|
+
.cc-resize-handle { position: absolute; top: 0; left: -3px; bottom: 0; width: 6px; cursor: col-resize; z-index: 301; }
|
|
587
|
+
.cc-resize-handle::after { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 2px; height: 32px; background: var(--border); border-radius: 2px; opacity: 0; transition: opacity 0.15s; }
|
|
588
|
+
.cc-resize-handle:hover::after, .cc-resize-handle.active::after { opacity: 1; background: var(--blue); }
|
|
589
|
+
body.cc-resizing { cursor: col-resize !important; user-select: none !important; }
|
|
590
|
+
|
|
584
591
|
.modal-body { padding: var(--space-7) var(--space-8); overflow-y: auto; overflow-x: auto; white-space: pre-wrap; font-size: var(--text-md); line-height: 1.7; color: var(--muted); font-family: Consolas, monospace; }
|
|
585
592
|
|
|
586
593
|
/* Responsive: tablet / narrow window */
|
package/engine/meeting.js
CHANGED
|
@@ -88,11 +88,11 @@ function discoverMeetingWork(config) {
|
|
|
88
88
|
const key = `meeting-${meeting.id}-r${round}-${concluder}`;
|
|
89
89
|
if (activeKeys.has(key)) continue;
|
|
90
90
|
|
|
91
|
-
const humanNotes = (meeting.humanNotes
|
|
92
|
-
const allFindings = Object.entries(meeting.findings
|
|
91
|
+
const humanNotes = (Array.isArray(meeting.humanNotes) ? meeting.humanNotes : []).map(n => '- ' + n).join('\n');
|
|
92
|
+
const allFindings = Object.entries(typeof meeting.findings === 'object' && meeting.findings ? meeting.findings : {}).map(([agent, f]) =>
|
|
93
93
|
`### ${agents[agent]?.name || agent}\n\n${f.content || '(no findings)'}`
|
|
94
94
|
).join('\n\n---\n\n');
|
|
95
|
-
const allDebate = Object.entries(meeting.debate
|
|
95
|
+
const allDebate = Object.entries(typeof meeting.debate === 'object' && meeting.debate ? meeting.debate : {}).map(([agent, d]) =>
|
|
96
96
|
`### ${agents[agent]?.name || agent}\n\n${d.content || '(no response)'}`
|
|
97
97
|
).join('\n\n---\n\n');
|
|
98
98
|
|
|
@@ -184,6 +184,10 @@ function discoverMeetingWork(config) {
|
|
|
184
184
|
function collectMeetingFindings(meetingId, agentId, roundName, output) {
|
|
185
185
|
const meeting = getMeeting(meetingId);
|
|
186
186
|
if (!meeting) return;
|
|
187
|
+
if (meeting.status === 'completed' || meeting.status === 'archived') {
|
|
188
|
+
log('info', `Ignoring late findings from ${agentId} for completed meeting ${meetingId}`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
187
191
|
|
|
188
192
|
const { text } = shared.parseStreamJsonOutput(output, { maxTextLength: 50000 });
|
|
189
193
|
const rawContent = (text || '').trim();
|
|
@@ -256,11 +260,35 @@ function addMeetingNote(meetingId, note) {
|
|
|
256
260
|
return meeting;
|
|
257
261
|
}
|
|
258
262
|
|
|
263
|
+
function _killMeetingDispatches(meetingId) {
|
|
264
|
+
try {
|
|
265
|
+
const DISPATCH_PATH = path.join(__dirname, '..', 'engine', 'dispatch.json');
|
|
266
|
+
const dispatch = safeJson(DISPATCH_PATH) || {};
|
|
267
|
+
const toKill = (dispatch.active || []).filter(d => d.meta?.meetingId === meetingId);
|
|
268
|
+
if (toKill.length === 0) return 0;
|
|
269
|
+
// Remove from active and move to completed
|
|
270
|
+
shared.mutateJsonFileLocked(DISPATCH_PATH, (dp) => {
|
|
271
|
+
dp.active = (dp.active || []).filter(d => d.meta?.meetingId !== meetingId);
|
|
272
|
+
dp.completed = dp.completed || [];
|
|
273
|
+
for (const d of toKill) {
|
|
274
|
+
dp.completed.push({ ...d, result: 'error', reason: 'Meeting ended/advanced by human', completed_at: new Date().toISOString() });
|
|
275
|
+
}
|
|
276
|
+
if (dp.completed.length > 100) dp.completed = dp.completed.slice(-100);
|
|
277
|
+
return dp;
|
|
278
|
+
}, { defaultValue: { pending: [], active: [], completed: [] } });
|
|
279
|
+
log('info', `Killed ${toKill.length} active meeting dispatch(es) for ${meetingId}`);
|
|
280
|
+
return toKill.length;
|
|
281
|
+
} catch (e) { log('warn', 'kill meeting dispatches: ' + e.message); return 0; }
|
|
282
|
+
}
|
|
283
|
+
|
|
259
284
|
function advanceMeetingRound(meetingId) {
|
|
260
285
|
const meeting = getMeeting(meetingId);
|
|
261
|
-
if (!meeting || meeting.status === 'completed') return null;
|
|
286
|
+
if (!meeting || meeting.status === 'completed' || meeting.status === 'archived') return null;
|
|
287
|
+
_killMeetingDispatches(meetingId);
|
|
262
288
|
if (meeting.status === 'investigating') { meeting.status = 'debating'; meeting.round = 2; }
|
|
263
289
|
else if (meeting.status === 'debating') { meeting.status = 'concluding'; meeting.round = 3; }
|
|
290
|
+
else if (meeting.status === 'concluding') { meeting.status = 'completed'; meeting.completedAt = new Date().toISOString(); }
|
|
291
|
+
else return meeting; // no change
|
|
264
292
|
meeting.roundStartedAt = new Date().toISOString();
|
|
265
293
|
saveMeeting(meeting);
|
|
266
294
|
return meeting;
|
|
@@ -269,6 +297,7 @@ function advanceMeetingRound(meetingId) {
|
|
|
269
297
|
function endMeeting(meetingId) {
|
|
270
298
|
const meeting = getMeeting(meetingId);
|
|
271
299
|
if (!meeting) return null;
|
|
300
|
+
_killMeetingDispatches(meetingId);
|
|
272
301
|
meeting.status = 'completed';
|
|
273
302
|
meeting.completedAt = new Date().toISOString();
|
|
274
303
|
saveMeeting(meeting);
|
|
@@ -294,6 +323,7 @@ function unarchiveMeeting(id) {
|
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
function deleteMeeting(id) {
|
|
326
|
+
_killMeetingDispatches(id);
|
|
297
327
|
const filePath = path.join(MEETINGS_DIR, id + '.json');
|
|
298
328
|
if (!fs.existsSync(filePath)) return false;
|
|
299
329
|
fs.unlinkSync(filePath);
|
package/engine/shared.js
CHANGED
|
@@ -258,6 +258,7 @@ function gitEnv() {
|
|
|
258
258
|
* Single source of truth — used by llm.js, consolidation.js, and lifecycle.js.
|
|
259
259
|
*/
|
|
260
260
|
function parseStreamJsonOutput(raw, { maxTextLength = 0 } = {}) {
|
|
261
|
+
if (typeof raw !== 'string') raw = '';
|
|
261
262
|
let text = '';
|
|
262
263
|
let usage = null;
|
|
263
264
|
let sessionId = null;
|
package/engine.js
CHANGED
|
@@ -1131,7 +1131,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1131
1131
|
|
|
1132
1132
|
const id = item.id; // Work item ID = PRD item ID — no indirection
|
|
1133
1133
|
const complexity = item.estimated_complexity || 'medium';
|
|
1134
|
-
const criteria = (item.acceptance_criteria
|
|
1134
|
+
const criteria = (Array.isArray(item.acceptance_criteria) ? item.acceptance_criteria : []).map(c => `- ${c}`).join('\n');
|
|
1135
1135
|
|
|
1136
1136
|
const newItem = {
|
|
1137
1137
|
id,
|
|
@@ -1471,7 +1471,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
1471
1471
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1472
1472
|
).join('\n');
|
|
1473
1473
|
vars.references = refs ? '## References\n\n' + refs : '';
|
|
1474
|
-
const ac = (item.acceptanceCriteria
|
|
1474
|
+
const ac = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1475
1475
|
vars.acceptance_criteria = ac ? '## Acceptance Criteria\n\n' + ac : '';
|
|
1476
1476
|
|
|
1477
1477
|
// Inject checkpoint context if agent left a checkpoint.json from a prior run
|
|
@@ -1496,9 +1496,9 @@ function discoverFromWorkItems(config, project) {
|
|
|
1496
1496
|
'',
|
|
1497
1497
|
'A previous agent run timed out but left a checkpoint. Continue from where it left off.',
|
|
1498
1498
|
'',
|
|
1499
|
-
cpData.completed && cpData.completed.length > 0 ? `### Completed\n${cpData.completed.map(s => '- ' + s).join('\n')}` : '',
|
|
1500
|
-
cpData.remaining && cpData.remaining.length > 0 ? `### Remaining\n${cpData.remaining.map(s => '- ' + s).join('\n')}` : '',
|
|
1501
|
-
cpData.blockers && cpData.blockers.length > 0 ? `### Blockers\n${cpData.blockers.map(s => '- ' + s).join('\n')}` : '',
|
|
1499
|
+
Array.isArray(cpData.completed) && cpData.completed.length > 0 ? `### Completed\n${cpData.completed.map(s => '- ' + s).join('\n')}` : '',
|
|
1500
|
+
Array.isArray(cpData.remaining) && cpData.remaining.length > 0 ? `### Remaining\n${cpData.remaining.map(s => '- ' + s).join('\n')}` : '',
|
|
1501
|
+
Array.isArray(cpData.blockers) && cpData.blockers.length > 0 ? `### Blockers\n${cpData.blockers.map(s => '- ' + s).join('\n')}` : '',
|
|
1502
1502
|
cpData.branch_state ? `### Branch State\n${cpData.branch_state}` : '',
|
|
1503
1503
|
].filter(Boolean).join('\n');
|
|
1504
1504
|
vars.checkpoint_context = cpSummary;
|
|
@@ -1806,7 +1806,7 @@ function discoverCentralWorkItems(config) {
|
|
|
1806
1806
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1807
1807
|
).join('\n');
|
|
1808
1808
|
vars.references = fanRefs ? '## References\n\n' + fanRefs : '';
|
|
1809
|
-
const fanAc = (item.acceptanceCriteria
|
|
1809
|
+
const fanAc = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1810
1810
|
vars.acceptance_criteria = fanAc ? '## Acceptance Criteria\n\n' + fanAc : '';
|
|
1811
1811
|
|
|
1812
1812
|
if (workType === 'ask') {
|
|
@@ -1888,7 +1888,7 @@ function discoverCentralWorkItems(config) {
|
|
|
1888
1888
|
'- [' + (r.title || r.url) + '](' + r.url + ')' + (r.type ? ' (' + r.type + ')' : '')
|
|
1889
1889
|
).join('\n');
|
|
1890
1890
|
vars.references = normRefs ? '## References\n\n' + normRefs : '';
|
|
1891
|
-
const normAc = (item.acceptanceCriteria
|
|
1891
|
+
const normAc = (Array.isArray(item.acceptanceCriteria) ? item.acceptanceCriteria : []).map(c => '- [ ] ' + c).join('\n');
|
|
1892
1892
|
vars.acceptance_criteria = normAc ? '## Acceptance Criteria\n\n' + normAc : '';
|
|
1893
1893
|
|
|
1894
1894
|
// Inject checkpoint context if agent left a checkpoint.json from a prior run
|
|
@@ -1914,9 +1914,9 @@ function discoverCentralWorkItems(config) {
|
|
|
1914
1914
|
'',
|
|
1915
1915
|
'A previous agent run timed out but left a checkpoint. Continue from where it left off.',
|
|
1916
1916
|
'',
|
|
1917
|
-
cpData.completed && cpData.completed.length > 0 ? `### Completed\n${cpData.completed.map(s => '- ' + s).join('\n')}` : '',
|
|
1918
|
-
cpData.remaining && cpData.remaining.length > 0 ? `### Remaining\n${cpData.remaining.map(s => '- ' + s).join('\n')}` : '',
|
|
1919
|
-
cpData.blockers && cpData.blockers.length > 0 ? `### Blockers\n${cpData.blockers.map(s => '- ' + s).join('\n')}` : '',
|
|
1917
|
+
Array.isArray(cpData.completed) && cpData.completed.length > 0 ? `### Completed\n${cpData.completed.map(s => '- ' + s).join('\n')}` : '',
|
|
1918
|
+
Array.isArray(cpData.remaining) && cpData.remaining.length > 0 ? `### Remaining\n${cpData.remaining.map(s => '- ' + s).join('\n')}` : '',
|
|
1919
|
+
Array.isArray(cpData.blockers) && cpData.blockers.length > 0 ? `### Blockers\n${cpData.blockers.map(s => '- ' + s).join('\n')}` : '',
|
|
1920
1920
|
cpData.branch_state ? `### Branch State\n${cpData.branch_state}` : '',
|
|
1921
1921
|
].filter(Boolean).join('\n');
|
|
1922
1922
|
vars.checkpoint_context = cpSummary;
|
|
@@ -2125,7 +2125,7 @@ function discoverWork(config) {
|
|
|
2125
2125
|
for (const item of allWork) {
|
|
2126
2126
|
addToDispatch(item);
|
|
2127
2127
|
if (item.meta?.source === 'pr-human-feedback') {
|
|
2128
|
-
clearPendingHumanFeedbackFlag(item.meta
|
|
2128
|
+
clearPendingHumanFeedbackFlag(item.meta?.project, item.meta?.pr?.id);
|
|
2129
2129
|
}
|
|
2130
2130
|
}
|
|
2131
2131
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.149",
|
|
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"
|