@yemi33/minions 0.1.1676 → 0.1.1678
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-agents.js +2 -2
- package/dashboard/js/render-dispatch.js +6 -11
- package/dashboard/js/render-inbox.js +2 -2
- package/dashboard/js/render-kb.js +8 -8
- package/dashboard/js/render-meetings.js +8 -8
- package/dashboard/js/render-pinned.js +2 -2
- package/dashboard/js/render-plans.js +7 -10
- package/dashboard/js/render-prs.js +5 -4
- package/dashboard/js/render-schedules.js +14 -7
- package/dashboard/js/render-watches.js +10 -6
- package/dashboard/js/render-work-items.js +9 -7
- package/engine/ado.js +1 -2
- package/engine/copilot-models.json +1 -1
- package/engine/github.js +6 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -39,7 +39,7 @@ function renderAgents(agents) {
|
|
|
39
39
|
agentData = agents;
|
|
40
40
|
const grid = document.getElementById('agents-grid');
|
|
41
41
|
grid.innerHTML = agents.map(a => `
|
|
42
|
-
<div class="agent-card ${statusColor(a.status)}" onclick="if(shouldIgnoreSelectionClick(event))return;openAgentDetail(
|
|
42
|
+
<div class="agent-card ${statusColor(a.status)}" data-agent-id="${escapeHtml(a.id)}" onclick="if(shouldIgnoreSelectionClick(event))return;openAgentDetail(this.dataset.agentId)">
|
|
43
43
|
<div class="agent-card-header">
|
|
44
44
|
<span class="agent-name"><span class="agent-emoji">${escapeHtml(a.emoji)}</span>${escapeHtml(a.name)}${_runtimeTagHtml(a.runtime)}</span>
|
|
45
45
|
<span class="status-badge ${escapeHtml(a.status)}">${escapeHtml(a.status)}</span>
|
|
@@ -116,7 +116,7 @@ async function openAgentDetail(id) {
|
|
|
116
116
|
document.getElementById('detail-content').innerHTML =
|
|
117
117
|
'<div style="padding:24px;text-align:center">' +
|
|
118
118
|
'<div style="color:var(--red);margin-bottom:12px">Error loading agent detail: ' + escapeHtml(e.message) + '</div>' +
|
|
119
|
-
'<button
|
|
119
|
+
'<button data-agent-id="' + escapeHtml(id) + '" onclick="openAgentDetail(this.dataset.agentId)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:12px">Retry</button>' +
|
|
120
120
|
' <button onclick="closeDetail()" style="padding:6px 16px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:12px;color:var(--text)">Close</button>' +
|
|
121
121
|
'</div>';
|
|
122
122
|
}
|
|
@@ -18,12 +18,7 @@ function renderEngineStatus(engine) {
|
|
|
18
18
|
// Detect stale engine — says running but heartbeat is old (>2 min)
|
|
19
19
|
if (state === 'running' && engine?.heartbeat) {
|
|
20
20
|
staleMs = Date.now() - engine.heartbeat;
|
|
21
|
-
if (staleMs > 120000)
|
|
22
|
-
state = 'stale';
|
|
23
|
-
}
|
|
24
|
-
} else if (state === 'running' && !engine?.heartbeat) {
|
|
25
|
-
// Running but no heartbeat yet — engine just started or old version
|
|
26
|
-
state = 'running';
|
|
21
|
+
if (staleMs > 120000) state = 'stale';
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
badge.className = 'engine-badge ' + (state === 'stale' ? 'stopped' : state);
|
|
@@ -172,12 +167,12 @@ function renderDispatch(dispatch) {
|
|
|
172
167
|
'</tr>';
|
|
173
168
|
}).join('') + '</tbody></table>';
|
|
174
169
|
if (completed.length > COMPLETED_PER_PAGE) {
|
|
175
|
-
completedEl.
|
|
170
|
+
completedEl.insertAdjacentHTML('beforeend', '<div class="pr-pager">' +
|
|
176
171
|
'<span class="pr-page-info">Showing ' + (compStart + 1) + ' to ' + Math.min(compStart + COMPLETED_PER_PAGE, completed.length) + ' of ' + completed.length + '</span>' +
|
|
177
172
|
'<div class="pr-pager-btns">' +
|
|
178
173
|
'<button class="pr-pager-btn ' + (_completedPage === 0 ? 'disabled' : '') + '" onclick="_completedPrev()">Prev</button>' +
|
|
179
174
|
'<button class="pr-pager-btn ' + (_completedPage >= totalCompPages - 1 ? 'disabled' : '') + '" onclick="_completedNext()">Next</button>' +
|
|
180
|
-
'</div></div>';
|
|
175
|
+
'</div></div>');
|
|
181
176
|
}
|
|
182
177
|
} else {
|
|
183
178
|
completedEl.innerHTML = '<p class="empty">No completed dispatches yet.</p>';
|
|
@@ -206,12 +201,12 @@ function renderEngineLog(log) {
|
|
|
206
201
|
'</div>'
|
|
207
202
|
).join('');
|
|
208
203
|
if (reversed.length > LOG_PER_PAGE) {
|
|
209
|
-
el.
|
|
204
|
+
el.insertAdjacentHTML('beforeend', '<div class="pr-pager">' +
|
|
210
205
|
'<span class="pr-page-info">Showing ' + (logStart + 1) + ' to ' + Math.min(logStart + LOG_PER_PAGE, reversed.length) + ' of ' + reversed.length + '</span>' +
|
|
211
206
|
'<div class="pr-pager-btns">' +
|
|
212
207
|
'<button class="pr-pager-btn ' + (_logPage === 0 ? 'disabled' : '') + '" onclick="_logPrev()">Prev</button>' +
|
|
213
208
|
'<button class="pr-pager-btn ' + (_logPage >= totalLogPages - 1 ? 'disabled' : '') + '" onclick="_logNext()">Next</button>' +
|
|
214
|
-
'</div></div>';
|
|
209
|
+
'</div></div>');
|
|
215
210
|
}
|
|
216
211
|
}
|
|
217
212
|
|
|
@@ -228,7 +223,7 @@ async function showErrorDetails(agentId, reason, task) {
|
|
|
228
223
|
document.getElementById('modal').classList.add('open');
|
|
229
224
|
|
|
230
225
|
try {
|
|
231
|
-
const output = await
|
|
226
|
+
const output = await safeFetch('/api/agent/' + agentId + '/output').then(r => r.text());
|
|
232
227
|
const lines = output.split('\n');
|
|
233
228
|
const stderrIdx = lines.findIndex(l => l.startsWith('## stderr'));
|
|
234
229
|
let summary = '';
|
|
@@ -43,12 +43,12 @@ function renderInbox(inbox) {
|
|
|
43
43
|
</div>`;
|
|
44
44
|
}).join('');
|
|
45
45
|
if (inbox.length > INBOX_PER_PAGE) {
|
|
46
|
-
list.
|
|
46
|
+
list.insertAdjacentHTML('beforeend', '<div class="pr-pager">' +
|
|
47
47
|
'<span class="pr-page-info">Showing ' + (inboxStart + 1) + ' to ' + Math.min(inboxStart + INBOX_PER_PAGE, inbox.length) + ' of ' + inbox.length + '</span>' +
|
|
48
48
|
'<div class="pr-pager-btns">' +
|
|
49
49
|
'<button class="pr-pager-btn ' + (_inboxPage === 0 ? 'disabled' : '') + '" onclick="_inboxPrev()">Prev</button>' +
|
|
50
50
|
'<button class="pr-pager-btn ' + (_inboxPage >= totalInboxPages - 1 ? 'disabled' : '') + '" onclick="_inboxNext()">Next</button>' +
|
|
51
|
-
'</div></div>';
|
|
51
|
+
'</div></div>');
|
|
52
52
|
}
|
|
53
53
|
restoreNotifBadges();
|
|
54
54
|
}
|
|
@@ -134,12 +134,12 @@ function renderKnowledgeBase() {
|
|
|
134
134
|
'</div>';
|
|
135
135
|
}).join('');
|
|
136
136
|
if (items.length > KB_PER_PAGE) {
|
|
137
|
-
listEl.
|
|
137
|
+
listEl.insertAdjacentHTML('beforeend', '<div class="pr-pager">' +
|
|
138
138
|
'<span class="pr-page-info">Showing ' + (kbStart + 1) + ' to ' + Math.min(kbStart + KB_PER_PAGE, items.length) + ' of ' + items.length + '</span>' +
|
|
139
139
|
'<div class="pr-pager-btns">' +
|
|
140
140
|
'<button class="pr-pager-btn ' + (_kbPage === 0 ? 'disabled' : '') + '" onclick="_kbPrev()">Prev</button>' +
|
|
141
141
|
'<button class="pr-pager-btn ' + (_kbPage >= totalKbPages - 1 ? 'disabled' : '') + '" onclick="_kbNext()">Next</button>' +
|
|
142
|
-
'</div></div>';
|
|
142
|
+
'</div></div>');
|
|
143
143
|
}
|
|
144
144
|
restoreNotifBadges();
|
|
145
145
|
}
|
|
@@ -242,18 +242,18 @@ function openCreateKbModal() {
|
|
|
242
242
|
'<textarea id="kb-new-content" rows="8" style="display:block;width:100%;margin-top:4px;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);resize:vertical;font-family:inherit" placeholder="Write your knowledge entry..."></textarea></label>' +
|
|
243
243
|
'<div style="display:flex;justify-content:flex-end;gap:8px">' +
|
|
244
244
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
245
|
-
'<button onclick="submitKbEntry()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
|
|
245
|
+
'<button onclick="submitKbEntry(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Save</button>' +
|
|
246
246
|
'</div>' +
|
|
247
247
|
'</div>';
|
|
248
248
|
document.getElementById('modal').classList.add('open');
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
async function submitKbEntry() {
|
|
252
|
-
var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = '
|
|
251
|
+
async function submitKbEntry(e) {
|
|
252
|
+
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; }
|
|
253
253
|
const category = document.getElementById('kb-new-category').value;
|
|
254
254
|
const title = document.getElementById('kb-new-title').value;
|
|
255
255
|
const content = document.getElementById('kb-new-content').value;
|
|
256
|
-
if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = '
|
|
256
|
+
if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } alert('Title and content are required'); return; }
|
|
257
257
|
try {
|
|
258
258
|
showToast('cmd-toast', 'KB entry created', true);
|
|
259
259
|
const res = await fetch('/api/knowledge', {
|
|
@@ -261,8 +261,8 @@ async function submitKbEntry() {
|
|
|
261
261
|
body: JSON.stringify({ category, title, content })
|
|
262
262
|
});
|
|
263
263
|
if (res.ok) { closeModal(); refreshKnowledgeBase(); }
|
|
264
|
-
else { if (btn) { btn.disabled = false; btn.textContent = '
|
|
265
|
-
} catch (
|
|
264
|
+
else { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } const d = await res.json().catch(() => ({})); showToast('cmd-toast', 'KB create failed: ' + (d.error || 'unknown'), false); }
|
|
265
|
+
} catch (err) { if (btn) { btn.disabled = false; btn.textContent = 'Save'; } showToast('cmd-toast', 'Error: ' + err.message, false); }
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
async function kbOpenItem(category, file) {
|
|
@@ -26,7 +26,7 @@ function renderMeetings(meetings) {
|
|
|
26
26
|
const visible = _showArchived ? meetings : active;
|
|
27
27
|
if (visible.length === 0) {
|
|
28
28
|
el.innerHTML = '<p class="empty">No active meetings.</p>';
|
|
29
|
-
if (archived.length) el.
|
|
29
|
+
if (archived.length) el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:10px" onclick="_toggleArchivedMeetings()">Show ' + archived.length + ' archived</button></div>');
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -66,17 +66,17 @@ function renderMeetings(meetings) {
|
|
|
66
66
|
}).join('');
|
|
67
67
|
|
|
68
68
|
if (visible.length > MTG_PER_PAGE) {
|
|
69
|
-
el.
|
|
69
|
+
el.insertAdjacentHTML('beforeend', '<div class="pr-pager">' +
|
|
70
70
|
'<span class="pr-page-info">Showing ' + (start + 1) + ' to ' + Math.min(start + MTG_PER_PAGE, visible.length) + ' of ' + visible.length + '</span>' +
|
|
71
71
|
'<div class="pr-pager-btns">' +
|
|
72
72
|
'<button class="pr-pager-btn ' + (_mtgPage === 0 ? 'disabled' : '') + '" onclick="_mtgPrev()">Prev</button>' +
|
|
73
73
|
'<button class="pr-pager-btn ' + (_mtgPage >= totalPages - 1 ? 'disabled' : '') + '" onclick="_mtgNext()">Next</button>' +
|
|
74
|
-
'</div></div>';
|
|
74
|
+
'</div></div>');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (archived.length > 0) {
|
|
78
|
-
el.
|
|
79
|
-
(_showArchived ? 'Hide' : 'Show') + ' ' + archived.length + ' archived</button></div>';
|
|
78
|
+
el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:10px" onclick="_toggleArchivedMeetings()">' +
|
|
79
|
+
(_showArchived ? 'Hide' : 'Show') + ' ' + archived.length + ' archived</button></div>');
|
|
80
80
|
}
|
|
81
81
|
restoreNotifBadges();
|
|
82
82
|
}
|
|
@@ -254,14 +254,14 @@ function openCreateMeetingModal() {
|
|
|
254
254
|
'<div style="color:var(--text);font-size:var(--text-md)">Participants<div style="display:flex;flex-direction:column;gap:4px;margin-top:4px" id="mtg-participants">' + agentOpts + '</div></div>' +
|
|
255
255
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
256
256
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
257
|
-
'<button onclick="_submitCreateMeeting()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Start Meeting</button>' +
|
|
257
|
+
'<button onclick="_submitCreateMeeting(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Start Meeting</button>' +
|
|
258
258
|
'</div>' +
|
|
259
259
|
'</div>';
|
|
260
260
|
document.getElementById('modal').classList.add('open');
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
async function _submitCreateMeeting() {
|
|
264
|
-
var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Starting...'; }
|
|
263
|
+
async function _submitCreateMeeting(e) {
|
|
264
|
+
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Starting...'; }
|
|
265
265
|
const title = document.getElementById('mtg-title')?.value?.trim();
|
|
266
266
|
const agenda = document.getElementById('mtg-agenda')?.value?.trim();
|
|
267
267
|
if (!title || !agenda) { if (btn) { btn.disabled = false; btn.textContent = 'Start Meeting'; } alert('Title and agenda required'); return; }
|
|
@@ -4,7 +4,7 @@ function renderPinned(entries) {
|
|
|
4
4
|
entries = (entries || []).filter(function(e) { return !isDeleted('pin:' + e.title); });
|
|
5
5
|
const el = document.getElementById('pinned-content');
|
|
6
6
|
if (!el) return;
|
|
7
|
-
if (
|
|
7
|
+
if (entries.length === 0) {
|
|
8
8
|
el.innerHTML = '<p class="empty">No pinned notes. Pin important context that all agents should see.</p>';
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
@@ -40,7 +40,7 @@ async function submitPinnedNote(e) {
|
|
|
40
40
|
const title = document.getElementById('pin-title').value;
|
|
41
41
|
const content = document.getElementById('pin-content').value;
|
|
42
42
|
const level = document.getElementById('pin-level').value;
|
|
43
|
-
if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = 'Pin
|
|
43
|
+
if (!title || !content) { if (btn) { btn.disabled = false; btn.textContent = 'Pin'; } alert('Title and content required'); return; }
|
|
44
44
|
try { closeModal(); } catch { /* may not be open */ }
|
|
45
45
|
|
|
46
46
|
// Optimistic render: append the new entry to the pinned list and re-render immediately
|
|
@@ -21,15 +21,15 @@ function openCreatePlanModal() {
|
|
|
21
21
|
'<div style="font-size:11px;color:var(--muted)">After creating, click Execute on the plan card to have an agent convert it into a PRD with work items.</div>' +
|
|
22
22
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:4px">' +
|
|
23
23
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
24
|
-
'<button onclick="_submitCreatePlan()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Create Plan</button>' +
|
|
24
|
+
'<button onclick="_submitCreatePlan(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Create Plan</button>' +
|
|
25
25
|
'</div>' +
|
|
26
26
|
'</div>';
|
|
27
27
|
document.getElementById('modal').classList.add('open');
|
|
28
28
|
setTimeout(() => document.getElementById('plan-new-title')?.focus(), 100);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async function _submitCreatePlan() {
|
|
32
|
-
var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Creating...'; }
|
|
31
|
+
async function _submitCreatePlan(e) {
|
|
32
|
+
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Creating...'; }
|
|
33
33
|
const title = document.getElementById('plan-new-title')?.value?.trim();
|
|
34
34
|
const content = document.getElementById('plan-new-content')?.value?.trim();
|
|
35
35
|
if (!title) { if (btn) { btn.disabled = false; btn.textContent = 'Create Plan'; } alert('Title is required'); return; }
|
|
@@ -144,16 +144,13 @@ function renderPlans(plans) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
// Link .md plans to their PRD .json — if a PRD is being worked on, the source plan is too
|
|
148
|
-
//
|
|
147
|
+
// Link .md plans to their PRD .json — if a PRD is being worked on, the source plan is too.
|
|
148
|
+
// The .md → .json mapping is materialized in planToPrdFile below; here we only need to
|
|
149
|
+
// know whether any PRD is active to mark all candidate source .md plans as working.
|
|
149
150
|
const workingJsons = new Set([...workingPlanFiles].filter(f => f.endsWith('.json')));
|
|
150
151
|
if (workingJsons.size > 0) {
|
|
151
152
|
for (const p of plans) {
|
|
152
|
-
if (p.format === 'draft' && p.file.endsWith('.md'))
|
|
153
|
-
// A .md plan is "working" if any PRD .json has active dispatches
|
|
154
|
-
// (since the .md is the source that generated those PRDs)
|
|
155
|
-
if (workingJsons.size > 0) workingPlanFiles.add(p.file);
|
|
156
|
-
}
|
|
153
|
+
if (p.format === 'draft' && p.file.endsWith('.md')) workingPlanFiles.add(p.file);
|
|
157
154
|
}
|
|
158
155
|
}
|
|
159
156
|
|
|
@@ -134,15 +134,15 @@ function openAddPrModal() {
|
|
|
134
134
|
'<div style="font-size:11px;color:var(--muted);margin-top:-4px;padding-left:24px">Off = context only (e.g. teammate\'s PR). On = agents actively monitor and fix issues.</div>' +
|
|
135
135
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">' +
|
|
136
136
|
'<button onclick="closeModal()" class="pr-pager-btn">Cancel</button>' +
|
|
137
|
-
'<button onclick="_submitLinkPr()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Link PR</button>' +
|
|
137
|
+
'<button onclick="_submitLinkPr(event)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Link PR</button>' +
|
|
138
138
|
'</div>' +
|
|
139
139
|
'</div>';
|
|
140
140
|
document.getElementById('modal').classList.add('open');
|
|
141
141
|
setTimeout(() => document.getElementById('pr-link-url')?.focus(), 100);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
async function _submitLinkPr() {
|
|
145
|
-
var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = 'Linking...'; }
|
|
144
|
+
async function _submitLinkPr(e) {
|
|
145
|
+
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = 'Linking...'; }
|
|
146
146
|
const url = document.getElementById('pr-link-url')?.value?.trim();
|
|
147
147
|
if (!url) { if (btn) { btn.disabled = false; btn.textContent = 'Link PR'; } alert('PR URL is required'); return; }
|
|
148
148
|
const title = document.getElementById('pr-link-title')?.value?.trim() || '';
|
|
@@ -166,7 +166,8 @@ async function unlinkPr(id) {
|
|
|
166
166
|
if (!confirm('Remove ' + id + ' from tracking?')) return;
|
|
167
167
|
showToast('cmd-toast', id + ' removed', true);
|
|
168
168
|
markDeleted('pr:' + id);
|
|
169
|
-
const
|
|
169
|
+
const escId = window.CSS && CSS.escape ? CSS.escape(id) : id;
|
|
170
|
+
const row = document.querySelector('[data-pr-id="' + escId + '"]')?.closest('tr');
|
|
170
171
|
if (row) row.remove();
|
|
171
172
|
try {
|
|
172
173
|
const res = await fetch('/api/pull-requests/delete', {
|
|
@@ -292,7 +292,7 @@ function renderSchedules(schedules) {
|
|
|
292
292
|
const lastRun = s._lastRun ? timeAgo(s._lastRun) : 'never';
|
|
293
293
|
const typeBadge = '<span class="dispatch-type ' + escHtml(s.type || 'implement') + '">' + escHtml(s.type || 'implement') + '</span>';
|
|
294
294
|
const humanCron = _cronToHuman(s.cron || '');
|
|
295
|
-
html += '<tr style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openScheduleDetail(\'' + escHtml(s.id) + '\')">' +
|
|
295
|
+
html += '<tr data-sched-id="' + escHtml(s.id || '') + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openScheduleDetail(\'' + escHtml(s.id) + '\')">' +
|
|
296
296
|
'<td><span class="pr-id">' + escHtml(s.id || '') + '</span></td>' +
|
|
297
297
|
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escHtml(s.title || '') + '">' + escHtml(s.title || '') + '</td>' +
|
|
298
298
|
'<td><span title="' + escHtml(s.cron || '') + '" style="font-size:11px;color:var(--blue)">' + escHtml(humanCron) + '</span></td>' +
|
|
@@ -459,7 +459,7 @@ function _scheduleFormHtml(sched, isEdit) {
|
|
|
459
459
|
'</label>' +
|
|
460
460
|
'<div style="display:flex;justify-content:flex-end;gap:8px;margin-top:8px">' +
|
|
461
461
|
'<button onclick="closeModal()" class="pr-pager-btn" style="padding:6px 16px;font-size:var(--text-md)">Cancel</button>' +
|
|
462
|
-
'<button onclick="submitSchedule(' + isEdit + ')" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">' + (isEdit ? 'Save' : 'Create') + '</button>' +
|
|
462
|
+
'<button onclick="submitSchedule(' + isEdit + ',event)" style="padding:6px 16px;font-size:var(--text-md);background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">' + (isEdit ? 'Save' : 'Create') + '</button>' +
|
|
463
463
|
'</div>' +
|
|
464
464
|
'</div>';
|
|
465
465
|
}
|
|
@@ -487,8 +487,8 @@ function openEditScheduleModal(id) {
|
|
|
487
487
|
_updateCronPreview();
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
async function submitSchedule(isEdit) {
|
|
491
|
-
var btn = event?.target; if (btn) { btn.disabled = true; btn.textContent = isEdit ? 'Saving...' : 'Creating...'; }
|
|
490
|
+
async function submitSchedule(isEdit, e) {
|
|
491
|
+
var btn = (e || window.event)?.target; if (btn) { btn.disabled = true; btn.textContent = isEdit ? 'Saving...' : 'Creating...'; }
|
|
492
492
|
_showScheduleError('');
|
|
493
493
|
const title = document.getElementById('sched-edit-title').value.trim();
|
|
494
494
|
const cron = window._schedComputedCron || '';
|
|
@@ -505,7 +505,7 @@ async function submitSchedule(isEdit) {
|
|
|
505
505
|
id = _generateScheduleId(title);
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
function _resetSchedBtn() { if (btn) { btn.disabled = false; btn.textContent = isEdit ? 'Save
|
|
508
|
+
function _resetSchedBtn() { if (btn) { btn.disabled = false; btn.textContent = isEdit ? 'Save' : 'Create'; } }
|
|
509
509
|
if (!title) { _resetSchedBtn(); _showScheduleError('Title is required'); return; }
|
|
510
510
|
if (!cron) { _resetSchedBtn(); _showScheduleError('Schedule is required \u2014 select days and time, or use natural language'); return; }
|
|
511
511
|
|
|
@@ -523,9 +523,15 @@ async function submitSchedule(isEdit) {
|
|
|
523
523
|
} catch (e) { _resetSchedBtn(); _showScheduleError('Error: ' + e.message); }
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
function _findSchedRow(id) {
|
|
527
|
+
var sel = 'tr[data-sched-id="' + (window.CSS && CSS.escape ? CSS.escape(id) : id) + '"]';
|
|
528
|
+
return document.querySelector(sel);
|
|
529
|
+
}
|
|
530
|
+
|
|
526
531
|
async function toggleScheduleEnabled(id, enabled) {
|
|
527
532
|
// Optimistic toggle — swap badge text immediately
|
|
528
|
-
|
|
533
|
+
var row = _findSchedRow(id);
|
|
534
|
+
if (row) { var badge = row.querySelector('.status-badge'); if (badge) badge.textContent = enabled ? 'ENABLED' : 'DISABLED'; }
|
|
529
535
|
try {
|
|
530
536
|
const res = await fetch('/api/schedules/update', {
|
|
531
537
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -542,7 +548,8 @@ async function deleteSchedule(id) {
|
|
|
542
548
|
if (!confirm('Delete scheduled task "' + id + '"?')) return;
|
|
543
549
|
showToast('cmd-toast', 'Schedule deleted', true);
|
|
544
550
|
markDeleted('sched:' + id);
|
|
545
|
-
|
|
551
|
+
var row = _findSchedRow(id);
|
|
552
|
+
if (row) row.remove();
|
|
546
553
|
try {
|
|
547
554
|
const res = await fetch('/api/schedules/delete', {
|
|
548
555
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -184,8 +184,10 @@ function toggleWatchPause(id, pause) {
|
|
|
184
184
|
method: 'POST',
|
|
185
185
|
headers: { 'Content-Type': 'application/json' },
|
|
186
186
|
body: JSON.stringify({ id: id, status: newStatus })
|
|
187
|
-
}).then(function(res) {
|
|
188
|
-
|
|
187
|
+
}).then(async function(res) {
|
|
188
|
+
var data = await res.json().catch(function() { return {}; });
|
|
189
|
+
if (!res.ok || data.error) showToast('cmd-toast', 'Error: ' + (data.error || ('HTTP ' + res.status)), false);
|
|
190
|
+
else if (typeof refresh === 'function') refresh();
|
|
189
191
|
}).catch(function(err) {
|
|
190
192
|
showToast('cmd-toast', 'Error: ' + err.message, false);
|
|
191
193
|
});
|
|
@@ -297,12 +299,14 @@ function submitWatch() {
|
|
|
297
299
|
stopAfter: stopAfter,
|
|
298
300
|
onNotMet: onNotMet || null,
|
|
299
301
|
})
|
|
300
|
-
}).then(function(res) {
|
|
301
|
-
|
|
302
|
-
|
|
302
|
+
}).then(async function(res) {
|
|
303
|
+
var data = await res.json().catch(function() { return {}; });
|
|
304
|
+
if (!res.ok || data.error) {
|
|
305
|
+
showToast('cmd-toast', 'Error: ' + (data.error || ('HTTP ' + res.status)), false);
|
|
303
306
|
} else {
|
|
304
|
-
showToast('cmd-toast', 'Watch created: ' + data.watch.id, true);
|
|
307
|
+
showToast('cmd-toast', 'Watch created: ' + (data.watch && data.watch.id || ''), true);
|
|
305
308
|
closeModal();
|
|
309
|
+
if (typeof refresh === 'function') refresh();
|
|
306
310
|
}
|
|
307
311
|
}).catch(function(err) {
|
|
308
312
|
showToast('cmd-toast', 'Error: ' + err.message, false);
|
|
@@ -40,7 +40,7 @@ function wiRow(item) {
|
|
|
40
40
|
: (item.branchStrategy === 'shared-branch' && item.status === 'done')
|
|
41
41
|
? '<span style="font-size:9px;color:var(--muted)" title="Part of shared branch — aggregate PR created at verify stage">shared branch</span>'
|
|
42
42
|
: '<span style="color:var(--muted)">—</span>';
|
|
43
|
-
return '<tr style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\')">' +
|
|
43
|
+
return '<tr data-wi-id="' + escapeHtml(item.id) + '" style="cursor:pointer" onclick="if(shouldIgnoreSelectionClick(event))return;openWorkItemDetail(\'' + escapeHtml(item.id) + '\')">' +
|
|
44
44
|
'<td><span class="pr-id">' + escapeHtml(item.id || '') + '</span></td>' +
|
|
45
45
|
'<td style="max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escapeHtml((item.title || '').slice(0, 200)) + '">' + escapeHtml(item.title || '') + '</td>' +
|
|
46
46
|
'<td><span style="font-size:10px;color:var(--muted)">' + escapeHtml(item._source || '') + '</span>' +
|
|
@@ -195,12 +195,16 @@ async function submitWorkItemEdit(id, source, e) {
|
|
|
195
195
|
} catch (e) { alert('Update error: ' + e.message); editWorkItem(id, source); }
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
function _removeWiRow(id) {
|
|
199
|
+
var sel = 'tr[data-wi-id="' + (window.CSS && CSS.escape ? CSS.escape(id) : id) + '"]';
|
|
200
|
+
document.querySelectorAll(sel).forEach(function(r) { r.remove(); });
|
|
201
|
+
}
|
|
202
|
+
|
|
198
203
|
async function deleteWorkItem(id, source) {
|
|
199
204
|
if (!confirm('Delete work item ' + id + '? This will kill any running agent and remove all dispatch history.')) return;
|
|
200
205
|
showToast('cmd-toast', 'Work item deleted', true);
|
|
201
206
|
markDeleted('wi:' + id);
|
|
202
|
-
|
|
203
|
-
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
207
|
+
_removeWiRow(id);
|
|
204
208
|
try {
|
|
205
209
|
const res = await fetch('/api/work-items/delete', {
|
|
206
210
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -214,8 +218,7 @@ async function cancelWorkItem(id, source) {
|
|
|
214
218
|
if (!confirm('Cancel work item ' + id + '? This will kill any running agent and mark it cancelled.')) return;
|
|
215
219
|
showToast('cmd-toast', 'Work item cancelled', true);
|
|
216
220
|
markDeleted('wi:' + id);
|
|
217
|
-
|
|
218
|
-
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
221
|
+
_removeWiRow(id);
|
|
219
222
|
try {
|
|
220
223
|
const res = await fetch('/api/work-items/cancel', {
|
|
221
224
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
@@ -228,8 +231,7 @@ async function cancelWorkItem(id, source) {
|
|
|
228
231
|
async function archiveWorkItem(id, source) {
|
|
229
232
|
showToast('cmd-toast', 'Archived ' + id, true);
|
|
230
233
|
markDeleted('wi:' + id);
|
|
231
|
-
|
|
232
|
-
(wiTable || document).querySelectorAll('tr').forEach(function(r) { if (r.textContent.includes(id)) r.remove(); });
|
|
234
|
+
_removeWiRow(id);
|
|
233
235
|
try {
|
|
234
236
|
const res = await fetch('/api/work-items/archive', {
|
|
235
237
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
package/engine/ado.js
CHANGED
|
@@ -795,10 +795,9 @@ async function pollPrHumanComments(config) {
|
|
|
795
795
|
for (const comment of (thread.comments || [])) {
|
|
796
796
|
if (!comment.content || comment.commentType === 'system') continue;
|
|
797
797
|
const content = comment.content;
|
|
798
|
-
// Skip
|
|
798
|
+
// Skip explicitly ignored authors and CI-report bodies, but do not ignore bot authors by default.
|
|
799
799
|
const authorName = (comment.author?.displayName || '').toLowerCase();
|
|
800
800
|
if (ignoredAuthors.some(a => authorName.includes(a))) continue;
|
|
801
|
-
if (/\b(bot|service|build|pipeline|codecov|sonar)\b/i.test(authorName)) continue;
|
|
802
801
|
if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(content)) continue;
|
|
803
802
|
// Detect agent comments (included in context, but don't trigger fix)
|
|
804
803
|
const isAgent = /\bMinions\s*\(/i.test(content) || /\bby\s+Minions\b/i.test(content) || /\[minions\]/i.test(content);
|
package/engine/github.js
CHANGED
|
@@ -561,14 +561,12 @@ async function pollPrHumanComments(config) {
|
|
|
561
561
|
...(Array.isArray(reviewComments) ? reviewComments : []).map(c => ({ ...c, _type: 'review' }))
|
|
562
562
|
];
|
|
563
563
|
|
|
564
|
-
// Separate: agent comments (included in context, don't trigger fix) vs
|
|
565
|
-
//
|
|
564
|
+
// Separate: agent comments (included in context, don't trigger fix) vs actionable comments (trigger fix).
|
|
565
|
+
// Bot-authored comments are actionable unless explicitly ignored or clearly CI report noise.
|
|
566
566
|
const ignoredAuthors = new Set((config.engine?.ignoredCommentAuthors || []).map(a => a.toLowerCase()));
|
|
567
|
-
function
|
|
568
|
-
if (c.user?.type === 'Bot') return true;
|
|
567
|
+
function _isIgnoredComment(c) {
|
|
569
568
|
const login = (c.user?.login || '').toLowerCase();
|
|
570
569
|
if (ignoredAuthors.has(login)) return true;
|
|
571
|
-
if (/\b(bot|codecov|sonar|dependabot|renovate|github-actions|azure-pipelines)\b/i.test(login)) return true;
|
|
572
570
|
const body = c.body || '';
|
|
573
571
|
if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(body)) return true;
|
|
574
572
|
if (/!\[.*\]\(https?:\/\/.*badge/i.test(body)) return true;
|
|
@@ -581,16 +579,16 @@ async function pollPrHumanComments(config) {
|
|
|
581
579
|
if (/\[minions\]/i.test(body)) return true;
|
|
582
580
|
return false;
|
|
583
581
|
}
|
|
584
|
-
const
|
|
582
|
+
const actionableComments = allComments.filter(c => !_isIgnoredComment(c));
|
|
585
583
|
|
|
586
584
|
const cutoffStr = pr.humanFeedback?.lastProcessedCommentDate || pr.created || '1970-01-01';
|
|
587
585
|
const cutoffMs = new Date(cutoffStr).getTime() || 0;
|
|
588
586
|
|
|
589
|
-
// Collect ALL
|
|
587
|
+
// Collect ALL actionable comments for full context, track new ones for triggering
|
|
590
588
|
const allCommentEntries = [];
|
|
591
589
|
const newComments = [];
|
|
592
590
|
|
|
593
|
-
for (const c of
|
|
591
|
+
for (const c of actionableComments) {
|
|
594
592
|
const date = c.created_at || c.updated_at || '';
|
|
595
593
|
const isAgent = _isAgentComment(c);
|
|
596
594
|
const entry = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1678",
|
|
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"
|