@yemi33/minions 0.1.518 → 0.1.520
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/live-stream.js +26 -28
- package/dashboard/js/refresh.js +33 -26
- package/engine/ado.js +1 -1
- package/engine/dispatch.js +4 -1
- package/engine/lifecycle.js +1 -0
- package/engine/queries.js +10 -2
- package/engine.js +20 -25
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.520 (2026-04-07)
|
|
4
|
+
|
|
5
|
+
### Other
|
|
6
|
+
- perf: reduce curl timeout to 5s, cache getDispatch() reads
|
|
7
|
+
|
|
8
|
+
## 0.1.519 (2026-04-07)
|
|
9
|
+
|
|
10
|
+
### Other
|
|
11
|
+
- perf: fix 5 performance bottlenecks in engine and dashboard
|
|
12
|
+
|
|
3
13
|
## 0.1.518 (2026-04-07)
|
|
4
14
|
|
|
5
15
|
### Fixes
|
|
@@ -3,24 +3,26 @@
|
|
|
3
3
|
let livePollingInterval = null;
|
|
4
4
|
let liveEventSource = null;
|
|
5
5
|
let _steerInFlight = false;
|
|
6
|
+
let _lastRenderedText = '';
|
|
6
7
|
|
|
7
8
|
function renderLiveChatMessage(raw) {
|
|
8
9
|
const el = document.getElementById('live-messages');
|
|
9
10
|
if (!el) return;
|
|
11
|
+
const fragments = [];
|
|
10
12
|
|
|
11
13
|
function renderJsonObj(obj) {
|
|
12
14
|
if (obj.type === 'assistant' && obj.message?.content) {
|
|
13
15
|
for (const block of obj.message.content) {
|
|
14
16
|
if (block.type === 'thinking') {
|
|
15
|
-
|
|
17
|
+
fragments.push('<div style="font-size:10px;color:var(--muted);padding:2px 8px;font-style:italic">\u{1F4AD} Thinking...</div>');
|
|
16
18
|
}
|
|
17
19
|
if (block.type === 'text' && block.text) {
|
|
18
|
-
|
|
20
|
+
fragments.push('<div style="background:var(--surface2);padding:8px 12px;border-radius:12px 12px 12px 2px;max-width:90%;margin:4px 0;font-size:12px;word-break:break-word">' + renderMd(block.text) + '</div>');
|
|
19
21
|
}
|
|
20
22
|
if (block.type === 'tool_use') {
|
|
21
|
-
|
|
23
|
+
fragments.push('<div style="background:var(--surface);border:1px solid var(--border);padding:4px 8px;border-radius:4px;margin:2px 0;font-size:10px;color:var(--muted);cursor:pointer" onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display===\'none\'?\'block\':\'none\'">' +
|
|
22
24
|
'\u{1F527} ' + escHtml(block.name || 'tool') + '</div>' +
|
|
23
|
-
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + escHtml(JSON.stringify(block.input || {}, null, 2).slice(0, 500)) + '</div>';
|
|
25
|
+
'<div style="display:none;background:var(--bg);padding:4px 8px;border-radius:4px;margin:0 0 4px;font-size:10px;font-family:monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;color:var(--muted)">' + escHtml(JSON.stringify(block.input || {}, null, 2).slice(0, 500)) + '</div>');
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -28,11 +30,11 @@ function renderLiveChatMessage(raw) {
|
|
|
28
30
|
const content = obj.message?.content?.[0]?.content || obj.content || '';
|
|
29
31
|
const text = typeof content === 'string' ? content : JSON.stringify(content);
|
|
30
32
|
if (text.length > 10) {
|
|
31
|
-
|
|
33
|
+
fragments.push('<div style="background:var(--bg);border-left:2px solid var(--border);padding:2px 8px;margin:0 0 2px 16px;font-size:9px;font-family:monospace;color:var(--muted);max-height:100px;overflow-y:auto;white-space:pre-wrap;cursor:pointer" onclick="this.style.maxHeight=this.style.maxHeight===\'100px\'?\'none\':\'100px\'">' + escHtml(text.slice(0, 1000)) + (text.length > 1000 ? '...' : '') + '</div>');
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
if (obj.type === 'result') {
|
|
35
|
-
|
|
37
|
+
fragments.push('<div style="background:rgba(63,185,80,0.1);border:1px solid var(--green);padding:8px 12px;border-radius:8px;margin:8px 0;font-size:12px;color:var(--green)">\u2713 Task complete</div>');
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -41,47 +43,36 @@ function renderLiveChatMessage(raw) {
|
|
|
41
43
|
const trimmed = line.trim();
|
|
42
44
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
43
45
|
|
|
44
|
-
// Human steering messages
|
|
45
46
|
if (trimmed.startsWith('[human-steering]')) {
|
|
46
47
|
const msg = trimmed.replace('[human-steering] ', '');
|
|
47
|
-
|
|
48
|
-
'<div style="font-size:9px;opacity:0.7;margin-top:2px">\u2713 Queued</div></div>';
|
|
48
|
+
fragments.push('<div style="align-self:flex-end;background:var(--blue);color:#fff;padding:6px 12px;border-radius:12px 12px 2px 12px;max-width:80%;margin:4px 0;font-size:12px">' + escHtml(msg) +
|
|
49
|
+
'<div style="font-size:9px;opacity:0.7;margin-top:2px">\u2713 Queued</div></div>');
|
|
49
50
|
continue;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
// Heartbeat lines
|
|
53
|
-
if (trimmed.startsWith('[heartbeat]')) {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Steering failure notices
|
|
52
|
+
if (trimmed.startsWith('[heartbeat]')) continue;
|
|
58
53
|
if (trimmed.startsWith('[steering-failed]')) {
|
|
59
54
|
const msg = trimmed.replace('[steering-failed] ', '');
|
|
60
|
-
|
|
55
|
+
fragments.push('<div style="background:rgba(248,81,73,0.1);border:1px solid var(--red);color:var(--red);padding:6px 12px;border-radius:8px;margin:4px 0;font-size:11px">\u26A0 ' + escHtml(msg) + '</div>');
|
|
61
56
|
continue;
|
|
62
57
|
}
|
|
63
|
-
|
|
64
|
-
// JSON array format (--output-format json)
|
|
65
58
|
if (trimmed.startsWith('[')) {
|
|
66
59
|
try {
|
|
67
60
|
const arr = JSON.parse(trimmed);
|
|
68
61
|
if (Array.isArray(arr)) { for (const obj of arr) renderJsonObj(obj); continue; }
|
|
69
|
-
} catch { /* fall through
|
|
62
|
+
} catch { /* fall through */ }
|
|
70
63
|
}
|
|
71
|
-
|
|
72
|
-
// Single JSON object (--output-format stream-json)
|
|
73
64
|
if (trimmed.startsWith('{')) {
|
|
74
65
|
try { renderJsonObj(JSON.parse(trimmed)); continue; } catch { /* fall through */ }
|
|
75
66
|
}
|
|
76
|
-
|
|
77
|
-
// Fallback: raw text (stderr, non-JSON lines)
|
|
78
67
|
if (trimmed.startsWith('[stderr]')) {
|
|
79
|
-
|
|
68
|
+
fragments.push('<div style="font-size:9px;color:var(--red);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
80
69
|
} else {
|
|
81
|
-
|
|
70
|
+
fragments.push('<div style="font-size:10px;color:var(--muted);font-family:monospace;padding:1px 4px">' + escHtml(trimmed) + '</div>');
|
|
82
71
|
}
|
|
83
72
|
}
|
|
84
73
|
|
|
74
|
+
if (fragments.length > 0) el.innerHTML += fragments.join('');
|
|
75
|
+
|
|
85
76
|
// Auto-scroll
|
|
86
77
|
if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) {
|
|
87
78
|
el.scrollTop = el.scrollHeight;
|
|
@@ -94,6 +85,7 @@ function startLiveStream(agentId) {
|
|
|
94
85
|
|
|
95
86
|
const msgEl = document.getElementById('live-messages');
|
|
96
87
|
if (msgEl) msgEl.innerHTML = '';
|
|
88
|
+
_lastRenderedText = '';
|
|
97
89
|
|
|
98
90
|
// Use polling instead of SSE to avoid HTTP/1.1 connection exhaustion
|
|
99
91
|
// (SSE holds a persistent connection, blocking CC and other API calls)
|
|
@@ -126,8 +118,14 @@ async function refreshLiveOutput() {
|
|
|
126
118
|
const el = document.getElementById('live-messages');
|
|
127
119
|
if (el) {
|
|
128
120
|
const wasAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 150;
|
|
129
|
-
|
|
130
|
-
|
|
121
|
+
// Incremental render: only parse new content if text is an extension of previous
|
|
122
|
+
if (_lastRenderedText && text.length > _lastRenderedText.length && text.startsWith(_lastRenderedText.slice(0, 200))) {
|
|
123
|
+
renderLiveChatMessage(text.slice(_lastRenderedText.length));
|
|
124
|
+
} else {
|
|
125
|
+
el.innerHTML = '';
|
|
126
|
+
renderLiveChatMessage(text);
|
|
127
|
+
}
|
|
128
|
+
_lastRenderedText = text;
|
|
131
129
|
if (wasAtBottom) el.scrollTop = el.scrollHeight;
|
|
132
130
|
}
|
|
133
131
|
} catch (e) { console.error('live-stream reload:', e.message); }
|
package/dashboard/js/refresh.js
CHANGED
|
@@ -25,6 +25,15 @@ function _detectPageChanges(data) {
|
|
|
25
25
|
return changes;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Change detection — skip renders for sections that haven't changed since last refresh
|
|
29
|
+
const _sectionCache = {};
|
|
30
|
+
function _changed(key, value) {
|
|
31
|
+
var json = JSON.stringify(value);
|
|
32
|
+
if (_sectionCache[key] === json) return false;
|
|
33
|
+
_sectionCache[key] = json;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
function _processStatusUpdate(data) {
|
|
29
38
|
// Detect fresh install — clear stale browser state if install ID changed
|
|
30
39
|
if (data.installId) {
|
|
@@ -35,45 +44,43 @@ function _processStatusUpdate(data) {
|
|
|
35
44
|
}
|
|
36
45
|
localStorage.setItem('minions-install-id', data.installId);
|
|
37
46
|
}
|
|
47
|
+
// Always update cheap elements
|
|
38
48
|
document.getElementById('ts').textContent = new Date(data.timestamp).toLocaleTimeString();
|
|
39
49
|
const engineState = (data.engine && data.engine.state) ? data.engine.state : 'stopped';
|
|
40
50
|
document.getElementById('setup-banner').style.display = (!data.initialized && engineState !== 'stopped') ? 'block' : 'none';
|
|
41
|
-
renderAgents(data.agents);
|
|
42
|
-
renderPrdProgress(data.prdProgress);
|
|
43
|
-
_cachePrdItems(data.prdProgress);
|
|
44
|
-
renderInbox(data.inbox || []);
|
|
45
|
-
cmdUpdateAgentList(data.agents);
|
|
46
|
-
cmdUpdateProjectList(data.projects || []);
|
|
47
|
-
renderNotes(data.notes);
|
|
48
|
-
renderPrd(data.prd, data.prdProgress);
|
|
49
|
-
// Auto-approve badge
|
|
50
51
|
const autoEl = document.getElementById('auto-approve-badge');
|
|
51
52
|
if (autoEl) autoEl.innerHTML = data.autoMode?.approvePlans
|
|
52
53
|
? '<span style="font-size:9px;font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(63,185,80,0.15);color:var(--green);border:1px solid rgba(63,185,80,0.3)">AUTO-APPROVE</span>'
|
|
53
54
|
: '';
|
|
54
|
-
// Inbox consolidation threshold from config
|
|
55
55
|
const threshEl = document.getElementById('inbox-threshold');
|
|
56
56
|
if (threshEl && data.autoMode?.inboxThreshold) threshEl.textContent = data.autoMode.inboxThreshold;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
|
|
58
|
+
// Render only changed sections
|
|
59
|
+
if (_changed('agents', data.agents)) { renderAgents(data.agents); cmdUpdateAgentList(data.agents); }
|
|
60
|
+
if (_changed('prdProgress', data.prdProgress)) { renderPrdProgress(data.prdProgress); _cachePrdItems(data.prdProgress); }
|
|
61
|
+
if (_changed('inbox', data.inbox)) renderInbox(data.inbox || []);
|
|
62
|
+
if (_changed('projects', data.projects)) { cmdUpdateProjectList(data.projects || []); renderProjects(data.projects || []); }
|
|
63
|
+
if (_changed('notes', data.notes)) renderNotes(data.notes);
|
|
64
|
+
if (_changed('prd', [data.prd, data.prdProgress])) renderPrd(data.prd, data.prdProgress);
|
|
65
|
+
if (_changed('prs', data.pullRequests)) renderPrs(data.pullRequests || []);
|
|
66
|
+
if (_changed('archivedPrds', data.archivedPrds)) renderArchiveButtons(data.archivedPrds || []);
|
|
67
|
+
if (_changed('engine', data.engine)) renderEngineStatus(data.engine);
|
|
68
|
+
if (_changed('version', data.version)) renderVersionBanner(data.version);
|
|
69
|
+
if (_changed('dispatch', data.dispatch)) renderDispatch(data.dispatch);
|
|
62
70
|
window._lastDispatch = data.dispatch;
|
|
63
71
|
window._lastWorkItems = data.workItems || [];
|
|
64
72
|
window._lastStatus = data;
|
|
65
73
|
prunePrdRequeueState(window._lastWorkItems);
|
|
66
|
-
renderEngineLog(data.engineLog || []);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
// Update sidebar counts
|
|
74
|
+
if (_changed('engineLog', data.engineLog)) renderEngineLog(data.engineLog || []);
|
|
75
|
+
if (_changed('metrics', data.metrics)) renderMetrics(data.metrics || {});
|
|
76
|
+
if (_changed('workItems', data.workItems)) renderWorkItems(data.workItems || []);
|
|
77
|
+
if (_changed('skills', data.skills)) renderSkills(data.skills || []);
|
|
78
|
+
if (_changed('mcpServers', data.mcpServers)) renderMcpServers(data.mcpServers || []);
|
|
79
|
+
if (_changed('schedules', data.schedules)) renderSchedules(data.schedules || []);
|
|
80
|
+
if (_changed('meetings', data.meetings)) renderMeetings(data.meetings || []);
|
|
81
|
+
if (_changed('pipelines', data.pipelines) && typeof renderPipelines === 'function') renderPipelines(data.pipelines || []);
|
|
82
|
+
if (_changed('pinned', data.pinned)) renderPinned(data.pinned || []);
|
|
83
|
+
// Sidebar counts (cheap)
|
|
77
84
|
const swi = document.getElementById('sidebar-wi');
|
|
78
85
|
if (swi) swi.textContent = (data.workItems || []).length || '';
|
|
79
86
|
const spr = document.getElementById('sidebar-pr');
|
package/engine/ado.js
CHANGED
|
@@ -495,7 +495,7 @@ function checkLiveReviewStatus(pr, project) {
|
|
|
495
495
|
const orgBase = shared.getAdoOrgBase(project);
|
|
496
496
|
const prNum = (pr.id || '').replace(/^PR-/, '');
|
|
497
497
|
const url = `${orgBase}/${project.adoProject}/_apis/git/repositories/${project.repositoryId}/pullrequests/${prNum}?api-version=7.1`;
|
|
498
|
-
const result = exec(`curl -s -H "Authorization: Bearer ${token}" "${url}"`, { encoding: 'utf-8', timeout:
|
|
498
|
+
const result = exec(`curl -s --max-time 4 -H "Authorization: Bearer ${token}" "${url}"`, { encoding: 'utf-8', timeout: 5000, windowsHide: true });
|
|
499
499
|
const prData = JSON.parse(result);
|
|
500
500
|
const votes = (prData.reviewers || []).map(r => r.vote).filter(v => v !== undefined);
|
|
501
501
|
if (votes.length === 0) return 'pending';
|
package/engine/dispatch.js
CHANGED
|
@@ -24,12 +24,15 @@ function lifecycle() { if (!_lifecycle) _lifecycle = require('./lifecycle'); ret
|
|
|
24
24
|
|
|
25
25
|
function mutateDispatch(mutator) {
|
|
26
26
|
const defaultDispatch = { pending: [], active: [], completed: [] };
|
|
27
|
-
|
|
27
|
+
const result = mutateJsonFileLocked(DISPATCH_PATH, (dispatch) => {
|
|
28
28
|
dispatch.pending = Array.isArray(dispatch.pending) ? dispatch.pending : [];
|
|
29
29
|
dispatch.active = Array.isArray(dispatch.active) ? dispatch.active : [];
|
|
30
30
|
dispatch.completed = Array.isArray(dispatch.completed) ? dispatch.completed : [];
|
|
31
31
|
return mutator(dispatch) || dispatch;
|
|
32
32
|
}, { defaultValue: defaultDispatch });
|
|
33
|
+
// Invalidate the read cache so next getDispatch() sees fresh data
|
|
34
|
+
try { require('./queries').invalidateDispatchCache(); } catch {}
|
|
35
|
+
return result;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
// ─── Add to Dispatch ─────────────────────────────────────────────────────────
|
package/engine/lifecycle.js
CHANGED
|
@@ -277,6 +277,7 @@ function checkPlanCompletion(meta, config) {
|
|
|
277
277
|
// Archive deferred until verify completes
|
|
278
278
|
|
|
279
279
|
log('info', `PRD ${planFile} completed: ${doneItems.length} done, ${failedItems.length} failed, runtime ${runtimeMin}m`);
|
|
280
|
+
return true;
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
// ─── Archive Plan ───────────────────────────────────────────────────────────
|
package/engine/queries.js
CHANGED
|
@@ -79,9 +79,17 @@ function getControl() {
|
|
|
79
79
|
return safeJson(CONTROL_PATH) || { state: 'stopped', pid: null };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
let _dispatchCache = null;
|
|
83
|
+
let _dispatchCacheAt = 0;
|
|
82
84
|
function getDispatch() {
|
|
83
|
-
|
|
85
|
+
// Short-lived cache — dispatch.json is read 10+ times per tick but only changes on mutateDispatch
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
if (_dispatchCache && (now - _dispatchCacheAt) < 2000) return _dispatchCache;
|
|
88
|
+
_dispatchCache = safeJson(DISPATCH_PATH) || { pending: [], active: [], completed: [] };
|
|
89
|
+
_dispatchCacheAt = now;
|
|
90
|
+
return _dispatchCache;
|
|
84
91
|
}
|
|
92
|
+
function invalidateDispatchCache() { _dispatchCache = null; _dispatchCacheAt = 0; }
|
|
85
93
|
|
|
86
94
|
function getDispatchQueue() {
|
|
87
95
|
const d = getDispatch();
|
|
@@ -874,7 +882,7 @@ module.exports = {
|
|
|
874
882
|
resetPrdInfoCache,
|
|
875
883
|
|
|
876
884
|
// Core state
|
|
877
|
-
getConfig, getControl, getDispatch, getDispatchQueue,
|
|
885
|
+
getConfig, getControl, getDispatch, getDispatchQueue, invalidateDispatchCache,
|
|
878
886
|
getNotes, getNotesWithMeta, getEngineLog, getMetrics,
|
|
879
887
|
|
|
880
888
|
// Inbox
|
package/engine.js
CHANGED
|
@@ -1492,6 +1492,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
1492
1492
|
const prdSyncQueue = [];
|
|
1493
1493
|
const skipped = { gated: 0, noAgent: 0 };
|
|
1494
1494
|
let needsWrite = false;
|
|
1495
|
+
const selfHealKeys = new Set(); // Collect keys for batched self-heal (1 lock instead of N)
|
|
1495
1496
|
|
|
1496
1497
|
for (const item of items) {
|
|
1497
1498
|
try {
|
|
@@ -1526,25 +1527,13 @@ function discoverFromWorkItems(config, project) {
|
|
|
1526
1527
|
}
|
|
1527
1528
|
|
|
1528
1529
|
const key = `work-${project?.name || 'default'}-${item.id}`;
|
|
1529
|
-
// Self-heal:
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
const prev = Array.isArray(dp.completed) ? dp.completed : [];
|
|
1534
|
-
const next = [];
|
|
1535
|
-
for (let i = 0; i < prev.length; i++) {
|
|
1536
|
-
if (prev[i].meta?.dispatchKey !== key) next.push(prev[i]);
|
|
1537
|
-
}
|
|
1538
|
-
dp.completed = next;
|
|
1539
|
-
return dp;
|
|
1540
|
-
});
|
|
1541
|
-
dispatchCooldowns.delete(key);
|
|
1542
|
-
} catch (e) { log('warn', 'self-heal dispatch state: ' + e.message); }
|
|
1543
|
-
// Cooldown bypass for resumed items — clear in-memory cooldown so they dispatch immediately
|
|
1530
|
+
// Self-heal: collect keys for batched dispatch.json cleanup (after the loop)
|
|
1531
|
+
selfHealKeys.add(key);
|
|
1532
|
+
dispatchCooldowns.delete(key);
|
|
1533
|
+
// Cooldown bypass for resumed items
|
|
1544
1534
|
if (item._resumedAt) {
|
|
1545
|
-
dispatchCooldowns.delete(key);
|
|
1546
1535
|
delete item._resumedAt;
|
|
1547
|
-
|
|
1536
|
+
needsWrite = true;
|
|
1548
1537
|
}
|
|
1549
1538
|
if (isAlreadyDispatched(key)) {
|
|
1550
1539
|
if (item.status === WI_STATUS.PENDING) { item.status = WI_STATUS.DISPATCHED; needsWrite = true; }
|
|
@@ -1696,6 +1685,16 @@ function discoverFromWorkItems(config, project) {
|
|
|
1696
1685
|
} catch (err) { log('warn', `discoverFromWorkItems: skipping ${item.id}: ${err.message}`); }
|
|
1697
1686
|
}
|
|
1698
1687
|
|
|
1688
|
+
// Batched self-heal: clear all stale completed entries in ONE lock acquisition
|
|
1689
|
+
if (selfHealKeys.size > 0) {
|
|
1690
|
+
try {
|
|
1691
|
+
mutateDispatch((dp) => {
|
|
1692
|
+
dp.completed = (Array.isArray(dp.completed) ? dp.completed : []).filter(d => !selfHealKeys.has(d.meta?.dispatchKey));
|
|
1693
|
+
return dp;
|
|
1694
|
+
});
|
|
1695
|
+
} catch (e) { log('warn', 'batched self-heal: ' + e.message); }
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1699
1698
|
// Auto-promote decomposed parents to done when all sub-tasks complete
|
|
1700
1699
|
for (const item of items) {
|
|
1701
1700
|
if (item.status !== WI_STATUS.DECOMPOSED || !item._subItemIds?.length) continue;
|
|
@@ -2264,10 +2263,8 @@ function discoverWork(config) {
|
|
|
2264
2263
|
}
|
|
2265
2264
|
if (plan.status !== 'approved' && plan.status !== 'active') continue;
|
|
2266
2265
|
// Simulate the meta object checkPlanCompletion expects
|
|
2267
|
-
lifecycle.checkPlanCompletion({ item: { sourcePlan: f } }, config);
|
|
2268
|
-
|
|
2269
|
-
const after = safeJson(path.join(prdDir, f));
|
|
2270
|
-
if (after?.status === 'completed') completedPlanCache.add(f);
|
|
2266
|
+
const completed = lifecycle.checkPlanCompletion({ item: { sourcePlan: f } }, config);
|
|
2267
|
+
if (completed) completedPlanCache.add(f);
|
|
2271
2268
|
}
|
|
2272
2269
|
}
|
|
2273
2270
|
} catch (e) { log('warn', 'plan completion sweep: ' + e.message); }
|
|
@@ -2379,10 +2376,8 @@ async function tickInner() {
|
|
|
2379
2376
|
if (completedPlanCache.has(file)) continue;
|
|
2380
2377
|
const plan = safeJson(path.join(PRD_DIR, file));
|
|
2381
2378
|
if (plan && plan.missing_features && plan.status !== 'completed') {
|
|
2382
|
-
checkPlanCompletion({ item: { sourcePlan: file } }, config);
|
|
2383
|
-
|
|
2384
|
-
const after = safeJson(path.join(PRD_DIR, file));
|
|
2385
|
-
if (after?.status === 'completed') completedPlanCache.add(file);
|
|
2379
|
+
const completed = checkPlanCompletion({ item: { sourcePlan: file } }, config);
|
|
2380
|
+
if (completed) completedPlanCache.add(file);
|
|
2386
2381
|
} else if (plan?.status === 'completed') {
|
|
2387
2382
|
completedPlanCache.add(file);
|
|
2388
2383
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.520",
|
|
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"
|