@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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.95 (2026-04-01)
4
+
5
+ ### Dashboard
6
+ - dashboard/js/render-plans.js
7
+
8
+ ## 0.1.94 (2026-04-01)
9
+
10
+ ### Dashboard
11
+ - dashboard/js/render-prs.js
12
+
3
13
  ## 0.1.93 (2026-04-01)
4
14
 
5
15
  ### Dashboard
@@ -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
- // For .md plans: any work item with a sourcePlan .json means the plan is being executed
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 (!confirm('Archive PRD "' + file + '"? Work items will be preserved.')) return;
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
- if (res.ok) {
527
- closeModal();
528
- showToast('cmd-toast', 'PRD archived', true);
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
- const d = await res.json();
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
- 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 };
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 = 3;
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
- '<pre style="white-space:pre-wrap;word-wrap:break-word;margin:0;font-family:Consolas,monospace;font-size:12px;line-height:1.7;color:var(--muted)">' + escHtml(item.content) + '</pre>';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.93",
3
+ "version": "0.1.95",
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"