@yemi33/minions 0.1.93 → 0.1.95
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 +10 -0
- package/dashboard/js/render-plans.js +59 -12
- package/dashboard/js/render-prs.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -80,9 +80,14 @@ function derivePlanStatus(prdFile, mdFile, prdJsonStatus, workItems) {
|
|
|
80
80
|
if (allDone && !hasActiveWork) return 'completed';
|
|
81
81
|
if (hasActiveWork || hasPendingPrd) return 'in-progress';
|
|
82
82
|
if (hasFailed && !hasActiveWork) return 'has-failures';
|
|
83
|
+
|
|
83
84
|
if (prdJsonStatus === 'awaiting-approval' && implementWi.length === 0) return 'awaiting-approval';
|
|
84
85
|
if (prdJsonStatus === 'approved' && implementWi.length === 0) return 'approved';
|
|
85
86
|
|
|
87
|
+
// plan-to-prd conversion done but no implement items and PRD is gone — truly completed
|
|
88
|
+
const prdConversionDone = wi.some(w => w.type === 'plan-to-prd' && w.status === 'done');
|
|
89
|
+
if (prdConversionDone && implementWi.length === 0) return 'completed';
|
|
90
|
+
|
|
86
91
|
return prdJsonStatus || 'active';
|
|
87
92
|
}
|
|
88
93
|
|
|
@@ -172,7 +177,14 @@ function renderPlans(plans) {
|
|
|
172
177
|
if (prdFile && p.format !== 'prd') {
|
|
173
178
|
const linkedPrd = plans.find(pp => pp.file === prdFile && pp.format === 'prd');
|
|
174
179
|
if (linkedPrd) prdJsonStatus = linkedPrd.status || prdJsonStatus;
|
|
180
|
+
// If the linked PRD was archived, treat the .md plan as completed
|
|
181
|
+
else if (!linkedPrd) {
|
|
182
|
+
const archivedPrd = archivedPlans.find(pp => pp.file === prdFile && pp.format === 'prd');
|
|
183
|
+
if (archivedPrd) prdJsonStatus = 'completed';
|
|
184
|
+
}
|
|
175
185
|
}
|
|
186
|
+
// 'converted' means plan-to-PRD succeeded — treat as 'approved' for status derivation
|
|
187
|
+
if (prdJsonStatus === 'converted') prdJsonStatus = prdFile ? 'approved' : 'completed';
|
|
176
188
|
|
|
177
189
|
// Single source of truth: derive status from work items
|
|
178
190
|
const effectiveStatus = isArchived ? 'completed' : derivePlanStatus(prdFile, p.file, prdJsonStatus, allWi);
|
|
@@ -180,7 +192,8 @@ function renderPlans(plans) {
|
|
|
180
192
|
const statusLabelsMap = {
|
|
181
193
|
'completed': 'Completed', 'in-progress': 'In Progress', 'paused': 'Paused',
|
|
182
194
|
'awaiting-approval': 'Awaiting Approval', 'approved': 'Approved', 'rejected': 'Rejected',
|
|
183
|
-
'revision-requested': 'Revision Requested', 'has-failures': 'Has Failures', 'active': 'Active'
|
|
195
|
+
'revision-requested': 'Revision Requested', 'has-failures': 'Has Failures', 'active': 'Active',
|
|
196
|
+
'converted': 'Converted to PRD', 'draft': 'Draft'
|
|
184
197
|
};
|
|
185
198
|
const label = statusLabelsMap[effectiveStatus] || effectiveStatus;
|
|
186
199
|
const needsAction = (effectiveStatus === 'awaiting-approval' || effectiveStatus === 'paused') && !isArchived;
|
|
@@ -222,6 +235,10 @@ function renderPlans(plans) {
|
|
|
222
235
|
: '';
|
|
223
236
|
const verifyBtn = showVerify ? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--green)" ' +
|
|
224
237
|
'onclick="event.stopPropagation();triggerVerify(\'' + escHtml(prdFile) + '\',this)">Verify</button>' : '';
|
|
238
|
+
const showArchive = !isArchived;
|
|
239
|
+
const archiveFile = prdFile || p.file;
|
|
240
|
+
const archiveBtn = showArchive ? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px" ' +
|
|
241
|
+
'onclick="event.stopPropagation();planArchive(\'' + escHtml(archiveFile) + '\',this)">Archive</button>' : '';
|
|
225
242
|
const deleteBtn = !isArchived ? '<button class="pr-pager-btn" style="font-size:9px;padding:2px 8px;color:var(--red)" ' +
|
|
226
243
|
'onclick="event.stopPropagation();planDelete(\'' + escHtml(p.file) + '\')">Delete</button>' : '';
|
|
227
244
|
|
|
@@ -238,7 +255,7 @@ function renderPlans(plans) {
|
|
|
238
255
|
(p.updatedAt ? '<span title="Last updated: ' + p.updatedAt + '">Updated ' + timeAgo(p.updatedAt) + '</span>' : '') +
|
|
239
256
|
(p.completedAt ? '<span>' + p.completedAt.slice(0, 10) + '</span>' : '') +
|
|
240
257
|
(p.generatedBy ? '<span>by ' + escHtml(p.generatedBy) + '</span>' : '') +
|
|
241
|
-
executeBtn + pauseBtn + resumeBtn + verifyBtn + deleteBtn +
|
|
258
|
+
executeBtn + pauseBtn + resumeBtn + verifyBtn + archiveBtn + deleteBtn +
|
|
242
259
|
'</div>' +
|
|
243
260
|
'</div>' +
|
|
244
261
|
'</div>' +
|
|
@@ -432,11 +449,12 @@ async function planView(file) {
|
|
|
432
449
|
const isPaused = planStatus === 'awaiting-approval' || planStatus === 'paused';
|
|
433
450
|
// Check if work is in progress for this plan
|
|
434
451
|
const wi = window._lastWorkItems || [];
|
|
452
|
+
// Find the linked PRD for this .md plan (if any)
|
|
453
|
+
const linkedPrdFile = isMdPlan ? (window._lastStatus?.plans || []).find(p => p.sourcePlan === normalizedFile && p.format === 'prd')?.file : null;
|
|
435
454
|
const hasActiveWork = wi.some(w =>
|
|
436
455
|
(w.status === 'pending' || w.status === 'dispatched') &&
|
|
437
456
|
(w.planFile === normalizedFile || w.sourcePlan === normalizedFile ||
|
|
438
|
-
|
|
439
|
-
(isMdPlan && w.sourcePlan && w.sourcePlan.endsWith('.json')))
|
|
457
|
+
(linkedPrdFile && w.sourcePlan === linkedPrdFile))
|
|
440
458
|
);
|
|
441
459
|
const prdCompleted = wi.some(w => w.type === 'plan-to-prd' && w.status === 'done' && w.planFile === normalizedFile);
|
|
442
460
|
// Check if a PRD already exists for this plan (via plans list sourcePlan linkage)
|
|
@@ -454,9 +472,12 @@ async function planView(file) {
|
|
|
454
472
|
const modalVerifyBtn = isModalCompleted ? '<button class="pr-pager-btn" style="font-size:10px;padding:2px 10px;color:var(--green)" ' +
|
|
455
473
|
'onclick="triggerVerify(\'' + escHtml(normalizedFile) + '\',this)">Verify</button>' : '';
|
|
456
474
|
|
|
475
|
+
const modalArchiveBtn = '<button class="pr-pager-btn" style="font-size:10px;padding:2px 10px;color:var(--muted)" ' +
|
|
476
|
+
'onclick="planArchive(\'' + escHtml(normalizedFile) + '\')">Archive</button>';
|
|
457
477
|
const lastModLabel = lastMod ? '<div style="font-size:10px;color:var(--muted);font-weight:400;margin-top:2px">Last updated: ' + new Date(lastMod).toLocaleString() + '</div>' : '';
|
|
458
478
|
const actionBtns = '<div style="display:flex;gap:4px;flex-wrap:wrap;margin-top:4px">' +
|
|
459
479
|
(modalCompletedLabel || '') + (modalInProgressLabel || '') + (modalExecuteBtn || '') + (modalPauseBtn || '') + (modalResumeBtn || '') + (modalVerifyBtn || '') +
|
|
480
|
+
' ' + modalArchiveBtn +
|
|
460
481
|
' <button class="pr-pager-btn" style="font-size:10px;padding:2px 10px;color:var(--red)" ' +
|
|
461
482
|
'onclick="planDelete(\'' + escHtml(normalizedFile) + '\')">Delete</button>' +
|
|
462
483
|
'</div>';
|
|
@@ -516,23 +537,27 @@ async function planDelete(file) {
|
|
|
516
537
|
} catch (e) { alert('Error: ' + e.message); }
|
|
517
538
|
}
|
|
518
539
|
|
|
519
|
-
async function planArchive(file) {
|
|
520
|
-
if (
|
|
540
|
+
async function planArchive(file, btn) {
|
|
541
|
+
if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Archiving...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
|
|
542
|
+
function resetBtn() { if (btn) { btn.textContent = btn.dataset.origText || 'Archive'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
|
|
521
543
|
try {
|
|
522
544
|
const res = await fetch('/api/plans/archive', {
|
|
523
545
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
524
546
|
body: JSON.stringify({ file })
|
|
525
547
|
});
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
548
|
+
const ct = res.headers.get('content-type') || '';
|
|
549
|
+
if (!ct.includes('json')) { resetBtn(); alert('Archive failed — dashboard may need a restart'); return; }
|
|
550
|
+
const d = await res.json().catch(() => ({}));
|
|
551
|
+
if (res.ok && d.ok) {
|
|
552
|
+
try { closeModal(); } catch { /* may not be open */ }
|
|
553
|
+
showToast('cmd-toast', 'Archived' + (file.endsWith('.json') ? ' PRD and source plan' : ''), true);
|
|
529
554
|
refreshPlans();
|
|
530
555
|
refresh();
|
|
531
556
|
} else {
|
|
532
|
-
|
|
557
|
+
resetBtn();
|
|
533
558
|
alert('Archive failed: ' + (d.error || 'unknown'));
|
|
534
559
|
}
|
|
535
|
-
} catch (e) { alert('Error: ' + e.message); }
|
|
560
|
+
} catch (e) { resetBtn(); alert('Error: ' + e.message); }
|
|
536
561
|
}
|
|
537
562
|
|
|
538
563
|
async function planPause(file, btn) {
|
|
@@ -674,4 +699,26 @@ async function triggerVerify(file, btn) {
|
|
|
674
699
|
} catch (e) { if (btn) { btn.textContent = btn.dataset.origText || 'Verify'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } alert('Error: ' + e.message); }
|
|
675
700
|
}
|
|
676
701
|
|
|
677
|
-
|
|
702
|
+
async function planUnarchive(file, btn) {
|
|
703
|
+
if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Restoring...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
|
|
704
|
+
function resetBtn() { if (btn) { btn.textContent = btn.dataset.origText || 'Unarchive'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } }
|
|
705
|
+
try {
|
|
706
|
+
const res = await fetch('/api/plans/unarchive', {
|
|
707
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
708
|
+
body: JSON.stringify({ file })
|
|
709
|
+
});
|
|
710
|
+
const ct = res.headers.get('content-type') || '';
|
|
711
|
+
if (!ct.includes('json')) { resetBtn(); alert('Unarchive failed — dashboard may need a restart'); return; }
|
|
712
|
+
const d = await res.json().catch(() => ({}));
|
|
713
|
+
if (res.ok && d.ok) {
|
|
714
|
+
showToast('cmd-toast', 'Restored from archive', true);
|
|
715
|
+
refreshPlans();
|
|
716
|
+
refresh();
|
|
717
|
+
} else {
|
|
718
|
+
resetBtn();
|
|
719
|
+
alert('Unarchive failed: ' + (d.error || 'unknown'));
|
|
720
|
+
}
|
|
721
|
+
} catch (e) { resetBtn(); alert('Error: ' + e.message); }
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
window.MinionsPlans = { openCreatePlanModal, refreshPlans, derivePlanStatus, renderPlans, openArchivedPlansModal, qaDisablePrdButtons, showPlanVersionActions, qaJustSave, planExecute, planSubmitRevise, planShowRevise, planHideRevise, planView, planApprove, planArchive, planUnarchive, planDelete, planPause, planReject, planDiscuss, planOpenInDocChat, planRegeneratePRD, openVerifyGuide, triggerVerify };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
let allPrs = [];
|
|
4
4
|
let prPage = 0;
|
|
5
|
-
const PR_PER_PAGE =
|
|
5
|
+
const PR_PER_PAGE = 25;
|
|
6
6
|
|
|
7
7
|
function prRow(pr) {
|
|
8
8
|
// Minions review (agent) state — separate from ADO human review
|
|
@@ -84,7 +84,7 @@ function openModal(i) {
|
|
|
84
84
|
document.getElementById('modal-title').textContent = item.name;
|
|
85
85
|
document.getElementById('modal-body').innerHTML =
|
|
86
86
|
'<div style="margin-bottom:12px"><button class="pr-pager-btn" style="font-size:10px;padding:3px 10px" onclick="promoteToKB(\'' + escHtml(item.name) + '\')">Add to Knowledge Base</button></div>' +
|
|
87
|
-
'<
|
|
87
|
+
'<div style="font-size:12px;line-height:1.7;color:var(--muted)">' + renderMd(item.content) + '</div>';
|
|
88
88
|
_modalDocContext = { title: item.name, content: item.content, selection: '' };
|
|
89
89
|
_modalFilePath = 'notes/inbox/' + item.name; showModalQa();
|
|
90
90
|
// Clear notification badge when opening this document
|
package/package.json
CHANGED