@yemi33/minions 0.1.1562 → 0.1.1563
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 +4 -1
- package/dashboard/js/render-inbox.js +6 -6
- package/dashboard/js/render-meetings.js +6 -6
- package/dashboard/js/render-other.js +5 -2
- package/dashboard/js/render-pinned.js +3 -3
- package/dashboard/js/render-pipelines.js +6 -4
- package/dashboard/js/render-plans.js +11 -6
- package/dashboard/js/render-prd.js +11 -5
- package/dashboard/js/render-prs.js +3 -4
- package/dashboard/js/render-schedules.js +3 -3
- package/dashboard/js/render-watches.js +14 -5
- package/dashboard/js/render-work-items.js +9 -9
- package/dashboard/js/settings.js +23 -1
- package/dashboard/js/utils.js +5 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -158,17 +158,17 @@ function modalCancelEdit() {
|
|
|
158
158
|
|
|
159
159
|
async function deleteInboxItem(name) {
|
|
160
160
|
if (!confirm('Delete "' + name + '" from inbox?')) return;
|
|
161
|
+
showToast('cmd-toast', 'Inbox item deleted', true);
|
|
161
162
|
markDeleted('inbox:' + name);
|
|
162
163
|
const card = document.querySelector('.inbox-item[data-file="notes/inbox/' + CSS.escape(name) + '"]');
|
|
163
164
|
if (card) card.remove();
|
|
164
|
-
showToast('cmd-toast', 'Inbox item deleted', true);
|
|
165
165
|
try {
|
|
166
166
|
const res = await fetch('/api/inbox/delete', {
|
|
167
167
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
168
168
|
body: JSON.stringify({ name })
|
|
169
169
|
});
|
|
170
|
-
if (!res.ok) { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
171
|
-
} catch (e) { showToast('cmd-toast', 'Delete error: ' + e.message, false); refresh(); }
|
|
170
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); clearDeleted('inbox:' + name); showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
171
|
+
} catch (e) { clearDeleted('inbox:' + name); showToast('cmd-toast', 'Delete error: ' + e.message, false); refresh(); }
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
async function openInboxInExplorer(name) {
|
|
@@ -223,11 +223,11 @@ async function submitQuickNote() {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async function doPromoteToKB(name, category) {
|
|
226
|
+
showToast('cmd-toast', 'Promoted to Knowledge Base', true);
|
|
226
227
|
try { closeModal(); } catch { /* expected */ }
|
|
227
228
|
markDeleted('inbox:' + name);
|
|
228
229
|
const card = document.querySelector('.inbox-item[data-file="notes/inbox/' + CSS.escape(name) + '"]');
|
|
229
230
|
if (card) card.remove();
|
|
230
|
-
showToast('cmd-toast', 'Promoted to Knowledge Base', true);
|
|
231
231
|
try {
|
|
232
232
|
const res = await fetch('/api/inbox/promote-kb', {
|
|
233
233
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -235,8 +235,8 @@ async function doPromoteToKB(name, category) {
|
|
|
235
235
|
});
|
|
236
236
|
const data = await res.json();
|
|
237
237
|
if (res.ok) { refreshKnowledgeBase(); }
|
|
238
|
-
else { showToast('cmd-toast', 'Promote failed: ' + (data.error || 'unknown'), false); refresh(); }
|
|
239
|
-
} catch (e) { showToast('cmd-toast', 'Promote error: ' + e.message, false); refresh(); }
|
|
238
|
+
else { clearDeleted('inbox:' + name); showToast('cmd-toast', 'Promote failed: ' + (data.error || 'unknown'), false); refresh(); }
|
|
239
|
+
} catch (e) { clearDeleted('inbox:' + name); showToast('cmd-toast', 'Promote error: ' + e.message, false); refresh(); }
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
window.MinionsInbox = { renderInbox, promoteToKB, renderNotes, openNotesModal, modalToggleEdit, modalSaveEdit, modalCancelEdit, deleteInboxItem, openInboxInExplorer, openQuickNoteModal, submitQuickNote, doPromoteToKB };
|
|
@@ -337,17 +337,17 @@ async function _endMeeting(id, btn) {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
async function _archiveMeeting(id) {
|
|
340
|
+
showToast('cmd-toast', 'Meeting archived', true);
|
|
340
341
|
markDeleted('mtg:' + id);
|
|
341
342
|
try { closeModal(); } catch { /* may not be open */ }
|
|
342
343
|
document.querySelectorAll('[onclick*="openMeetingDetail(\'' + id + '\')"]').forEach(function(el) { el.remove(); });
|
|
343
|
-
showToast('cmd-toast', 'Meeting archived', true);
|
|
344
344
|
try {
|
|
345
345
|
const res = await fetch('/api/meetings/archive', {
|
|
346
346
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
347
347
|
body: JSON.stringify({ id })
|
|
348
348
|
});
|
|
349
|
-
if (!res.ok) {
|
|
350
|
-
} catch (e) {
|
|
349
|
+
if (!res.ok) { clearDeleted('mtg:' + id); const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
350
|
+
} catch (e) { clearDeleted('mtg:' + id); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
async function _unarchiveMeeting(id) {
|
|
@@ -480,17 +480,17 @@ async function _createPlanFromMeeting(id, btn) {
|
|
|
480
480
|
|
|
481
481
|
async function _deleteMeeting(id) {
|
|
482
482
|
if (!confirm('Delete this meeting? This cannot be undone.')) return;
|
|
483
|
+
showToast('cmd-toast', 'Meeting deleted', true);
|
|
483
484
|
markDeleted('mtg:' + id);
|
|
484
485
|
try { closeModal(); } catch { /* may not be open */ }
|
|
485
486
|
document.querySelectorAll('[onclick*="openMeetingDetail(\'' + id + '\')"]').forEach(function(el) { el.remove(); });
|
|
486
|
-
showToast('cmd-toast', 'Meeting deleted', true);
|
|
487
487
|
try {
|
|
488
488
|
const res = await fetch('/api/meetings/delete', {
|
|
489
489
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
490
490
|
body: JSON.stringify({ id })
|
|
491
491
|
});
|
|
492
|
-
if (!res.ok) { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
493
|
-
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
492
|
+
if (!res.ok) { clearDeleted('mtg:' + id); const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
493
|
+
} catch (e) { clearDeleted('mtg:' + id); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
494
494
|
}
|
|
495
495
|
|
|
496
496
|
window.MinionsMeetings = { renderMeetings, openMeetingDetail, openCreateMeetingModal };
|
|
@@ -23,12 +23,11 @@ function renderProjects(projects) {
|
|
|
23
23
|
|
|
24
24
|
async function projectChipRemove(name) {
|
|
25
25
|
if (!confirm('Remove project "' + name + '"? Pending work cancels, active agents are killed, data dir is archived to projects/.archived/.')) return;
|
|
26
|
+
showToast('cmd-toast', 'Removing project "' + name + '"...', true);
|
|
26
27
|
markDeleted('project:' + name);
|
|
27
|
-
// Re-render immediately from cached state so the chip disappears without waiting on the API
|
|
28
28
|
if (window._lastStatus && Array.isArray(window._lastStatus.projects)) {
|
|
29
29
|
renderProjects(window._lastStatus.projects);
|
|
30
30
|
}
|
|
31
|
-
showToast('cmd-toast', 'Removing project "' + name + '"...', true);
|
|
32
31
|
try {
|
|
33
32
|
const res = await fetch('/api/projects/remove', {
|
|
34
33
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -37,6 +36,8 @@ async function projectChipRemove(name) {
|
|
|
37
36
|
const d = await res.json().catch(() => ({}));
|
|
38
37
|
if (!res.ok || !d.ok) {
|
|
39
38
|
// Revert: refresh restores the chip from server state
|
|
39
|
+
clearDeleted('project:' + name);
|
|
40
|
+
if (window._lastStatus && Array.isArray(window._lastStatus.projects)) renderProjects(window._lastStatus.projects);
|
|
40
41
|
showToast('cmd-toast', 'Remove failed: ' + (d.error || 'unknown'), false);
|
|
41
42
|
if (typeof refresh === 'function') refresh();
|
|
42
43
|
return;
|
|
@@ -50,6 +51,8 @@ async function projectChipRemove(name) {
|
|
|
50
51
|
showToast('cmd-toast', parts.join(' — '), true);
|
|
51
52
|
if (typeof refresh === 'function') refresh();
|
|
52
53
|
} catch (e) {
|
|
54
|
+
clearDeleted('project:' + name);
|
|
55
|
+
if (window._lastStatus && Array.isArray(window._lastStatus.projects)) renderProjects(window._lastStatus.projects);
|
|
53
56
|
showToast('cmd-toast', 'Error: ' + e.message, false);
|
|
54
57
|
if (typeof refresh === 'function') refresh();
|
|
55
58
|
}
|
|
@@ -77,13 +77,13 @@ async function submitPinnedNote(e) {
|
|
|
77
77
|
|
|
78
78
|
async function removePinnedNote(title) {
|
|
79
79
|
if (!confirm('Unpin "' + title + '"?')) return;
|
|
80
|
+
showToast('cmd-toast', 'Note unpinned', true);
|
|
80
81
|
markDeleted('pin:' + title);
|
|
81
82
|
const btn = (window.event)?.target; if (btn) { const card = btn.closest('.pinned-card') || btn.parentElement?.parentElement; if (card) card.remove(); }
|
|
82
|
-
showToast('cmd-toast', 'Note unpinned', true);
|
|
83
83
|
try {
|
|
84
84
|
const res = await fetch('/api/pinned/remove', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title }) });
|
|
85
|
-
if (!res.ok) { showToast('cmd-toast', 'Unpin failed', false); refresh(); }
|
|
86
|
-
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
85
|
+
if (!res.ok) { clearDeleted('pin:' + title); showToast('cmd-toast', 'Unpin failed', false); refresh(); }
|
|
86
|
+
} catch (e) { clearDeleted('pin:' + title); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function openPinnedView(idx) {
|
|
@@ -248,7 +248,8 @@ function _buildNodeChain(stages, run, options) {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
function renderPipelines(pipelines) {
|
|
251
|
-
|
|
251
|
+
pipelines = (pipelines || []).filter(function(p) { return !isDeleted('pipeline:' + p.id); });
|
|
252
|
+
_pipelinesData = pipelines;
|
|
252
253
|
const el = document.getElementById('pipelines-content');
|
|
253
254
|
const countEl = document.getElementById('pipelines-count');
|
|
254
255
|
if (!el) return;
|
|
@@ -515,14 +516,15 @@ async function _continuePipeline(id, stageId, btn) {
|
|
|
515
516
|
|
|
516
517
|
async function _deletePipelineConfirm(id) {
|
|
517
518
|
if (!confirm('Delete pipeline "' + id + '"?')) return;
|
|
519
|
+
showToast('cmd-toast', 'Pipeline deleted', true);
|
|
518
520
|
markDeleted('pipeline:' + id);
|
|
519
521
|
try { closeModal(); } catch {}
|
|
520
|
-
|
|
522
|
+
renderPipelines(_pipelinesData);
|
|
521
523
|
try {
|
|
522
524
|
var res = await fetch('/api/pipelines/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: id }) });
|
|
523
|
-
if (!res.ok) { showToast('cmd-toast', 'Delete failed', false); }
|
|
525
|
+
if (!res.ok) { clearDeleted('pipeline:' + id); showToast('cmd-toast', 'Delete failed', false); }
|
|
524
526
|
refresh();
|
|
525
|
-
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
527
|
+
} catch (e) { clearDeleted('pipeline:' + id); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
526
528
|
}
|
|
527
529
|
|
|
528
530
|
function openCreatePipelineModal() {
|
|
@@ -575,9 +575,11 @@ async function planApprove(file, btn) {
|
|
|
575
575
|
async function planDelete(file) {
|
|
576
576
|
_stopPlanPoll();
|
|
577
577
|
if (!confirm('Delete plan "' + file + '"? This cannot be undone.')) return;
|
|
578
|
-
markDeleted('plan:' + file);
|
|
579
|
-
closeModal();
|
|
580
578
|
showToast('cmd-toast', 'Plan deleted', true);
|
|
579
|
+
markDeleted('plan:' + file);
|
|
580
|
+
try { closeModal(); } catch { /* may not be open */ }
|
|
581
|
+
if (window._lastPlans) renderPlans(window._lastPlans);
|
|
582
|
+
if (typeof rerenderPrdFromCache === 'function') rerenderPrdFromCache();
|
|
581
583
|
try {
|
|
582
584
|
const res = await fetch('/api/plans/delete', {
|
|
583
585
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -587,10 +589,11 @@ async function planDelete(file) {
|
|
|
587
589
|
refresh();
|
|
588
590
|
} else {
|
|
589
591
|
const d = await res.json().catch(() => ({}));
|
|
590
|
-
|
|
592
|
+
clearDeleted('plan:' + file);
|
|
593
|
+
showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false);
|
|
591
594
|
refresh(); // revert optimistic
|
|
592
595
|
}
|
|
593
|
-
} catch (e) {
|
|
596
|
+
} catch (e) { clearDeleted('plan:' + file); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
594
597
|
}
|
|
595
598
|
|
|
596
599
|
async function planArchive(file, btn) {
|
|
@@ -600,6 +603,7 @@ async function planArchive(file, btn) {
|
|
|
600
603
|
: 'Archive this plan?';
|
|
601
604
|
if (!confirm(confirmMsg)) return;
|
|
602
605
|
_stopPlanPoll();
|
|
606
|
+
showToast('cmd-toast', isPrd ? 'Archiving PRD and linked source plan...' : 'Archiving plan...', true);
|
|
603
607
|
markDeleted('plan:' + file);
|
|
604
608
|
if (isPrd) {
|
|
605
609
|
var prdRecord = (window._lastPlans || []).find(function(p) { return p.file === file && p.sourcePlan; });
|
|
@@ -608,7 +612,6 @@ async function planArchive(file, btn) {
|
|
|
608
612
|
if (window._lastPlans) renderPlans(window._lastPlans);
|
|
609
613
|
if (typeof rerenderPrdFromCache === 'function') rerenderPrdFromCache();
|
|
610
614
|
try { closeModal(); } catch { /* may not be open */ }
|
|
611
|
-
showToast('cmd-toast', isPrd ? 'Archiving PRD and linked source plan...' : 'Archiving plan...', true);
|
|
612
615
|
try {
|
|
613
616
|
const res = await fetch('/api/plans/archive', {
|
|
614
617
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -626,10 +629,12 @@ async function planArchive(file, btn) {
|
|
|
626
629
|
}
|
|
627
630
|
refresh();
|
|
628
631
|
} else {
|
|
632
|
+
clearDeleted('plan:' + file);
|
|
633
|
+
if (prdRecord) clearDeleted('plan:' + prdRecord.sourcePlan);
|
|
629
634
|
showToast('cmd-toast', 'Archive failed: ' + (d.error || 'unknown'), false);
|
|
630
635
|
refresh();
|
|
631
636
|
}
|
|
632
|
-
} catch (e) { showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
637
|
+
} catch (e) { clearDeleted('plan:' + file); if (prdRecord) clearDeleted('plan:' + prdRecord.sourcePlan); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
633
638
|
}
|
|
634
639
|
|
|
635
640
|
async function planPause(file, btn) {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// render-prd.js — PRD-related rendering functions extracted from dashboard.html
|
|
2
2
|
|
|
3
|
+
function _prdItemDeleteKey(source, itemId) {
|
|
4
|
+
return 'prd-item:' + (source || '') + ':' + (itemId || '');
|
|
5
|
+
}
|
|
6
|
+
|
|
3
7
|
function renderPrd(prd, prog) {
|
|
4
8
|
const section = document.getElementById('prd-content');
|
|
5
9
|
const badge = document.getElementById('prd-badge');
|
|
@@ -19,7 +23,7 @@ function renderPrd(prd, prog) {
|
|
|
19
23
|
// Show per-PRD status summary in header when multiple PRDs exist
|
|
20
24
|
const existing = (prd.existing || []).filter(function(p) { return !isDeleted('plan:' + (p.file || '')); });
|
|
21
25
|
const allWi = window._lastWorkItems || [];
|
|
22
|
-
const prdItems = (prog?.items || []).filter(i => !i._archived && !isDeleted('plan:' + (i.source || '')));
|
|
26
|
+
const prdItems = (prog?.items || []).filter(i => !i._archived && !isDeleted('plan:' + (i.source || '')) && !isDeleted(_prdItemDeleteKey(i.source, i.id)));
|
|
23
27
|
|
|
24
28
|
if (existing.length === 0 && prdItems.length === 0) {
|
|
25
29
|
section.innerHTML = '<p class="prd-pending" style="margin-bottom:0">No PRD found.</p>';
|
|
@@ -84,7 +88,7 @@ function renderPrdProgress(prog) {
|
|
|
84
88
|
const el = document.getElementById('prd-progress-content');
|
|
85
89
|
const countEl = document.getElementById('prd-progress-count');
|
|
86
90
|
if (!prog) { el.innerHTML = ''; countEl.textContent = '—'; return; }
|
|
87
|
-
const visibleItems = (prog.items || []).filter(i => !isDeleted('plan:' + (i.source || '')));
|
|
91
|
+
const visibleItems = (prog.items || []).filter(i => !isDeleted('plan:' + (i.source || '')) && !isDeleted(_prdItemDeleteKey(i.source, i.id)));
|
|
88
92
|
|
|
89
93
|
// Compute overall progress from active (non-archived) items
|
|
90
94
|
const activeItems = visibleItems.filter(i => !i._archived);
|
|
@@ -748,16 +752,18 @@ async function prdItemSave(source, itemId) {
|
|
|
748
752
|
|
|
749
753
|
async function prdItemRemove(source, itemId) {
|
|
750
754
|
if (!confirm('Remove item ' + itemId + '? This also cancels any pending work item.')) return;
|
|
751
|
-
closeModal();
|
|
752
755
|
showToast('cmd-toast', 'Item removed', true);
|
|
756
|
+
markDeleted(_prdItemDeleteKey(source, itemId));
|
|
757
|
+
closeModal();
|
|
758
|
+
if (typeof rerenderPrdFromCache === 'function') rerenderPrdFromCache();
|
|
753
759
|
try {
|
|
754
760
|
const res = await fetch('/api/prd-items/remove', {
|
|
755
761
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
756
762
|
body: JSON.stringify({ source, itemId })
|
|
757
763
|
});
|
|
758
764
|
if (res.ok) { refresh(); }
|
|
759
|
-
else { const d = await res.json().catch(() => ({}));
|
|
760
|
-
} catch (e) {
|
|
765
|
+
else { const d = await res.json().catch(() => ({})); clearDeleted(_prdItemDeleteKey(source, itemId)); showToast('cmd-toast', 'Remove failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
766
|
+
} catch (e) { clearDeleted(_prdItemDeleteKey(source, itemId)); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
761
767
|
}
|
|
762
768
|
|
|
763
769
|
async function prdItemRequeue(workItemId, source, prdFile) {
|
|
@@ -154,18 +154,17 @@ async function _submitLinkPr() {
|
|
|
154
154
|
|
|
155
155
|
async function unlinkPr(id) {
|
|
156
156
|
if (!confirm('Remove ' + id + ' from tracking?')) return;
|
|
157
|
-
|
|
157
|
+
showToast('cmd-toast', id + ' removed', true);
|
|
158
158
|
markDeleted('pr:' + id);
|
|
159
159
|
const row = document.querySelector('[data-pr-id="' + id + '"]')?.closest('tr');
|
|
160
160
|
if (row) row.remove();
|
|
161
|
-
showToast('cmd-toast', id + ' removed', true);
|
|
162
161
|
try {
|
|
163
162
|
const res = await fetch('/api/pull-requests/delete', {
|
|
164
163
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
165
164
|
body: JSON.stringify({ id })
|
|
166
165
|
});
|
|
167
|
-
if (!res.ok) {
|
|
168
|
-
} catch (e) {
|
|
166
|
+
if (!res.ok) { clearDeleted('pr:' + id); const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Failed: ' + (d.error || 'unknown'), false); refresh(); return; }
|
|
167
|
+
} catch (e) { clearDeleted('pr:' + id); showToast('cmd-toast', 'Error: ' + e.message, false); refresh(); }
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
window.MinionsPrs = { prRow, prTableHtml, renderPrs, prPrev, prNext, openAllPrs, openModal, openAddPrModal, unlinkPr };
|
|
@@ -540,16 +540,16 @@ async function toggleScheduleEnabled(id, enabled) {
|
|
|
540
540
|
|
|
541
541
|
async function deleteSchedule(id) {
|
|
542
542
|
if (!confirm('Delete scheduled task "' + id + '"?')) return;
|
|
543
|
+
showToast('cmd-toast', 'Schedule deleted', true);
|
|
543
544
|
markDeleted('sched:' + id);
|
|
544
545
|
document.querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
545
|
-
showToast('cmd-toast', 'Schedule deleted', true);
|
|
546
546
|
try {
|
|
547
547
|
const res = await fetch('/api/schedules/delete', {
|
|
548
548
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
549
549
|
body: JSON.stringify({ id })
|
|
550
550
|
});
|
|
551
|
-
if (!res.ok) { const d = await res.json().catch(() => ({})); _showScheduleError('Delete failed: ' + (d.error || 'unknown')); refresh(); }
|
|
552
|
-
} catch (e) { _showScheduleError('Delete error: ' + e.message); refresh(); }
|
|
551
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); clearDeleted('sched:' + id); _showScheduleError('Delete failed: ' + (d.error || 'unknown')); refresh(); }
|
|
552
|
+
} catch (e) { clearDeleted('sched:' + id); _showScheduleError('Delete error: ' + e.message); refresh(); }
|
|
553
553
|
}
|
|
554
554
|
|
|
555
555
|
// Expose _generateScheduleId globally for the inline oninput handler
|
|
@@ -193,17 +193,26 @@ function toggleWatchPause(id, pause) {
|
|
|
193
193
|
|
|
194
194
|
function deleteWatch(id) {
|
|
195
195
|
if (!confirm('Delete this watch?')) return;
|
|
196
|
-
markDeleted('watch:' + id);
|
|
197
196
|
showToast('cmd-toast', 'Deleting watch...', true);
|
|
197
|
+
markDeleted('watch:' + id);
|
|
198
|
+
renderWatches(window._lastWatches || []);
|
|
198
199
|
fetch('/api/watches/delete', {
|
|
199
200
|
method: 'POST',
|
|
200
201
|
headers: { 'Content-Type': 'application/json' },
|
|
201
202
|
body: JSON.stringify({ id: id })
|
|
202
|
-
}).then(function(res) {
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
}).then(async function(res) {
|
|
204
|
+
const data = await res.json().catch(() => ({}));
|
|
205
|
+
if (!res.ok || data.error) {
|
|
206
|
+
clearDeleted('watch:' + id);
|
|
207
|
+
showToast('cmd-toast', 'Delete failed: ' + (data.error || 'unknown'), false);
|
|
208
|
+
if (typeof refresh === 'function') refresh();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
renderWatches(window._lastWatches || []);
|
|
205
212
|
}).catch(function(err) {
|
|
206
|
-
|
|
213
|
+
clearDeleted('watch:' + id);
|
|
214
|
+
showToast('cmd-toast', 'Delete error: ' + err.message, false);
|
|
215
|
+
if (typeof refresh === 'function') refresh();
|
|
207
216
|
});
|
|
208
217
|
}
|
|
209
218
|
|
|
@@ -197,46 +197,46 @@ async function submitWorkItemEdit(id, source, e) {
|
|
|
197
197
|
|
|
198
198
|
async function deleteWorkItem(id, source) {
|
|
199
199
|
if (!confirm('Delete work item ' + id + '? This will kill any running agent and remove all dispatch history.')) return;
|
|
200
|
+
showToast('cmd-toast', 'Work item deleted', true);
|
|
200
201
|
markDeleted('wi:' + id);
|
|
201
202
|
var wiTable = document.getElementById('work-items-content');
|
|
202
203
|
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
203
|
-
showToast('cmd-toast', 'Work item deleted', true);
|
|
204
204
|
try {
|
|
205
205
|
const res = await fetch('/api/work-items/delete', {
|
|
206
206
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
207
207
|
body: JSON.stringify({ id, source: source || undefined })
|
|
208
208
|
});
|
|
209
|
-
if (!res.ok) { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
210
|
-
} catch (e) { showToast('cmd-toast', 'Delete error: ' + e.message, false); refresh(); }
|
|
209
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); clearDeleted('wi:' + id); showToast('cmd-toast', 'Delete failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
210
|
+
} catch (e) { clearDeleted('wi:' + id); showToast('cmd-toast', 'Delete error: ' + e.message, false); refresh(); }
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
async function cancelWorkItem(id, source) {
|
|
214
214
|
if (!confirm('Cancel work item ' + id + '? This will kill any running agent and mark it cancelled.')) return;
|
|
215
|
+
showToast('cmd-toast', 'Work item cancelled', true);
|
|
215
216
|
markDeleted('wi:' + id);
|
|
216
217
|
var wiTable = document.getElementById('work-items-content');
|
|
217
218
|
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
218
|
-
showToast('cmd-toast', 'Work item cancelled', true);
|
|
219
219
|
try {
|
|
220
220
|
const res = await fetch('/api/work-items/cancel', {
|
|
221
221
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
222
222
|
body: JSON.stringify({ id, source: source || undefined })
|
|
223
223
|
});
|
|
224
|
-
if (!res.ok) { const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'Cancel failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
225
|
-
} catch (e) { showToast('cmd-toast', 'Cancel error: ' + e.message, false); refresh(); }
|
|
224
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); clearDeleted('wi:' + id); showToast('cmd-toast', 'Cancel failed: ' + (d.error || 'unknown'), false); refresh(); }
|
|
225
|
+
} catch (e) { clearDeleted('wi:' + id); showToast('cmd-toast', 'Cancel error: ' + e.message, false); refresh(); }
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
async function archiveWorkItem(id, source) {
|
|
229
|
+
showToast('cmd-toast', 'Archived ' + id, true);
|
|
229
230
|
markDeleted('wi:' + id);
|
|
230
231
|
var wiTable = document.getElementById('work-items-content');
|
|
231
232
|
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
232
|
-
showToast('cmd-toast', 'Archived ' + id, true);
|
|
233
233
|
try {
|
|
234
234
|
const res = await fetch('/api/work-items/archive', {
|
|
235
235
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
236
236
|
body: JSON.stringify({ id, source: source || undefined })
|
|
237
237
|
});
|
|
238
|
-
if (!res.ok) { const d = await res.json().catch(() => ({}));
|
|
239
|
-
} catch (e) {
|
|
238
|
+
if (!res.ok) { const d = await res.json().catch(() => ({})); clearDeleted('wi:' + id); showToast('cmd-toast', 'Archive failed: ' + (d.error || 'unknown'), false); refresh(); return; }
|
|
239
|
+
} catch (e) { clearDeleted('wi:' + id); showToast('cmd-toast', 'Archive error: ' + e.message, false); refresh(); }
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
let wiArchiveVisible = false;
|
package/dashboard/js/settings.js
CHANGED
|
@@ -86,7 +86,7 @@ async function openSettings() {
|
|
|
86
86
|
'<h3 style="font-size:13px;color:var(--blue);margin-bottom:8px">Projects</h3>' +
|
|
87
87
|
'<div style="display:flex;flex-direction:column;gap:12px;margin-bottom:16px">' +
|
|
88
88
|
(data.projects || []).map(function(p) {
|
|
89
|
-
return '<div style="border:1px solid var(--border);border-radius:6px;padding:10px 12px">' +
|
|
89
|
+
return '<div data-settings-project="' + escHtml(p.name) + '" style="border:1px solid var(--border);border-radius:6px;padding:10px 12px">' +
|
|
90
90
|
'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">' +
|
|
91
91
|
'<div style="font-size:12px;font-weight:600">' + escHtml(p.name) + '</div>' +
|
|
92
92
|
'<button onclick="MinionsSettings.removeProject(\'' + escHtml(p.name) + '\')" style="font-size:9px;padding:2px 8px;background:transparent;color:var(--red);border:1px solid var(--red);border-radius:3px;cursor:pointer">Remove</button>' +
|
|
@@ -409,7 +409,17 @@ async function resetSettingsToDefaults() {
|
|
|
409
409
|
|
|
410
410
|
async function removeProject(name) {
|
|
411
411
|
if (!confirm('Remove project "' + name + '"? Pending work cancels, active agents are killed, data dir is archived to projects/.archived/.')) return;
|
|
412
|
+
const prevProjects = (_settingsData && Array.isArray(_settingsData.projects)) ? _settingsData.projects.slice() : null;
|
|
412
413
|
showToast('cmd-toast', 'Removing project "' + name + '"...', true);
|
|
414
|
+
markDeleted('project:' + name);
|
|
415
|
+
if (_settingsData && Array.isArray(_settingsData.projects)) {
|
|
416
|
+
_settingsData.projects = _settingsData.projects.filter(function(p) { return p.name !== name; });
|
|
417
|
+
}
|
|
418
|
+
const card = document.querySelector('[data-settings-project="' + CSS.escape(name) + '"]');
|
|
419
|
+
if (card) card.remove();
|
|
420
|
+
if (window._lastStatus && Array.isArray(window._lastStatus.projects) && typeof renderProjects === 'function') {
|
|
421
|
+
renderProjects(window._lastStatus.projects);
|
|
422
|
+
}
|
|
413
423
|
try {
|
|
414
424
|
const res = await fetch('/api/projects/remove', {
|
|
415
425
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -417,7 +427,13 @@ async function removeProject(name) {
|
|
|
417
427
|
});
|
|
418
428
|
const d = await res.json().catch(() => ({}));
|
|
419
429
|
if (!res.ok || !d.ok) {
|
|
430
|
+
if (prevProjects) _settingsData.projects = prevProjects;
|
|
431
|
+
clearDeleted('project:' + name);
|
|
432
|
+
if (window._lastStatus && Array.isArray(window._lastStatus.projects) && typeof renderProjects === 'function') {
|
|
433
|
+
renderProjects(window._lastStatus.projects);
|
|
434
|
+
}
|
|
420
435
|
showToast('cmd-toast', 'Remove failed: ' + (d.error || 'unknown'), false);
|
|
436
|
+
openSettings();
|
|
421
437
|
return;
|
|
422
438
|
}
|
|
423
439
|
var parts = ['Removed "' + name + '"'];
|
|
@@ -430,7 +446,13 @@ async function removeProject(name) {
|
|
|
430
446
|
showToast('cmd-toast', parts.join(' — '), true);
|
|
431
447
|
setTimeout(() => openSettings(), 600);
|
|
432
448
|
} catch (e) {
|
|
449
|
+
if (prevProjects) _settingsData.projects = prevProjects;
|
|
450
|
+
clearDeleted('project:' + name);
|
|
451
|
+
if (window._lastStatus && Array.isArray(window._lastStatus.projects) && typeof renderProjects === 'function') {
|
|
452
|
+
renderProjects(window._lastStatus.projects);
|
|
453
|
+
}
|
|
433
454
|
showToast('cmd-toast', 'Error: ' + e.message, false);
|
|
455
|
+
openSettings();
|
|
434
456
|
}
|
|
435
457
|
}
|
|
436
458
|
|
package/dashboard/js/utils.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
// Signal the engine to tick immediately (pick up new work without waiting 60s)
|
|
4
4
|
function wakeEngine() { fetch('/api/engine/wakeup', { method: 'POST' }).catch(() => {}); }
|
|
5
5
|
|
|
6
|
-
// Optimistic delete suppression
|
|
7
|
-
const _deletedIds = new Map();
|
|
8
|
-
function markDeleted(key) { _deletedIds.set(key, Date.now() + 10000); }
|
|
6
|
+
// Optimistic delete suppression
|
|
7
|
+
const _deletedIds = new Map();
|
|
8
|
+
function markDeleted(key) { _deletedIds.set(key, Date.now() + 10000); }
|
|
9
|
+
function clearDeleted(key) { _deletedIds.delete(key); }
|
|
9
10
|
function isDeleted(key) { const exp = _deletedIds.get(key); if (!exp) return false; if (Date.now() > exp) { _deletedIds.delete(key); return false; } return true; }
|
|
10
11
|
|
|
11
12
|
// Pin-to-top — persisted server-side so CC and agents can also pin items
|
|
@@ -507,4 +508,4 @@ async function submitBugReport() {
|
|
|
507
508
|
}
|
|
508
509
|
}
|
|
509
510
|
|
|
510
|
-
window.MinionsUtils = { wakeEngine, escapeHtml, escHtml, renderMd, normalizePlanFile, timeAgo, statusColor, shouldIgnoreSelectionClick, llmCopyBtn, copyLlmText, openBugReport, submitBugReport };
|
|
511
|
+
window.MinionsUtils = { wakeEngine, markDeleted, clearDeleted, escapeHtml, escHtml, renderMd, normalizePlanFile, timeAgo, statusColor, shouldIgnoreSelectionClick, llmCopyBtn, copyLlmText, openBugReport, submitBugReport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1563",
|
|
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"
|