@yemi33/minions 0.1.839 → 0.1.840

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,8 +1,9 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.839 (2026-04-11)
3
+ ## 0.1.840 (2026-04-11)
4
4
 
5
5
  ### Features
6
+ - optimistic UI updates for all dashboard actions
6
7
  - per-item Re-open button on done PRD items (deterministic fallback)
7
8
  - stale PRD shows Regenerate PRD + Resume as-is buttons
8
9
 
@@ -558,16 +558,16 @@ async function planView(file) {
558
558
 
559
559
  async function planApprove(file, btn) {
560
560
  if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Approving...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
561
+ showToast('cmd-toast', 'Plan approved — work will begin on next engine tick', true);
561
562
  try {
562
563
  const res = await fetch('/api/plans/approve', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file }) });
563
564
  if (res.ok) {
564
- showToast('cmd-toast', 'Plan approved — work will begin on next engine tick', true);
565
565
  refreshPlans();
566
566
  refresh();
567
567
  } else {
568
568
  if (btn) { btn.textContent = btn.dataset.origText || 'Approve'; btn.style.pointerEvents = ''; btn.style.opacity = ''; }
569
569
  const d = await res.json().catch(() => ({}));
570
- alert('Approve failed: ' + (d.error || 'unknown'));
570
+ showToast('cmd-toast', 'Approve failed: ' + (d.error || 'unknown'), false);
571
571
  }
572
572
  } catch (e) { if (btn) { btn.textContent = btn.dataset.origText || 'Approve'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } showToast('cmd-toast', 'Error: ' + e.message, false); }
573
573
  }
@@ -626,16 +626,16 @@ async function planArchive(file, btn) {
626
626
 
627
627
  async function planPause(file, btn) {
628
628
  if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Pausing...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
629
+ showToast('cmd-toast', 'Plan paused — no new items will be dispatched', true);
629
630
  try {
630
631
  const res = await fetch('/api/plans/pause', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file }) });
631
632
  if (res.ok) {
632
- showToast('cmd-toast', 'Plan paused — no new items will be dispatched', true);
633
633
  refreshPlans();
634
634
  refresh();
635
635
  } else {
636
636
  if (btn) { btn.textContent = btn.dataset.origText || 'Pause'; btn.style.pointerEvents = ''; btn.style.opacity = ''; }
637
637
  const d = await res.json().catch(() => ({}));
638
- alert('Pause failed: ' + (d.error || 'unknown'));
638
+ showToast('cmd-toast', 'Pause failed: ' + (d.error || 'unknown'), false);
639
639
  }
640
640
  } catch (e) { if (btn) { btn.textContent = btn.dataset.origText || 'Pause'; btn.style.pointerEvents = ''; btn.style.opacity = ''; } showToast('cmd-toast', 'Error: ' + e.message, false); }
641
641
  }
@@ -643,14 +643,14 @@ async function planPause(file, btn) {
643
643
  async function planReject(file) {
644
644
  if (!confirm('Reject this plan? It will not be executed.')) return;
645
645
  const reason = prompt('Reason for rejection (optional):') || '';
646
+ showToast('cmd-toast', 'Plan rejected', true);
646
647
  try {
647
648
  const res = await fetch('/api/plans/reject', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file, reason }) });
648
649
  if (res.ok) {
649
- showToast('cmd-toast', 'Plan rejected', true);
650
650
  refreshPlans();
651
651
  } else {
652
652
  const d = await res.json().catch(() => ({}));
653
- alert('Reject failed: ' + (d.error || 'unknown'));
653
+ showToast('cmd-toast', 'Reject failed: ' + (d.error || 'unknown'), false);
654
654
  }
655
655
  } catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); }
656
656
  }
@@ -762,6 +762,7 @@ async function openVerifyGuide(file) {
762
762
 
763
763
  async function triggerVerify(file, btn) {
764
764
  if (btn) { btn.dataset.origText = btn.textContent; btn.textContent = 'Verifying...'; btn.style.pointerEvents = 'none'; btn.style.opacity = '0.6'; }
765
+ showToast('cmd-toast', 'Verify task queued', true);
765
766
  try {
766
767
  const res = await fetch('/api/plans/trigger-verify', {
767
768
  method: 'POST', headers: { 'Content-Type': 'application/json' },
@@ -771,7 +772,6 @@ async function triggerVerify(file, btn) {
771
772
  if (res.ok && d.ok) {
772
773
  try { closeModal(); } catch { /* may not be open */ }
773
774
  refresh();
774
- showToast('cmd-toast', d.verifyId ? 'Verify task ' + d.verifyId + ' created' : (d.message || 'Done'), true);
775
775
  } else {
776
776
  if (btn) { btn.textContent = btn.dataset.origText || 'Verify'; btn.style.pointerEvents = ''; btn.style.opacity = ''; }
777
777
  alert('Failed: ' + (d.error || 'unknown'));
@@ -713,20 +713,22 @@ async function prdItemEdit(source, itemId) {
713
713
 
714
714
  async function prdItemSave(source, itemId) {
715
715
  var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
716
+ const body = {
717
+ source, itemId,
718
+ name: document.getElementById('prd-edit-name').value,
719
+ description: document.getElementById('prd-edit-desc').value,
720
+ priority: document.getElementById('prd-edit-priority').value,
721
+ estimated_complexity: document.getElementById('prd-edit-complexity').value,
722
+ };
723
+ closeModal(); showToast('cmd-toast', 'Item updated', true);
716
724
  try {
717
725
  const res = await fetch('/api/prd-items/update', {
718
726
  method: 'POST', headers: { 'Content-Type': 'application/json' },
719
- body: JSON.stringify({
720
- source, itemId,
721
- name: document.getElementById('prd-edit-name').value,
722
- description: document.getElementById('prd-edit-desc').value,
723
- priority: document.getElementById('prd-edit-priority').value,
724
- estimated_complexity: document.getElementById('prd-edit-complexity').value,
725
- })
727
+ body: JSON.stringify(body)
726
728
  });
727
- if (res.ok) { closeModal(); refresh(); showToast('cmd-toast', 'Item updated', true); }
728
- else { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } const d = await res.json(); alert('Failed: ' + (d.error || 'unknown')); }
729
- } catch (e) { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } alert('Error: ' + e.message); }
729
+ if (res.ok) { refresh(); }
730
+ else { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Save failed: ' + (d.error || 'unknown'), false); }
731
+ } catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); }
730
732
  }
731
733
 
732
734
  async function prdItemRemove(source, itemId) {
@@ -775,19 +777,17 @@ async function prdItemRequeue(workItemId, source, prdFile) {
775
777
 
776
778
  async function prdItemReopen(source, itemId) {
777
779
  if (!confirm('Re-open this item? It will be set to "updated" and the engine will re-dispatch it on the existing branch.')) return;
780
+ showToast('cmd-toast', 'Item re-opened — will dispatch on next tick', true);
778
781
  try {
779
- // Set item to "updated" + clear stale flag + approve so materializer picks it up
780
782
  const res = await fetch('/api/prd-items/update', {
781
783
  method: 'POST', headers: { 'Content-Type': 'application/json' },
782
784
  body: JSON.stringify({ source, itemId, status: 'updated' })
783
785
  });
784
786
  if (res.ok) {
785
- // Also approve the plan (clears planStale so materializer runs)
786
787
  await fetch('/api/plans/approve', {
787
788
  method: 'POST', headers: { 'Content-Type': 'application/json' },
788
789
  body: JSON.stringify({ file: source, skipRegen: true })
789
790
  });
790
- showToast('cmd-toast', 'Item re-opened — will dispatch on next tick', true);
791
791
  refresh();
792
792
  } else {
793
793
  const d = await res.json().catch(() => ({}));
@@ -798,19 +798,19 @@ async function prdItemReopen(source, itemId) {
798
798
 
799
799
  async function _planApproveAction(prdFile, skipRegen, confirmMsg, successMsg) {
800
800
  if (!confirm(confirmMsg)) return;
801
+ showToast('cmd-toast', successMsg || (skipRegen ? 'Plan resumed' : 'PRD regeneration queued'), true);
801
802
  try {
802
803
  const res = await fetch('/api/plans/approve', {
803
804
  method: 'POST', headers: { 'Content-Type': 'application/json' },
804
805
  body: JSON.stringify({ file: prdFile, skipRegen: skipRegen || undefined })
805
806
  });
806
- const d = await res.json();
807
807
  if (res.ok) {
808
- showToast('cmd-toast', successMsg || (d.diffAwareUpdate ? 'Diff-aware PRD update queued' : 'Plan approved'), true);
809
808
  refresh();
810
809
  } else {
811
- alert('Failed: ' + (d.error || 'unknown'));
810
+ const d = await res.json().catch(() => ({}));
811
+ showToast('cmd-toast', 'Failed: ' + (d.error || 'unknown'), false);
812
812
  }
813
- } catch (e) { alert('Error: ' + e.message); }
813
+ } catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); }
814
814
  }
815
815
 
816
816
  function prdRegenerate(prdFile) {
@@ -198,13 +198,14 @@ async function deleteWorkItem(id, source) {
198
198
  markDeleted('wi:' + id);
199
199
  var wiTable = document.getElementById('work-items-content');
200
200
  (wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
201
+ showToast('cmd-toast', 'Work item deleted', true);
201
202
  try {
202
203
  const res = await fetch('/api/work-items/delete', {
203
204
  method: 'POST', headers: { 'Content-Type': 'application/json' },
204
205
  body: JSON.stringify({ id, source: source || undefined })
205
206
  });
206
- if (!res.ok) { const d = await res.json().catch(() => ({})); alert('Delete failed: ' + (d.error || 'unknown')); refresh(); }
207
- } catch (e) { alert('Delete error: ' + e.message); refresh(); }
207
+ if (!res.ok) { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false); refresh(); }
208
+ } catch (e) { showToast('cmd-toast', 'Delete error: ' + e.message, false); refresh(); }
208
209
  }
209
210
 
210
211
  async function archiveWorkItem(id, source) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.839",
3
+ "version": "0.1.840",
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"