@yemi33/minions 0.1.87 → 0.1.89
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 +14 -0
- package/dashboard/js/command-center.js +3 -3
- package/dashboard/js/render-meetings.js +6 -6
- package/dashboard/js/render-plans.js +42 -10
- package/dashboard/js/render-prd.js +3 -2
- package/dashboard.js +33 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.89 (2026-04-01)
|
|
4
|
+
|
|
5
|
+
### Dashboard
|
|
6
|
+
- dashboard.js
|
|
7
|
+
- dashboard/js/command-center.js
|
|
8
|
+
- dashboard/js/render-meetings.js
|
|
9
|
+
|
|
10
|
+
## 0.1.88 (2026-04-01)
|
|
11
|
+
|
|
12
|
+
### Dashboard
|
|
13
|
+
- dashboard.js
|
|
14
|
+
- dashboard/js/render-plans.js
|
|
15
|
+
- dashboard/js/render-prd.js
|
|
16
|
+
|
|
3
17
|
## 0.1.87 (2026-04-01)
|
|
4
18
|
|
|
5
19
|
### Engine
|
|
@@ -347,7 +347,7 @@ async function ccExecuteAction(action) {
|
|
|
347
347
|
status.innerHTML = '✓ Plan edited: <strong>' + escHtml(action.file) + '</strong>';
|
|
348
348
|
status.style.color = 'var(--green)';
|
|
349
349
|
} else {
|
|
350
|
-
status.innerHTML = data.answer ?
|
|
350
|
+
status.innerHTML = data.answer ? renderMd(data.answer) : '✗ Could not edit plan';
|
|
351
351
|
status.style.color = data.answer ? 'var(--muted)' : 'var(--red)';
|
|
352
352
|
}
|
|
353
353
|
break;
|
|
@@ -379,7 +379,7 @@ async function ccExecuteAction(action) {
|
|
|
379
379
|
status.innerHTML = '✓ Edited: <strong>' + escHtml(action.file) + '</strong>';
|
|
380
380
|
status.style.color = 'var(--green)';
|
|
381
381
|
} else {
|
|
382
|
-
status.innerHTML = data.answer ?
|
|
382
|
+
status.innerHTML = data.answer ? renderMd(data.answer) : '✗ Could not edit file';
|
|
383
383
|
status.style.color = data.answer ? 'var(--muted)' : 'var(--red)';
|
|
384
384
|
}
|
|
385
385
|
break;
|
|
@@ -414,7 +414,7 @@ async function ccExecuteAction(action) {
|
|
|
414
414
|
case 'create-meeting': {
|
|
415
415
|
const res = await fetch('/api/meetings', {
|
|
416
416
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
417
|
-
body: JSON.stringify({ topic: action.topic,
|
|
417
|
+
body: JSON.stringify({ title: action.title || action.topic, agenda: action.agenda || action.topic, participants: action.agents, rounds: action.rounds, project: action.project })
|
|
418
418
|
});
|
|
419
419
|
if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error(d.error || 'Meeting create failed'); }
|
|
420
420
|
const d = await res.json();
|
|
@@ -83,9 +83,9 @@ function openMeetingDetail(id) {
|
|
|
83
83
|
'<span style="font-size:10px;color:var(--muted)">' + escHtml(m.createdAt?.slice(0, 16).replace('T', ' ') || '') + '</span>' +
|
|
84
84
|
'</div>';
|
|
85
85
|
|
|
86
|
-
// Agenda
|
|
87
|
-
html += '<div style="background:var(--surface2);padding:8px 12px;border-radius:6px;font-size:12px">' +
|
|
88
|
-
'<strong>Agenda:</strong
|
|
86
|
+
// Agenda — render markdown-like formatting
|
|
87
|
+
html += '<div style="background:var(--surface2);padding:8px 12px;border-radius:6px;font-size:12px;white-space:pre-wrap;line-height:1.6">' +
|
|
88
|
+
'<strong>Agenda:</strong>\n' + renderMd(m.agenda || '') + '</div>';
|
|
89
89
|
|
|
90
90
|
// Per-agent panels
|
|
91
91
|
for (const agent of (m.participants || [])) {
|
|
@@ -96,14 +96,14 @@ function openMeetingDetail(id) {
|
|
|
96
96
|
if (m.findings?.[agent]) {
|
|
97
97
|
html += '<div style="padding:8px 12px;font-size:11px;border-bottom:1px solid var(--border)">' +
|
|
98
98
|
'<div style="color:var(--muted);font-size:10px;margin-bottom:4px">Round 1 — Findings</div>' +
|
|
99
|
-
'<div style="
|
|
99
|
+
'<div style="word-break:break-word;max-height:300px;overflow-y:auto">' + renderMd(m.findings[agent].content || '') + '</div></div>';
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// Debate
|
|
103
103
|
if (m.debate?.[agent]) {
|
|
104
104
|
html += '<div style="padding:8px 12px;font-size:11px;border-bottom:1px solid var(--border)">' +
|
|
105
105
|
'<div style="color:var(--muted);font-size:10px;margin-bottom:4px">Round 2 — Debate</div>' +
|
|
106
|
-
'<div style="
|
|
106
|
+
'<div style="word-break:break-word;max-height:300px;overflow-y:auto">' + renderMd(m.debate[agent].content || '') + '</div></div>';
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// Status
|
|
@@ -118,7 +118,7 @@ function openMeetingDetail(id) {
|
|
|
118
118
|
if (m.conclusion) {
|
|
119
119
|
html += '<div style="background:rgba(63,185,80,0.08);border:1px solid var(--green);border-radius:6px;padding:10px 14px">' +
|
|
120
120
|
'<div style="color:var(--green);font-weight:600;font-size:12px;margin-bottom:6px">Conclusion (by ' + escHtml(m.conclusion.agent || '?') + ')</div>' +
|
|
121
|
-
'<div style="font-size:12px;
|
|
121
|
+
'<div style="font-size:12px;word-break:break-word;max-height:400px;overflow-y:auto">' + renderMd(m.conclusion.content || '') + '</div></div>';
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// Human notes
|
|
@@ -461,9 +461,16 @@ async function planView(file) {
|
|
|
461
461
|
'onclick="planDelete(\'' + escHtml(normalizedFile) + '\')">Delete</button>' +
|
|
462
462
|
'</div>';
|
|
463
463
|
document.getElementById('modal-title').innerHTML = escHtml(title) + (versionLabel ? ' <span style="font-size:11px;font-weight:700;padding:1px 6px;border-radius:3px;background:rgba(56,139,253,0.15);color:var(--blue)">' + escHtml(versionLabel) + '</span>' : '') + lastModLabel + actionBtns;
|
|
464
|
-
document.getElementById('modal-body')
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
const modalBody = document.getElementById('modal-body');
|
|
465
|
+
if (normalizedFile.endsWith('.json')) {
|
|
466
|
+
modalBody.textContent = text;
|
|
467
|
+
modalBody.style.fontFamily = 'Consolas, monospace';
|
|
468
|
+
modalBody.style.whiteSpace = 'pre-wrap';
|
|
469
|
+
} else {
|
|
470
|
+
modalBody.innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(text) + '</div>';
|
|
471
|
+
modalBody.style.fontFamily = "'Segoe UI', system-ui, sans-serif";
|
|
472
|
+
modalBody.style.whiteSpace = 'normal';
|
|
473
|
+
}
|
|
467
474
|
_modalDocContext = { title, content: text, selection: '' };
|
|
468
475
|
_modalFilePath = resolvedPath || ((normalizedFile.endsWith('.json') ? 'prd/' : 'plans/') + normalizedFile); showModalQa();
|
|
469
476
|
// Clear notification badge when opening this document
|
|
@@ -509,6 +516,25 @@ async function planDelete(file) {
|
|
|
509
516
|
} catch (e) { alert('Error: ' + e.message); }
|
|
510
517
|
}
|
|
511
518
|
|
|
519
|
+
async function planArchive(file) {
|
|
520
|
+
if (!confirm('Archive PRD "' + file + '"? Work items will be preserved.')) return;
|
|
521
|
+
try {
|
|
522
|
+
const res = await fetch('/api/plans/archive', {
|
|
523
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
524
|
+
body: JSON.stringify({ file })
|
|
525
|
+
});
|
|
526
|
+
if (res.ok) {
|
|
527
|
+
closeModal();
|
|
528
|
+
showToast('cmd-toast', 'PRD archived', true);
|
|
529
|
+
refreshPlans();
|
|
530
|
+
refresh();
|
|
531
|
+
} else {
|
|
532
|
+
const d = await res.json();
|
|
533
|
+
alert('Archive failed: ' + (d.error || 'unknown'));
|
|
534
|
+
}
|
|
535
|
+
} catch (e) { alert('Error: ' + e.message); }
|
|
536
|
+
}
|
|
537
|
+
|
|
512
538
|
async function planPause(file, btn) {
|
|
513
539
|
if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Pausing...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
|
|
514
540
|
try {
|
|
@@ -578,9 +604,15 @@ async function planOpenInDocChat(file) {
|
|
|
578
604
|
try { title = JSON.parse(raw).plan_summary || file; } catch {}
|
|
579
605
|
}
|
|
580
606
|
document.getElementById('modal-title').textContent = 'Edit: ' + title;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
607
|
+
if (normalizedFile.endsWith('.json')) {
|
|
608
|
+
document.getElementById('modal-body').textContent = text;
|
|
609
|
+
document.getElementById('modal-body').style.fontFamily = 'Consolas, monospace';
|
|
610
|
+
document.getElementById('modal-body').style.whiteSpace = 'pre-wrap';
|
|
611
|
+
} else {
|
|
612
|
+
document.getElementById('modal-body').innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(text) + '</div>';
|
|
613
|
+
document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
|
|
614
|
+
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
615
|
+
}
|
|
584
616
|
_modalDocContext = { title: title, content: text, selection: '' };
|
|
585
617
|
_modalFilePath = resolvedPath || ((normalizedFile.endsWith('.json') ? 'prd/' : 'plans/') + normalizedFile); showModalQa();
|
|
586
618
|
const card = findCardForFile(_modalFilePath);
|
|
@@ -612,9 +644,9 @@ async function openVerifyGuide(file) {
|
|
|
612
644
|
const content = await fetch('/api/plans/' + encodeURIComponent(normalizedFile)).then(r => r.text());
|
|
613
645
|
document.getElementById('modal-title').innerHTML = 'Manual Testing Guide' +
|
|
614
646
|
' <button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;margin-left:8px;vertical-align:middle" onclick="openArchivedPrdModal()">Back</button>';
|
|
615
|
-
document.getElementById('modal-body').
|
|
616
|
-
document.getElementById('modal-body').style.fontFamily = '
|
|
617
|
-
document.getElementById('modal-body').style.whiteSpace = '
|
|
647
|
+
document.getElementById('modal-body').innerHTML = '<div style="font-size:12px;line-height:1.6">' + renderMd(content) + '</div>';
|
|
648
|
+
document.getElementById('modal-body').style.fontFamily = "'Segoe UI', system-ui, sans-serif";
|
|
649
|
+
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
618
650
|
_modalDocContext = { title: 'Manual Testing Guide', content, selection: '' };
|
|
619
651
|
_modalFilePath = 'prd/' + normalizedFile; showModalQa();
|
|
620
652
|
const card = findCardForFile(_modalFilePath);
|
|
@@ -642,4 +674,4 @@ async function triggerVerify(file, btn) {
|
|
|
642
674
|
} catch (e) { if (btn) { btn.textContent = btn.dataset.origText || 'Verify'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } alert('Error: ' + e.message); }
|
|
643
675
|
}
|
|
644
676
|
|
|
645
|
-
window.MinionsPlans = { openCreatePlanModal, refreshPlans, derivePlanStatus, renderPlans, openArchivedPlansModal, qaDisablePrdButtons, showPlanVersionActions, qaJustSave, planExecute, planSubmitRevise, planShowRevise, planHideRevise, planView, planApprove, planDelete, planPause, planReject, planDiscuss, planOpenInDocChat, planRegeneratePRD, openVerifyGuide, triggerVerify };
|
|
677
|
+
window.MinionsPlans = { openCreatePlanModal, refreshPlans, derivePlanStatus, renderPlans, openArchivedPlansModal, qaDisablePrdButtons, showPlanVersionActions, qaJustSave, planExecute, planSubmitRevise, planShowRevise, planHideRevise, planView, planApprove, planArchive, planDelete, planPause, planReject, planDiscuss, planOpenInDocChat, planRegeneratePRD, openVerifyGuide, triggerVerify };
|
|
@@ -36,7 +36,7 @@ function renderPrd(prd, prog) {
|
|
|
36
36
|
actions = ' <button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--green);border-color:var(--green);margin-left:4px" onclick="planApprove(\'' + escHtml(prdFile) + '\',this)">Approve</button>';
|
|
37
37
|
} else if (effectiveStatus === 'completed') {
|
|
38
38
|
actions = ' <button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--green);border-color:var(--green);margin-left:4px" onclick="triggerVerify(\'' + escHtml(prdFile) + '\',this)">Verify</button>' +
|
|
39
|
-
' <button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--muted);border-color:var(--border);margin-left:4px" onclick="
|
|
39
|
+
' <button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--muted);border-color:var(--border);margin-left:4px" onclick="planArchive(\'' + escHtml(prdFile) + '\')">Archive</button>';
|
|
40
40
|
} else if (effectiveStatus === 'in-progress') {
|
|
41
41
|
actions = ' <button class="pr-pager-btn" style="font-size:9px;padding:1px 6px;color:var(--yellow);border-color:var(--yellow);margin-left:4px" onclick="planPause(\'' + escHtml(prdFile) + '\',this)">Pause</button>';
|
|
42
42
|
} else if (effectiveStatus === 'paused') {
|
|
@@ -238,7 +238,7 @@ function renderPrdProgress(prog) {
|
|
|
238
238
|
? '<span onclick="event.stopPropagation();triggerVerify(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:9px;padding:1px 6px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px">Verify</span>'
|
|
239
239
|
: '<span onclick="event.stopPropagation();planPause(\'' + escHtml(g.file) + '\',this)" style="color:var(--yellow);cursor:pointer;font-size:9px;padding:1px 6px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.3);border-radius:3px">Pause</span>';
|
|
240
240
|
const archiveBtn = isCompleted
|
|
241
|
-
? '<span onclick="event.stopPropagation();
|
|
241
|
+
? '<span onclick="event.stopPropagation();planArchive(\'' + escHtml(g.file) + '\')" style="color:var(--muted);cursor:pointer;font-size:9px;padding:1px 6px;background:var(--surface);border:1px solid var(--border);border-radius:3px">Archive</span>'
|
|
242
242
|
: '';
|
|
243
243
|
const deleteBtn = '<span onclick="event.stopPropagation();planDelete(\'' + escHtml(g.file) + '\')" style="color:var(--red);cursor:pointer;font-size:9px;padding:1px 6px;background:rgba(248,81,73,0.1);border:1px solid rgba(248,81,73,0.3);border-radius:3px">Delete</span>';
|
|
244
244
|
const sourcePlanLink = g.sourcePlan
|
|
@@ -250,6 +250,7 @@ function renderPrdProgress(prog) {
|
|
|
250
250
|
'<span style="color:var(--text)">' + escHtml(summary || g.file) + '</span>' +
|
|
251
251
|
pausedLabel +
|
|
252
252
|
staleLabel +
|
|
253
|
+
'<span style="font-weight:700;font-size:11px;color:' + (done === g.items.length && g.items.length > 0 ? 'var(--green)' : 'var(--text)') + '">' + (g.items.length > 0 ? Math.round((done / g.items.length) * 100) : 0) + '%</span>' +
|
|
253
254
|
'<span style="color:var(--muted);font-weight:400;font-size:10px">' + g.items.length + ' items' +
|
|
254
255
|
(done ? ' · ' + done + ' done' : '') + (wip ? ' · ' + wip + ' active' : '') +
|
|
255
256
|
'</span>' +
|
package/dashboard.js
CHANGED
|
@@ -373,7 +373,7 @@ Available action types:
|
|
|
373
373
|
- **file-edit**: Edit any minions file via LLM. Fields: file (path relative to minions dir), instruction (what to change).
|
|
374
374
|
- **schedule**: Create or update a scheduled task. Fields: id (unique slug), title, cron (3-field: minute hour dayOfWeek), workType (implement/test/explore/ask/review/fix), project (optional), agent (optional), description (optional), priority (optional), enabled (default true). Example cron: "0 9 2" = every Tuesday at 9am.
|
|
375
375
|
- **delete-schedule**: Delete a scheduled task. Fields: id.
|
|
376
|
-
- **create-meeting**: Start a team meeting. Fields:
|
|
376
|
+
- **create-meeting**: Start a team meeting. Fields: title (short meeting name), agenda (detailed text — what agents should investigate/debate, numbered items work best), agents (array of agent IDs), rounds (optional, default 3), project (optional).
|
|
377
377
|
|
|
378
378
|
## Rules
|
|
379
379
|
|
|
@@ -2065,6 +2065,37 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2065
2065
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
2066
2066
|
}
|
|
2067
2067
|
|
|
2068
|
+
async function handlePlansArchive(req, res) {
|
|
2069
|
+
try {
|
|
2070
|
+
const body = await readBody(req);
|
|
2071
|
+
if (!body.file) return jsonReply(res, 400, { error: 'file required' });
|
|
2072
|
+
if (body.file.includes('..') || body.file.includes('\0') || body.file.includes('/') || body.file.includes('\\')) {
|
|
2073
|
+
return jsonReply(res, 400, { error: 'invalid filename' });
|
|
2074
|
+
}
|
|
2075
|
+
const planPath = resolvePlanPath(body.file);
|
|
2076
|
+
if (!fs.existsSync(planPath)) return jsonReply(res, 404, { error: 'plan not found' });
|
|
2077
|
+
|
|
2078
|
+
// Move to archive directory
|
|
2079
|
+
const archiveDir = body.file.endsWith('.json') ? path.join(PRD_DIR, 'archive') : path.join(PLANS_DIR, 'archive');
|
|
2080
|
+
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
2081
|
+
const archivePath = path.join(archiveDir, body.file);
|
|
2082
|
+
fs.renameSync(planPath, archivePath);
|
|
2083
|
+
|
|
2084
|
+
// Mark archived in JSON if PRD
|
|
2085
|
+
if (body.file.endsWith('.json')) {
|
|
2086
|
+
try {
|
|
2087
|
+
const prd = JSON.parse(safeRead(archivePath) || '{}');
|
|
2088
|
+
prd.status = 'archived';
|
|
2089
|
+
prd.archivedAt = new Date().toISOString();
|
|
2090
|
+
safeWrite(archivePath, prd);
|
|
2091
|
+
} catch { /* optional */ }
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
invalidateStatusCache();
|
|
2095
|
+
return jsonReply(res, 200, { ok: true, archived: body.file });
|
|
2096
|
+
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2068
2099
|
async function handlePlansRevise(req, res) {
|
|
2069
2100
|
try {
|
|
2070
2101
|
const body = await readBody(req);
|
|
@@ -3083,6 +3114,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3083
3114
|
{ method: 'POST', path: '/api/plans/reject', desc: 'Reject a plan', params: 'file, rejectedBy?, reason?', handler: handlePlansReject },
|
|
3084
3115
|
{ method: 'POST', path: '/api/plans/regenerate', desc: 'Reset pending/failed work items for a plan so they re-materialize', params: 'source', handler: handlePlansRegenerate },
|
|
3085
3116
|
{ method: 'POST', path: '/api/plans/delete', desc: 'Delete a plan file and clean up work items', params: 'file', handler: handlePlansDelete },
|
|
3117
|
+
{ method: 'POST', path: '/api/plans/archive', desc: 'Move a plan/PRD to archive (preserves work items)', params: 'file', handler: handlePlansArchive },
|
|
3086
3118
|
{ method: 'POST', path: '/api/plans/revise', desc: 'Request revision with feedback, dispatches agent to revise', params: 'file, feedback, requestedBy?', handler: handlePlansRevise },
|
|
3087
3119
|
{ method: 'POST', path: '/api/plans/discuss', desc: 'Generate a plan discussion session script for Claude CLI', params: 'file', handler: handlePlansDiscuss },
|
|
3088
3120
|
{ method: 'GET', path: /^\/api\/plans\/archive\/([^?]+)$/, desc: 'Read an archived plan file', handler: handlePlansArchiveRead },
|
package/package.json
CHANGED