@yemi33/minions 0.1.58 → 0.1.60
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 +39 -0
- package/dashboard/js/command-center.js +1 -1
- package/dashboard/js/live-stream.js +3 -3
- package/dashboard/js/modal-qa.js +4 -4
- package/dashboard/js/refresh.js +2 -2
- package/dashboard/js/render-inbox.js +3 -3
- package/dashboard/js/render-kb.js +1 -1
- package/dashboard/js/render-plans.js +3 -3
- package/dashboard/js/render-prs.js +2 -2
- package/dashboard/js/render-work-items.js +2 -2
- package/dashboard/js/settings.js +1 -1
- package/dashboard.js +46 -46
- package/engine/ado.js +2 -2
- package/engine/cli.js +16 -16
- package/engine/consolidation.js +4 -4
- package/engine/cooldown.js +117 -0
- package/engine/github.js +3 -3
- package/engine/lifecycle.js +14 -14
- package/engine/llm.js +2 -2
- package/engine/playbook.js +479 -0
- package/engine/preflight.js +2 -2
- package/engine/queries.js +13 -13
- package/engine/routing.js +163 -0
- package/engine/shared.js +6 -6
- package/engine/spawn-agent.js +3 -3
- package/engine.js +106 -733
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.60 (2026-03-30)
|
|
4
|
+
|
|
5
|
+
### Engine
|
|
6
|
+
- engine.js
|
|
7
|
+
- engine/cooldown.js
|
|
8
|
+
- engine/playbook.js
|
|
9
|
+
- engine/routing.js
|
|
10
|
+
|
|
11
|
+
### Other
|
|
12
|
+
- test/unit.test.js
|
|
13
|
+
|
|
14
|
+
## 0.1.59 (2026-03-30)
|
|
15
|
+
|
|
16
|
+
### Engine
|
|
17
|
+
- engine.js
|
|
18
|
+
- engine/ado.js
|
|
19
|
+
- engine/cli.js
|
|
20
|
+
- engine/consolidation.js
|
|
21
|
+
- engine/github.js
|
|
22
|
+
- engine/lifecycle.js
|
|
23
|
+
- engine/llm.js
|
|
24
|
+
- engine/preflight.js
|
|
25
|
+
- engine/queries.js
|
|
26
|
+
- engine/shared.js
|
|
27
|
+
- engine/spawn-agent.js
|
|
28
|
+
|
|
29
|
+
### Dashboard
|
|
30
|
+
- dashboard.js
|
|
31
|
+
- dashboard/js/command-center.js
|
|
32
|
+
- dashboard/js/live-stream.js
|
|
33
|
+
- dashboard/js/modal-qa.js
|
|
34
|
+
- dashboard/js/refresh.js
|
|
35
|
+
- dashboard/js/render-inbox.js
|
|
36
|
+
- dashboard/js/render-kb.js
|
|
37
|
+
- dashboard/js/render-plans.js
|
|
38
|
+
- dashboard/js/render-prs.js
|
|
39
|
+
- dashboard/js/render-work-items.js
|
|
40
|
+
- dashboard/js/settings.js
|
|
41
|
+
|
|
3
42
|
## 0.1.58 (2026-03-30)
|
|
4
43
|
|
|
5
44
|
### Engine
|
|
@@ -44,7 +44,7 @@ function ccSaveState() {
|
|
|
44
44
|
// Keep last 30 messages for display
|
|
45
45
|
const toSave = _ccMessages.slice(-30);
|
|
46
46
|
localStorage.setItem('cc-messages', JSON.stringify(toSave));
|
|
47
|
-
} catch {
|
|
47
|
+
} catch { /* localStorage might be full */ }
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function ccUpdateSessionIndicator() {
|
|
@@ -58,7 +58,7 @@ function renderLiveChatMessage(raw) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
continue;
|
|
61
|
-
} catch {}
|
|
61
|
+
} catch { /* JSON parse fallback */ }
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Fallback: raw text (stderr, non-JSON lines)
|
|
@@ -88,7 +88,7 @@ function startLiveStream(agentId) {
|
|
|
88
88
|
try {
|
|
89
89
|
const chunk = JSON.parse(e.data);
|
|
90
90
|
renderLiveChatMessage(chunk);
|
|
91
|
-
} catch {}
|
|
91
|
+
} catch (e) { console.error('live-stream:', e.message); }
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
liveEventSource.addEventListener('done', function() {
|
|
@@ -137,7 +137,7 @@ async function refreshLiveOutput() {
|
|
|
137
137
|
renderLiveChatMessage(text);
|
|
138
138
|
if (wasAtBottom) el.scrollTop = el.scrollHeight;
|
|
139
139
|
}
|
|
140
|
-
} catch {}
|
|
140
|
+
} catch (e) { console.error('live-stream reload:', e.message); }
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
async function sendSteering() {
|
package/dashboard/js/modal-qa.js
CHANGED
|
@@ -16,7 +16,7 @@ const _qaSessions = new Map(); // persist conversations across modal open/close
|
|
|
16
16
|
try {
|
|
17
17
|
const saved = JSON.parse(localStorage.getItem('qa-sessions') || '{}');
|
|
18
18
|
for (const [k, v] of Object.entries(saved)) _qaSessions.set(k, v);
|
|
19
|
-
} catch {}
|
|
19
|
+
} catch { /* optional */ }
|
|
20
20
|
function _saveQaSessions() {
|
|
21
21
|
try {
|
|
22
22
|
const obj = {};
|
|
@@ -24,7 +24,7 @@ function _saveQaSessions() {
|
|
|
24
24
|
const entries = [..._qaSessions.entries()].slice(-10);
|
|
25
25
|
for (const [k, v] of entries) obj[k] = { ...v, threadHtml: (v.threadHtml || '').slice(0, 50000) };
|
|
26
26
|
localStorage.setItem('qa-sessions', JSON.stringify(obj));
|
|
27
|
-
} catch {}
|
|
27
|
+
} catch { /* localStorage might be full */ }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function modalAskAboutSelection() {
|
|
@@ -293,13 +293,13 @@ async function qaReplacePrd(planFile) {
|
|
|
293
293
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
294
294
|
body: JSON.stringify({ file: existingPrd.file })
|
|
295
295
|
});
|
|
296
|
-
} catch {}
|
|
296
|
+
} catch (e) { console.error('plan pause:', e.message); }
|
|
297
297
|
try {
|
|
298
298
|
await fetch('/api/plans/regenerate', {
|
|
299
299
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
300
300
|
body: JSON.stringify({ source: existingPrd.file })
|
|
301
301
|
});
|
|
302
|
-
} catch {}
|
|
302
|
+
} catch (e) { console.error('plan regenerate:', e.message); }
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
planExecute(planFile, project, null);
|
package/dashboard/js/refresh.js
CHANGED
|
@@ -61,7 +61,7 @@ let _statusStream = null;
|
|
|
61
61
|
try {
|
|
62
62
|
_statusStream = new EventSource('/api/status-stream');
|
|
63
63
|
_statusStream.onmessage = (e) => {
|
|
64
|
-
try { _processStatusUpdate(JSON.parse(e.data)); } catch {}
|
|
64
|
+
try { _processStatusUpdate(JSON.parse(e.data)); } catch (e2) { console.error('status-stream:', e2.message); }
|
|
65
65
|
};
|
|
66
66
|
_statusStream.onerror = () => {
|
|
67
67
|
// Fall back to polling
|
|
@@ -82,4 +82,4 @@ switchPage(currentPage);
|
|
|
82
82
|
try {
|
|
83
83
|
const _hotReload = new EventSource('/api/hot-reload');
|
|
84
84
|
_hotReload.onmessage = (e) => { if (e.data === 'reload') location.reload(); };
|
|
85
|
-
} catch {}
|
|
85
|
+
} catch { /* expected */ }
|
|
@@ -142,7 +142,7 @@ async function openInboxInExplorer(name) {
|
|
|
142
142
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
143
143
|
body: JSON.stringify({ name })
|
|
144
144
|
});
|
|
145
|
-
} catch {}
|
|
145
|
+
} catch (e) { console.error('inbox open:', e.message); }
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
function openQuickNoteModal() {
|
|
@@ -174,9 +174,9 @@ async function submitQuickNote() {
|
|
|
174
174
|
body: JSON.stringify({ title: title || 'Quick note', what: content || title })
|
|
175
175
|
});
|
|
176
176
|
if (res.ok) {
|
|
177
|
-
try { closeModal(); } catch {}
|
|
177
|
+
try { closeModal(); } catch { /* expected */ }
|
|
178
178
|
refresh();
|
|
179
|
-
try { showToast('cmd-toast', 'Note saved to inbox', true); } catch {}
|
|
179
|
+
try { showToast('cmd-toast', 'Note saved to inbox', true); } catch { /* expected */ }
|
|
180
180
|
}
|
|
181
181
|
else { const d = await res.json().catch(() => ({})); alert('Error: ' + (d.error || 'unknown')); }
|
|
182
182
|
} catch (e) { alert('Error saving note: ' + e.message); }
|
|
@@ -36,10 +36,10 @@ async function _submitCreatePlan() {
|
|
|
36
36
|
});
|
|
37
37
|
const data = await res.json();
|
|
38
38
|
if (res.ok) {
|
|
39
|
-
try { closeModal(); } catch {}
|
|
39
|
+
try { closeModal(); } catch { /* expected */ }
|
|
40
40
|
refreshPlans();
|
|
41
41
|
refresh();
|
|
42
|
-
try { showToast('cmd-toast', 'Plan "' + data.file + '" created — click Execute to convert to PRD', true); } catch {}
|
|
42
|
+
try { showToast('cmd-toast', 'Plan "' + data.file + '" created — click Execute to convert to PRD', true); } catch { /* expected */ }
|
|
43
43
|
} else {
|
|
44
44
|
alert('Failed: ' + (data.error || 'unknown'));
|
|
45
45
|
}
|
|
@@ -50,7 +50,7 @@ async function refreshPlans() {
|
|
|
50
50
|
try {
|
|
51
51
|
const plans = await fetch('/api/plans').then(r => r.json());
|
|
52
52
|
renderPlans(plans);
|
|
53
|
-
} catch {}
|
|
53
|
+
} catch (e) { console.error('plans refresh:', e.message); }
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -135,9 +135,9 @@ async function _submitLinkPr() {
|
|
|
135
135
|
});
|
|
136
136
|
const data = await res.json();
|
|
137
137
|
if (res.ok) {
|
|
138
|
-
try { closeModal(); } catch {}
|
|
138
|
+
try { closeModal(); } catch { /* expected */ }
|
|
139
139
|
refresh();
|
|
140
|
-
try { showToast('cmd-toast', 'PR ' + (data.id || '') + ' linked' + (autoObserve ? ' (auto-observe on)' : ''), true); } catch {}
|
|
140
|
+
try { showToast('cmd-toast', 'PR ' + (data.id || '') + ' linked' + (autoObserve ? ' (auto-observe on)' : ''), true); } catch { /* expected */ }
|
|
141
141
|
} else {
|
|
142
142
|
alert('Failed: ' + (data.error || 'unknown'));
|
|
143
143
|
}
|
|
@@ -341,10 +341,10 @@ async function _submitCreateWorkItem() {
|
|
|
341
341
|
});
|
|
342
342
|
const data = await res.json();
|
|
343
343
|
if (res.ok) {
|
|
344
|
-
try { closeModal(); } catch {}
|
|
344
|
+
try { closeModal(); } catch { /* expected */ }
|
|
345
345
|
wakeEngine();
|
|
346
346
|
refresh();
|
|
347
|
-
try { showToast('cmd-toast', 'Work item ' + (data.id || '') + ' created', true); } catch {}
|
|
347
|
+
try { showToast('cmd-toast', 'Work item ' + (data.id || '') + ' created', true); } catch { /* expected */ }
|
|
348
348
|
} else {
|
|
349
349
|
alert('Failed: ' + (data.error || 'unknown'));
|
|
350
350
|
}
|
package/dashboard/js/settings.js
CHANGED
|
@@ -147,7 +147,7 @@ async function addProject() {
|
|
|
147
147
|
});
|
|
148
148
|
const addData = await addRes.json();
|
|
149
149
|
if (!addRes.ok) { alert('Failed: ' + (addData.error || 'unknown')); return; }
|
|
150
|
-
try { showToast('cmd-toast', 'Project "' + addData.name + '" added — restart engine to pick it up', true); } catch {}
|
|
150
|
+
try { showToast('cmd-toast', 'Project "' + addData.name + '" added — restart engine to pick it up', true); } catch { /* expected */ }
|
|
151
151
|
refresh();
|
|
152
152
|
} catch (e) { alert('Error: ' + e.message); }
|
|
153
153
|
}
|
package/dashboard.js
CHANGED
|
@@ -124,11 +124,11 @@ if (fs.existsSync(dashDir)) {
|
|
|
124
124
|
_reloadTimer = setTimeout(rebuildDashboardHtml, 300); // debounce 300ms
|
|
125
125
|
};
|
|
126
126
|
// Watch top-level files (styles.css, layout.html)
|
|
127
|
-
try { fs.watch(dashDir, scheduleReload); } catch {}
|
|
127
|
+
try { fs.watch(dashDir, scheduleReload); } catch { /* optional */ }
|
|
128
128
|
// Watch subdirectories (pages/, js/)
|
|
129
129
|
for (const sub of ['pages', 'js']) {
|
|
130
130
|
const subDir = path.join(dashDir, sub);
|
|
131
|
-
if (fs.existsSync(subDir)) try { fs.watch(subDir, scheduleReload); } catch {}
|
|
131
|
+
if (fs.existsSync(subDir)) try { fs.watch(subDir, scheduleReload); } catch { /* optional */ }
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -145,7 +145,7 @@ function getVerifyGuides() {
|
|
|
145
145
|
const planFile = planSlug + '.json';
|
|
146
146
|
guides.push({ file: f, planFile });
|
|
147
147
|
}
|
|
148
|
-
} catch {}
|
|
148
|
+
} catch (e) { console.error('getVerifyGuides:', e.message); }
|
|
149
149
|
return guides;
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -273,7 +273,7 @@ try {
|
|
|
273
273
|
const age = Date.now() - new Date(saved.lastActiveAt || 0).getTime();
|
|
274
274
|
if (age < CC_SESSION_EXPIRY_MS) ccSession = saved;
|
|
275
275
|
}
|
|
276
|
-
} catch {}
|
|
276
|
+
} catch { /* optional */ }
|
|
277
277
|
|
|
278
278
|
// Static system prompt — baked into session on creation, never changes
|
|
279
279
|
const CC_STATIC_SYSTEM_PROMPT = `You are the Command Center AI for a software engineering minions called "Minions."
|
|
@@ -459,7 +459,7 @@ try {
|
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
|
-
} catch {}
|
|
462
|
+
} catch { /* optional */ }
|
|
463
463
|
|
|
464
464
|
function persistDocSessions() {
|
|
465
465
|
const obj = {};
|
|
@@ -687,12 +687,12 @@ function cleanDispatchEntries(matchFn) {
|
|
|
687
687
|
try {
|
|
688
688
|
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim());
|
|
689
689
|
if (pid) process.kill(pid, 'SIGTERM');
|
|
690
|
-
} catch {}
|
|
691
|
-
try { fs.unlinkSync(pidFile); } catch {}
|
|
690
|
+
} catch { /* process may be dead */ }
|
|
691
|
+
try { fs.unlinkSync(pidFile); } catch { /* cleanup */ }
|
|
692
692
|
// Clean up temp prompt files
|
|
693
|
-
try { fs.unlinkSync(path.join(engineDir, 'tmp', `prompt-${d.id}.md`)); } catch {}
|
|
694
|
-
try { fs.unlinkSync(path.join(engineDir, 'tmp', `sysprompt-${d.id}.md`)); } catch {}
|
|
695
|
-
try { fs.unlinkSync(path.join(engineDir, 'tmp', `sysprompt-${d.id}.md.tmp`)); } catch {}
|
|
693
|
+
try { fs.unlinkSync(path.join(engineDir, 'tmp', `prompt-${d.id}.md`)); } catch { /* cleanup */ }
|
|
694
|
+
try { fs.unlinkSync(path.join(engineDir, 'tmp', `sysprompt-${d.id}.md`)); } catch { /* cleanup */ }
|
|
695
|
+
try { fs.unlinkSync(path.join(engineDir, 'tmp', `sysprompt-${d.id}.md.tmp`)); } catch { /* cleanup */ }
|
|
696
696
|
}
|
|
697
697
|
}
|
|
698
698
|
dispatch[queue] = dispatch[queue].filter(d => !matchFn(d));
|
|
@@ -729,7 +729,7 @@ function killEnginePid(pid) {
|
|
|
729
729
|
} else {
|
|
730
730
|
process.kill(pid, 'SIGKILL');
|
|
731
731
|
}
|
|
732
|
-
} catch {}
|
|
732
|
+
} catch { /* process may be dead */ }
|
|
733
733
|
}
|
|
734
734
|
|
|
735
735
|
function restartEngine() {
|
|
@@ -846,7 +846,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
846
846
|
dispatch.completed = dispatch.completed.filter(d => !d.meta?.parentKey || d.meta.parentKey !== dispatchKey);
|
|
847
847
|
return dispatch;
|
|
848
848
|
}, { defaultValue: { pending: [], active: [], completed: [] } });
|
|
849
|
-
} catch {}
|
|
849
|
+
} catch (e) { console.error('dispatch cleanup:', e.message); }
|
|
850
850
|
|
|
851
851
|
// Clear cooldown so item isn't blocked by exponential backoff
|
|
852
852
|
try {
|
|
@@ -856,7 +856,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
856
856
|
delete cooldowns[dispatchKey];
|
|
857
857
|
safeWrite(cooldownPath, cooldowns);
|
|
858
858
|
}
|
|
859
|
-
} catch {}
|
|
859
|
+
} catch (e) { console.error('cooldown cleanup:', e.message); }
|
|
860
860
|
|
|
861
861
|
return jsonReply(res, 200, { ok: true, id });
|
|
862
862
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
@@ -905,7 +905,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
905
905
|
if (key.includes(id)) { delete cooldowns[key]; cleaned = true; }
|
|
906
906
|
}
|
|
907
907
|
if (cleaned) safeWrite(cooldownPath, cooldowns);
|
|
908
|
-
} catch {}
|
|
908
|
+
} catch (e) { console.error('cooldown cleanup:', e.message); }
|
|
909
909
|
|
|
910
910
|
invalidateStatusCache();
|
|
911
911
|
return jsonReply(res, 200, { ok: true, id, dispatchRemoved });
|
|
@@ -1164,7 +1164,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1164
1164
|
safeWrite(wiPath, items);
|
|
1165
1165
|
workItemSynced = true;
|
|
1166
1166
|
}
|
|
1167
|
-
} catch {}
|
|
1167
|
+
} catch (e) { console.error('work item sync:', e.message); }
|
|
1168
1168
|
}
|
|
1169
1169
|
|
|
1170
1170
|
return jsonReply(res, 200, { ok: true, item, workItemSynced });
|
|
@@ -1196,7 +1196,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1196
1196
|
safeWrite(wiPath, filtered);
|
|
1197
1197
|
cancelled = true;
|
|
1198
1198
|
}
|
|
1199
|
-
} catch {}
|
|
1199
|
+
} catch (e) { console.error('work item cleanup:', e.message); }
|
|
1200
1200
|
}
|
|
1201
1201
|
// Also check central work-items
|
|
1202
1202
|
const centralPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
@@ -1205,7 +1205,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1205
1205
|
const before = items.length;
|
|
1206
1206
|
const filtered = items.filter(w => !(w.sourcePlan === body.source && w.id === body.itemId));
|
|
1207
1207
|
if (filtered.length < before) { safeWrite(centralPath, filtered); cancelled = true; }
|
|
1208
|
-
} catch {}
|
|
1208
|
+
} catch (e) { console.error('central work item cleanup:', e.message); }
|
|
1209
1209
|
|
|
1210
1210
|
// Clean dispatch entries for this item
|
|
1211
1211
|
cleanDispatchEntries(d =>
|
|
@@ -1235,16 +1235,16 @@ const server = http.createServer(async (req, res) => {
|
|
|
1235
1235
|
const status = JSON.parse(safeRead(statusPath) || '{}');
|
|
1236
1236
|
if (status.pid) {
|
|
1237
1237
|
if (process.platform === 'win32') {
|
|
1238
|
-
try { require('child_process').execSync('taskkill /PID ' + status.pid + ' /F /T', { stdio: 'pipe', timeout: 5000 }); } catch {}
|
|
1238
|
+
try { require('child_process').execSync('taskkill /PID ' + status.pid + ' /F /T', { stdio: 'pipe', timeout: 5000 }); } catch { /* process may be dead */ }
|
|
1239
1239
|
} else {
|
|
1240
|
-
try { process.kill(status.pid, 'SIGTERM'); } catch {}
|
|
1240
|
+
try { process.kill(status.pid, 'SIGTERM'); } catch { /* process may be dead */ }
|
|
1241
1241
|
}
|
|
1242
1242
|
}
|
|
1243
1243
|
status.status = 'idle';
|
|
1244
1244
|
delete status.currentTask;
|
|
1245
1245
|
delete status.dispatched;
|
|
1246
1246
|
safeWrite(statusPath, status);
|
|
1247
|
-
} catch {}
|
|
1247
|
+
} catch (e) { console.error('agent cancel:', e.message); }
|
|
1248
1248
|
|
|
1249
1249
|
cancelled.push({ agent: d.agent, task: d.task });
|
|
1250
1250
|
}
|
|
@@ -1291,7 +1291,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1291
1291
|
res.write(`data: ${JSON.stringify(content)}\n\n`);
|
|
1292
1292
|
offset = Buffer.byteLength(content, 'utf8');
|
|
1293
1293
|
}
|
|
1294
|
-
} catch {}
|
|
1294
|
+
} catch { /* optional */ }
|
|
1295
1295
|
|
|
1296
1296
|
// Watch for changes using fs.watchFile (cross-platform, works on Windows)
|
|
1297
1297
|
const watcher = () => {
|
|
@@ -1306,7 +1306,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1306
1306
|
const chunk = buf.toString('utf8');
|
|
1307
1307
|
if (chunk) res.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
1308
1308
|
}
|
|
1309
|
-
} catch {}
|
|
1309
|
+
} catch { /* optional */ }
|
|
1310
1310
|
};
|
|
1311
1311
|
|
|
1312
1312
|
fs.watchFile(liveLogPath, { interval: 500 }, watcher);
|
|
@@ -1497,7 +1497,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1497
1497
|
const meta = `<!-- swept: ${new Date().toISOString()} | reason: ${reason} -->\n`;
|
|
1498
1498
|
safeWrite(destPath, meta + content);
|
|
1499
1499
|
safeUnlink(filePath);
|
|
1500
|
-
} catch {}
|
|
1500
|
+
} catch (e) { console.error('kb archive:', e.message); }
|
|
1501
1501
|
}
|
|
1502
1502
|
|
|
1503
1503
|
// Process removals (stale/empty) — archive, not delete
|
|
@@ -1534,7 +1534,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1534
1534
|
safeWrite(path.join(destDir, entry.file), updated);
|
|
1535
1535
|
safeUnlink(srcPath);
|
|
1536
1536
|
reclassified++;
|
|
1537
|
-
} catch {}
|
|
1537
|
+
} catch (e) { console.error('kb reclassify:', e.message); }
|
|
1538
1538
|
}
|
|
1539
1539
|
|
|
1540
1540
|
// Prune swept files older than 30 days
|
|
@@ -1545,9 +1545,9 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1545
1545
|
const fp = path.join(kbArchiveDir, f);
|
|
1546
1546
|
try {
|
|
1547
1547
|
if (Date.now() - fs.statSync(fp).mtimeMs > SWEPT_RETENTION_MS) { safeUnlink(fp); pruned++; }
|
|
1548
|
-
} catch {}
|
|
1548
|
+
} catch { /* cleanup */ }
|
|
1549
1549
|
}
|
|
1550
|
-
} catch {}
|
|
1550
|
+
} catch { /* optional */ }
|
|
1551
1551
|
|
|
1552
1552
|
const summary = `${merged} duplicates merged, ${removed} stale removed, ${reclassified} reclassified${pruned ? ', ' + pruned + ' old swept files pruned' : ''}`;
|
|
1553
1553
|
safeWrite(path.join(ENGINE_DIR, 'kb-swept.json'), JSON.stringify({ timestamp: new Date().toISOString(), summary }));
|
|
@@ -1575,7 +1575,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1575
1575
|
const filePath = path.join(dir, f);
|
|
1576
1576
|
const content = safeRead(filePath) || '';
|
|
1577
1577
|
let updatedAt = '';
|
|
1578
|
-
try { updatedAt = new Date(fs.statSync(filePath).mtimeMs).toISOString(); } catch {}
|
|
1578
|
+
try { updatedAt = new Date(fs.statSync(filePath).mtimeMs).toISOString(); } catch { /* optional */ }
|
|
1579
1579
|
const isJson = f.endsWith('.json');
|
|
1580
1580
|
if (isJson) {
|
|
1581
1581
|
try {
|
|
@@ -1597,7 +1597,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1597
1597
|
revisionFeedback: plan.revision_feedback || null,
|
|
1598
1598
|
sourcePlan: plan.source_plan || null,
|
|
1599
1599
|
});
|
|
1600
|
-
} catch {}
|
|
1600
|
+
} catch { /* JSON parse fallback */ }
|
|
1601
1601
|
} else {
|
|
1602
1602
|
const titleMatch = content.match(/^#\s+(?:Plan:\s*)?(.+)/m);
|
|
1603
1603
|
const projectMatch = content.match(/\*\*Project:\*\*\s*(.+)/m);
|
|
@@ -1655,7 +1655,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1655
1655
|
if (!content) return jsonReply(res, 404, { error: 'not found' });
|
|
1656
1656
|
// Find the actual file path for Last-Modified header + expose resolved relative path
|
|
1657
1657
|
const planCandidates = [resolvePlanPath(file), path.join(PRD_DIR, file), path.join(PRD_DIR, 'guides', file), path.join(PLANS_DIR, file), path.join(PRD_DIR, 'archive', file), path.join(PLANS_DIR, 'archive', file)];
|
|
1658
|
-
for (const p of planCandidates) { try { const st = fs.statSync(p); if (st) { res.setHeader('Last-Modified', st.mtime.toISOString()); res.setHeader('X-Resolved-Path', path.relative(MINIONS_DIR, p).replace(/\\/g, '/')); break; } } catch {} }
|
|
1658
|
+
for (const p of planCandidates) { try { const st = fs.statSync(p); if (st) { res.setHeader('Last-Modified', st.mtime.toISOString()); res.setHeader('X-Resolved-Path', path.relative(MINIONS_DIR, p).replace(/\\/g, '/')); break; } } catch { /* optional */ } }
|
|
1659
1659
|
const contentType = file.endsWith('.json') ? 'application/json' : 'text/plain';
|
|
1660
1660
|
res.setHeader('Content-Type', contentType + '; charset=utf-8');
|
|
1661
1661
|
res.setHeader('Cache-Control', 'no-cache');
|
|
@@ -1699,7 +1699,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1699
1699
|
}
|
|
1700
1700
|
}
|
|
1701
1701
|
if (changed) safeWrite(wiPath, items);
|
|
1702
|
-
} catch {}
|
|
1702
|
+
} catch (e) { console.error('resume work items:', e.message); }
|
|
1703
1703
|
}
|
|
1704
1704
|
|
|
1705
1705
|
// Clear dispatch completed entries for resumed items so they aren't dedup-blocked
|
|
@@ -1759,16 +1759,16 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1759
1759
|
const agentStatus = JSON.parse(safeRead(statusPath) || '{}');
|
|
1760
1760
|
if (agentStatus.pid) {
|
|
1761
1761
|
if (process.platform === 'win32') {
|
|
1762
|
-
try { require('child_process').execSync('taskkill /PID ' + agentStatus.pid + ' /F /T', { stdio: 'pipe', timeout: 5000 }); } catch {}
|
|
1762
|
+
try { require('child_process').execSync('taskkill /PID ' + agentStatus.pid + ' /F /T', { stdio: 'pipe', timeout: 5000 }); } catch { /* process may be dead */ }
|
|
1763
1763
|
} else {
|
|
1764
|
-
try { process.kill(agentStatus.pid, 'SIGTERM'); } catch {}
|
|
1764
|
+
try { process.kill(agentStatus.pid, 'SIGTERM'); } catch { /* process may be dead */ }
|
|
1765
1765
|
}
|
|
1766
1766
|
}
|
|
1767
1767
|
agentStatus.status = 'idle';
|
|
1768
1768
|
delete agentStatus.currentTask;
|
|
1769
1769
|
delete agentStatus.dispatched;
|
|
1770
1770
|
safeWrite(statusPath, agentStatus);
|
|
1771
|
-
} catch {}
|
|
1771
|
+
} catch (e) { console.error('agent reset:', e.message); }
|
|
1772
1772
|
killedAgents.add(activeEntry.agent);
|
|
1773
1773
|
}
|
|
1774
1774
|
}
|
|
@@ -1785,7 +1785,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1785
1785
|
if (w.id) resetItemIds.add(w.id);
|
|
1786
1786
|
}
|
|
1787
1787
|
if (changed) safeWrite(wiPath, items);
|
|
1788
|
-
} catch {}
|
|
1788
|
+
} catch (e) { console.error('reset work items:', e.message); }
|
|
1789
1789
|
}
|
|
1790
1790
|
|
|
1791
1791
|
// Remove dispatch active entries for reset items or killed agents.
|
|
@@ -1842,7 +1842,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1842
1842
|
}
|
|
1843
1843
|
|
|
1844
1844
|
// Delete old PRD — agent will write replacement at same path
|
|
1845
|
-
try { fs.unlinkSync(prdPath); } catch {}
|
|
1845
|
+
try { fs.unlinkSync(prdPath); } catch { /* cleanup */ }
|
|
1846
1846
|
|
|
1847
1847
|
// Queue plan-to-prd regeneration with instructions to preserve completed items
|
|
1848
1848
|
const wiPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
@@ -1963,7 +1963,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
1963
1963
|
if (filtered.length < items.length) {
|
|
1964
1964
|
safeWrite(wiInfo.path, filtered);
|
|
1965
1965
|
}
|
|
1966
|
-
} catch {}
|
|
1966
|
+
} catch (e) { console.error('work item sync:', e.message); }
|
|
1967
1967
|
}
|
|
1968
1968
|
|
|
1969
1969
|
// Count plan items that have no work item yet (will auto-materialize)
|
|
@@ -2012,7 +2012,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2012
2012
|
cleaned += items.length - filtered.length;
|
|
2013
2013
|
safeWrite(wiPath, filtered);
|
|
2014
2014
|
}
|
|
2015
|
-
} catch {}
|
|
2015
|
+
} catch (e) { console.error('plan cleanup:', e.message); }
|
|
2016
2016
|
}
|
|
2017
2017
|
|
|
2018
2018
|
// Clean up dispatch entries for this plan's items
|
|
@@ -2036,7 +2036,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2036
2036
|
}
|
|
2037
2037
|
}
|
|
2038
2038
|
if (changed) safeWrite(centralPath, centralItems);
|
|
2039
|
-
} catch {}
|
|
2039
|
+
} catch (e) { console.error('plan-to-prd cleanup:', e.message); }
|
|
2040
2040
|
}
|
|
2041
2041
|
|
|
2042
2042
|
invalidateStatusCache();
|
|
@@ -2177,7 +2177,7 @@ If nothing to do, return: { "duplicates": [], "reclassify": [], "remove": [] }`;
|
|
|
2177
2177
|
}
|
|
2178
2178
|
}
|
|
2179
2179
|
if (filtered.length < items.length) safeWrite(wiInfo.path, filtered);
|
|
2180
|
-
} catch {}
|
|
2180
|
+
} catch (e) { console.error('work item deletion:', e.message); }
|
|
2181
2181
|
}
|
|
2182
2182
|
for (const itemId of deletedItemIds) {
|
|
2183
2183
|
cleanDispatchEntries(d =>
|
|
@@ -2385,7 +2385,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2385
2385
|
// Move to archive
|
|
2386
2386
|
const archiveDir = path.join(MINIONS_DIR, 'notes', 'archive');
|
|
2387
2387
|
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
2388
|
-
try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `persisted-${name}`), _c); safeUnlink(inboxPath); } catch {}
|
|
2388
|
+
try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `persisted-${name}`), _c); safeUnlink(inboxPath); } catch (e) { console.error('inbox archive:', e.message); }
|
|
2389
2389
|
|
|
2390
2390
|
return jsonReply(res, 200, { ok: true, title });
|
|
2391
2391
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
@@ -2423,7 +2423,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2423
2423
|
// Move inbox item to archive
|
|
2424
2424
|
const archiveDir = path.join(MINIONS_DIR, 'notes', 'archive');
|
|
2425
2425
|
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
2426
|
-
try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `kb-${category}-${name}`), _c); safeUnlink(inboxPath); } catch {}
|
|
2426
|
+
try { const _c = safeRead(inboxPath); safeWrite(path.join(archiveDir, `kb-${category}-${name}`), _c); safeUnlink(inboxPath); } catch (e) { console.error('inbox archive:', e.message); }
|
|
2427
2427
|
|
|
2428
2428
|
return jsonReply(res, 200, { ok: true, category, file: name });
|
|
2429
2429
|
} catch (e) { return jsonReply(res, 400, { error: e.message }); }
|
|
@@ -2526,7 +2526,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2526
2526
|
fs.writeFileSync(psPath, psScript);
|
|
2527
2527
|
try {
|
|
2528
2528
|
selectedPath = execSync(`powershell -STA -NoProfile -ExecutionPolicy Bypass -File "${psPath}"`, { encoding: 'utf8', timeout: 120000 }).trim();
|
|
2529
|
-
} finally { try { fs.unlinkSync(psPath); } catch {} }
|
|
2529
|
+
} finally { try { fs.unlinkSync(psPath); } catch { /* cleanup */ } }
|
|
2530
2530
|
} else if (process.platform === 'darwin') {
|
|
2531
2531
|
selectedPath = execSync(`osascript -e 'POSIX path of (choose folder with prompt "Select project folder")'`, { encoding: 'utf8', timeout: 120000 }).trim();
|
|
2532
2532
|
} else {
|
|
@@ -2572,14 +2572,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2572
2572
|
remoteUrl.match(/https:\/\/dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/\s]+)/);
|
|
2573
2573
|
if (m) { detected.org = m[1]; detected.project = m[2]; detected.repoName = m[3]; }
|
|
2574
2574
|
}
|
|
2575
|
-
} catch {}
|
|
2575
|
+
} catch (e) { console.error('git remote detection:', e.message); }
|
|
2576
2576
|
try {
|
|
2577
2577
|
const pkgPath = path.join(target, 'package.json');
|
|
2578
2578
|
if (fs.existsSync(pkgPath)) {
|
|
2579
2579
|
const pkg = safeJson(pkgPath);
|
|
2580
2580
|
if (pkg.name) detected.name = pkg.name.replace(/^@[^/]+\//, '');
|
|
2581
2581
|
}
|
|
2582
|
-
} catch {}
|
|
2582
|
+
} catch { /* optional */ }
|
|
2583
2583
|
let description = '';
|
|
2584
2584
|
try {
|
|
2585
2585
|
const claudeMd = path.join(target, 'CLAUDE.md');
|
|
@@ -2587,7 +2587,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
2587
2587
|
const lines = (safeRead(claudeMd) || '').split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
2588
2588
|
if (lines[0] && lines[0].length < 200) description = lines[0].trim();
|
|
2589
2589
|
}
|
|
2590
|
-
} catch {}
|
|
2590
|
+
} catch { /* optional */ }
|
|
2591
2591
|
|
|
2592
2592
|
const name = body.name || detected.name;
|
|
2593
2593
|
const prUrlBase = detected.repoHost === 'github'
|
|
@@ -3042,7 +3042,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
3042
3042
|
|
|
3043
3043
|
// Also append to live-output.log so it shows in the chat view
|
|
3044
3044
|
const liveLogPath = path.join(agentDir, 'live-output.log');
|
|
3045
|
-
try { fs.appendFileSync(liveLogPath, '\n[human-steering] ' + message + '\n'); } catch {}
|
|
3045
|
+
try { fs.appendFileSync(liveLogPath, '\n[human-steering] ' + message + '\n'); } catch { /* optional */ }
|
|
3046
3046
|
|
|
3047
3047
|
return jsonReply(res, 200, { ok: true, message: 'Steering message sent' });
|
|
3048
3048
|
}},
|
package/engine/ado.js
CHANGED
|
@@ -91,7 +91,7 @@ async function forEachActivePr(config, token, callback) {
|
|
|
91
91
|
const updated = await callback(project, pr, prNum, orgBase);
|
|
92
92
|
if (updated) projectUpdated++;
|
|
93
93
|
} catch (err) {
|
|
94
|
-
try { engine().log('warn', `Failed to poll status for ${pr.id}: ${err.message}`); } catch {}
|
|
94
|
+
try { engine().log('warn', `Failed to poll status for ${pr.id}: ${err.message}`); } catch { /* engine not available */ }
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -160,7 +160,7 @@ async function pollPrStatus(config) {
|
|
|
160
160
|
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
161
161
|
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
162
162
|
shared.safeWrite(metricsPath, metrics);
|
|
163
|
-
} catch {}
|
|
163
|
+
} catch (err) { try { engine().log('warn', `Metrics update: ${err.message}`); } catch { /* engine not available */ } }
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
}
|