@yemi33/minions 0.1.57 → 0.1.59
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 +87 -58
- package/engine/ado.js +2 -2
- package/engine/cli.js +16 -16
- package/engine/consolidation.js +4 -4
- package/engine/github.js +3 -3
- package/engine/lifecycle.js +14 -14
- package/engine/llm.js +2 -2
- package/engine/preflight.js +2 -2
- package/engine/queries.js +13 -13
- package/engine/shared.js +6 -6
- package/engine/spawn-agent.js +3 -3
- package/engine.js +101 -101
- package/package.json +1 -1
package/engine/cli.js
CHANGED
|
@@ -56,7 +56,7 @@ const commands = {
|
|
|
56
56
|
printPreflight(results, { label: 'Preflight checks' });
|
|
57
57
|
console.log(' Some checks failed — agents may not work. Run `minions doctor` for details.\n');
|
|
58
58
|
}
|
|
59
|
-
} catch {}
|
|
59
|
+
} catch (e) { console.error('preflight:', e.message); }
|
|
60
60
|
|
|
61
61
|
const e = engine();
|
|
62
62
|
const control = getControl();
|
|
@@ -74,7 +74,7 @@ const commands = {
|
|
|
74
74
|
process.kill(control.pid, 0);
|
|
75
75
|
alive = true;
|
|
76
76
|
}
|
|
77
|
-
} catch {}
|
|
77
|
+
} catch { /* process may be dead */ }
|
|
78
78
|
}
|
|
79
79
|
if (alive) {
|
|
80
80
|
console.log(`Engine is already running (PID ${control.pid}).`);
|
|
@@ -119,7 +119,7 @@ const commands = {
|
|
|
119
119
|
try {
|
|
120
120
|
const pidStr = fs.readFileSync(pidFile, 'utf8').trim();
|
|
121
121
|
if (pidStr) agentPid = parseInt(pidStr);
|
|
122
|
-
} catch {}
|
|
122
|
+
} catch { /* optional */ }
|
|
123
123
|
|
|
124
124
|
if (!agentPid) {
|
|
125
125
|
const status = getAgentStatus(agentId);
|
|
@@ -131,7 +131,7 @@ const commands = {
|
|
|
131
131
|
if (ageMs < 300000) {
|
|
132
132
|
agentPid = -1;
|
|
133
133
|
}
|
|
134
|
-
} catch {}
|
|
134
|
+
} catch { /* optional */ }
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -213,7 +213,7 @@ const commands = {
|
|
|
213
213
|
let prsCreated = 0;
|
|
214
214
|
try {
|
|
215
215
|
prsCreated = lifecycle.syncPrsFromOutput(output, agentId, item.meta, config);
|
|
216
|
-
} catch {}
|
|
216
|
+
} catch (err) { e.log('warn', `Orphan PR sync: ${err.message}`); }
|
|
217
217
|
|
|
218
218
|
// Update work item status
|
|
219
219
|
if (item.meta?.item?.id) {
|
|
@@ -235,7 +235,7 @@ const commands = {
|
|
|
235
235
|
safeWrite(wiPath, items);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
-
} catch {}
|
|
238
|
+
} catch (err) { e.log('warn', `Orphan WI fallback: ${err.message}`); }
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
@@ -248,16 +248,16 @@ const commands = {
|
|
|
248
248
|
'',
|
|
249
249
|
{ processWorkItemFailure: false }
|
|
250
250
|
);
|
|
251
|
-
} catch {}
|
|
251
|
+
} catch (err) { e.log('warn', `Orphan dispatch complete: ${err.message}`); }
|
|
252
252
|
|
|
253
253
|
// Check plan completion
|
|
254
254
|
if (isSuccess && item.meta?.item?.sourcePlan) {
|
|
255
|
-
try { lifecycle.checkPlanCompletion(item.meta, config); } catch {}
|
|
255
|
+
try { lifecycle.checkPlanCompletion(item.meta, config); } catch (err) { e.log('warn', `Orphan plan completion: ${err.message}`); }
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
recovered++;
|
|
259
259
|
console.log(` ✓ Recovered ${agentId}: ${(item.task || '').slice(0, 60)} → ${result}${prsCreated ? ' (' + prsCreated + ' PR)' : ''}`);
|
|
260
|
-
} catch {}
|
|
260
|
+
} catch (err) { e.log('warn', `Orphan recovery: ${err.message}`); }
|
|
261
261
|
}
|
|
262
262
|
if (recovered > 0) {
|
|
263
263
|
e.log('info', `Orphan recovery: processed ${recovered} completion(s) from previous session`);
|
|
@@ -291,7 +291,7 @@ const commands = {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
if (changed) safeWrite(wiPath, items);
|
|
294
|
-
} catch {}
|
|
294
|
+
} catch (err) { e.log('warn', `Recovery WI reset: ${err.message}`); }
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
// Plan chain recovery removed — plans require explicit user execution via dashboard
|
|
@@ -338,7 +338,7 @@ const commands = {
|
|
|
338
338
|
for (const f of fs.readdirSync(prdDir).filter(f => f.endsWith('.json'))) {
|
|
339
339
|
filesToWatch.push(path.join(prdDir, f));
|
|
340
340
|
}
|
|
341
|
-
} catch {}
|
|
341
|
+
} catch { /* optional */ }
|
|
342
342
|
|
|
343
343
|
for (const filePath of filesToWatch) {
|
|
344
344
|
if (_watchedFiles.has(filePath)) continue;
|
|
@@ -354,7 +354,7 @@ const commands = {
|
|
|
354
354
|
e.tick();
|
|
355
355
|
}, 1000);
|
|
356
356
|
});
|
|
357
|
-
} catch {}
|
|
357
|
+
} catch { /* optional */ }
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
watchForWorkChanges();
|
|
@@ -366,7 +366,7 @@ const commands = {
|
|
|
366
366
|
shuttingDown = true;
|
|
367
367
|
console.log(`\n${signal} received — initiating graceful shutdown...`);
|
|
368
368
|
clearInterval(tickTimer);
|
|
369
|
-
for (const f of _watchedFiles) { try { fs.unwatchFile(f); } catch {} }
|
|
369
|
+
for (const f of _watchedFiles) { try { fs.unwatchFile(f); } catch { /* cleanup */ } }
|
|
370
370
|
safeWrite(CONTROL_PATH, { state: 'stopping', pid: process.pid, stopping_at: e.ts() });
|
|
371
371
|
e.log('info', `Graceful shutdown initiated (${signal})`);
|
|
372
372
|
|
|
@@ -418,7 +418,7 @@ const commands = {
|
|
|
418
418
|
}
|
|
419
419
|
const control = getControl();
|
|
420
420
|
if (control.pid && control.pid !== process.pid) {
|
|
421
|
-
try { process.kill(control.pid); } catch {}
|
|
421
|
+
try { process.kill(control.pid); } catch { /* process may be dead */ }
|
|
422
422
|
}
|
|
423
423
|
safeWrite(CONTROL_PATH, { state: 'stopped', stopped_at: e.ts() });
|
|
424
424
|
e.log('info', 'Engine stopped');
|
|
@@ -459,7 +459,7 @@ const commands = {
|
|
|
459
459
|
try {
|
|
460
460
|
const vFile = path.join(MINIONS_DIR, '.minions-version');
|
|
461
461
|
version = fs.readFileSync(vFile, 'utf8').trim();
|
|
462
|
-
} catch {}
|
|
462
|
+
} catch { /* optional */ }
|
|
463
463
|
|
|
464
464
|
console.log('\n=== Minions Engine ===\n');
|
|
465
465
|
console.log(`Version: ${version}`);
|
|
@@ -476,7 +476,7 @@ const commands = {
|
|
|
476
476
|
process.kill(control.pid, 0);
|
|
477
477
|
engineAlive = true;
|
|
478
478
|
}
|
|
479
|
-
} catch {}
|
|
479
|
+
} catch { /* process may be dead */ }
|
|
480
480
|
}
|
|
481
481
|
if (control.state === 'running' && !engineAlive) {
|
|
482
482
|
console.log(`Engine: stale (PID ${control.pid} is dead) — run: minions start`);
|
package/engine/consolidation.js
CHANGED
|
@@ -167,10 +167,10 @@ function consolidateWithLLM(items, existingNotes, files, config) {
|
|
|
167
167
|
|
|
168
168
|
const timeout = setTimeout(() => {
|
|
169
169
|
e.log('warn', 'LLM consolidation timed out after 3m — killing and falling back to regex');
|
|
170
|
-
try { proc.kill('SIGTERM'); } catch {}
|
|
170
|
+
try { proc.kill('SIGTERM'); } catch { /* process may be dead */ }
|
|
171
171
|
// Escalate to SIGKILL after 10s if process doesn't exit
|
|
172
172
|
setTimeout(() => {
|
|
173
|
-
try { proc.kill('SIGKILL'); } catch {}
|
|
173
|
+
try { proc.kill('SIGKILL'); } catch { /* process may be dead */ }
|
|
174
174
|
if (_consolidationInFlight) {
|
|
175
175
|
_consolidationInFlight = false;
|
|
176
176
|
_processingFiles.clear();
|
|
@@ -399,14 +399,14 @@ function classifyToKnowledgeBase(items) {
|
|
|
399
399
|
if (fs.existsSync(dir)) count += fs.readdirSync(dir).length;
|
|
400
400
|
}
|
|
401
401
|
safeWrite(path.join(ENGINE_DIR, 'kb-checkpoint.json'), JSON.stringify({ count, updatedAt: new Date().toISOString() }));
|
|
402
|
-
} catch {}
|
|
402
|
+
} catch (err) { engine().log('warn', `KB checkpoint: ${err.message}`); }
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
function archiveInboxFiles(files) {
|
|
406
406
|
const e = engine();
|
|
407
407
|
if (!fs.existsSync(ARCHIVE_DIR)) fs.mkdirSync(ARCHIVE_DIR, { recursive: true });
|
|
408
408
|
for (const f of files) {
|
|
409
|
-
try { fs.renameSync(path.join(INBOX_DIR, f), shared.uniquePath(path.join(ARCHIVE_DIR, `${e.dateStamp()}-${f}`))); } catch {}
|
|
409
|
+
try { fs.renameSync(path.join(INBOX_DIR, f), shared.uniquePath(path.join(ARCHIVE_DIR, `${e.dateStamp()}-${f}`))); } catch (err) { e.log('warn', `Inbox archive: ${err.message}`); }
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
412
|
|
package/engine/github.js
CHANGED
|
@@ -66,7 +66,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
66
66
|
const updated = await callback(project, pr, prNum, slug);
|
|
67
67
|
if (updated) projectUpdated++;
|
|
68
68
|
} catch (err) {
|
|
69
|
-
try { engine().log('warn', `GitHub: failed to poll PR ${pr.id}: ${err.message}`); } catch {}
|
|
69
|
+
try { engine().log('warn', `GitHub: failed to poll PR ${pr.id}: ${err.message}`); } catch { /* engine not available */ }
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -101,7 +101,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
101
101
|
centralUpdated++;
|
|
102
102
|
}
|
|
103
103
|
} catch (err) {
|
|
104
|
-
try { engine().log('warn', `GitHub: failed to poll central PR ${pr.id}: ${err.message}`); } catch {}
|
|
104
|
+
try { engine().log('warn', `GitHub: failed to poll central PR ${pr.id}: ${err.message}`); } catch { /* engine not available */ }
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
if (centralUpdated > 0) {
|
|
@@ -171,7 +171,7 @@ async function pollPrStatus(config) {
|
|
|
171
171
|
if (newReviewStatus === 'approved') metrics[authorId].prsApproved = (metrics[authorId].prsApproved || 0) + 1;
|
|
172
172
|
else metrics[authorId].prsRejected = (metrics[authorId].prsRejected || 0) + 1;
|
|
173
173
|
shared.safeWrite(metricsPath, metrics);
|
|
174
|
-
} catch {}
|
|
174
|
+
} catch (err) { try { engine().log('warn', `Metrics update: ${err.message}`); } catch { /* engine not available */ } }
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
package/engine/lifecycle.js
CHANGED
|
@@ -37,7 +37,7 @@ function checkPlanCompletion(meta, config) {
|
|
|
37
37
|
try {
|
|
38
38
|
const wi = safeJson(shared.projectWorkItemsPath(p)) || [];
|
|
39
39
|
allWorkItems = allWorkItems.concat(wi);
|
|
40
|
-
} catch {}
|
|
40
|
+
} catch { /* optional */ }
|
|
41
41
|
}
|
|
42
42
|
const planItems = allWorkItems.filter(w => w.sourcePlan === planFile && w.itemType !== 'pr' && w.itemType !== 'verify');
|
|
43
43
|
if (planItems.length === 0) return;
|
|
@@ -107,7 +107,7 @@ function checkPlanCompletion(meta, config) {
|
|
|
107
107
|
prsCreated.push(pr);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
} catch {}
|
|
110
|
+
} catch { /* optional */ }
|
|
111
111
|
}
|
|
112
112
|
const uniquePrs = [...new Map(prsCreated.map(pr => [pr.id || pr.url, pr])).values()];
|
|
113
113
|
|
|
@@ -281,11 +281,11 @@ function checkPlanCompletion(meta, config) {
|
|
|
281
281
|
try {
|
|
282
282
|
fs.renameSync(path.join(PLANS_DIR, md), path.join(planArchiveDir, md));
|
|
283
283
|
e.log('info', `Archived source plan: plans/archive/${md}`);
|
|
284
|
-
} catch {}
|
|
284
|
+
} catch (err) { e.log('warn', `Failed to archive plan ${md}: ${err.message}`); }
|
|
285
285
|
break;
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
-
} catch {}
|
|
288
|
+
} catch (err) { e.log('warn', `Plan archive scan: ${err.message}`); }
|
|
289
289
|
|
|
290
290
|
// 6. Clean up ALL worktrees created for this plan's work items (shared-branch + per-item)
|
|
291
291
|
try {
|
|
@@ -319,7 +319,7 @@ function checkPlanCompletion(meta, config) {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
if (cleanedWt > 0) e.log('info', `Plan completion: cleaned ${cleanedWt} worktree(s)`);
|
|
322
|
-
} catch {}
|
|
322
|
+
} catch (err) { e.log('warn', `Worktree cleanup: ${err.message}`); }
|
|
323
323
|
|
|
324
324
|
e.log('info', `PRD ${planFile} completed: ${doneItems.length} done, ${failedItems.length} failed, runtime ${runtimeMin}m`);
|
|
325
325
|
}
|
|
@@ -366,7 +366,7 @@ function chainPlanToPrd(dispatchItem, meta, config) {
|
|
|
366
366
|
if (fs.existsSync(jsonPath)) fs.renameSync(jsonPath, path.join(planDir, mdName));
|
|
367
367
|
planFileName = mdName;
|
|
368
368
|
e.log('info', `Plan chaining: renamed to .md (not valid JSON)`);
|
|
369
|
-
} catch {}
|
|
369
|
+
} catch (err) { e.log('warn', `Plan rename fallback: ${err.message}`); }
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -489,7 +489,7 @@ function syncPrdItemStatus(itemId, status, sourcePlan) {
|
|
|
489
489
|
return;
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
|
-
} catch {}
|
|
492
|
+
} catch (err) { engine().log('warn', `PRD status sync: ${err.message}`); }
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
// ─── PR Sync from Output ─────────────────────────────────────────────────────
|
|
@@ -689,7 +689,7 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
689
689
|
} catch (err) { e.log('warn', `Failed to remove worktree ${dir}: ${err.message}`); }
|
|
690
690
|
}
|
|
691
691
|
}
|
|
692
|
-
} catch {}
|
|
692
|
+
} catch (err) { e.log('warn', `Post-merge worktree cleanup: ${err.message}`); }
|
|
693
693
|
}
|
|
694
694
|
|
|
695
695
|
if (newStatus !== 'merged') return;
|
|
@@ -711,7 +711,7 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
713
|
if (updated > 0) e.log('info', `Post-merge: marked ${mergedItemId} as implemented for ${pr.id}`);
|
|
714
|
-
} catch {}
|
|
714
|
+
} catch (err) { e.log('warn', `Post-merge PRD update: ${err.message}`); }
|
|
715
715
|
}
|
|
716
716
|
|
|
717
717
|
const agentId = (pr.agent || '').toLowerCase();
|
|
@@ -1014,7 +1014,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1014
1014
|
sessionId, dispatchId: dispatchItem.id, savedAt: new Date().toISOString(),
|
|
1015
1015
|
branch: dispatchItem.meta?.branch || null,
|
|
1016
1016
|
});
|
|
1017
|
-
} catch {}
|
|
1017
|
+
} catch (err) { engine().log('warn', `Session save: ${err.message}`); }
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
1020
1020
|
// Handle decomposition results — create sub-items from decompose agent output
|
|
@@ -1038,7 +1038,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1038
1038
|
const wi = items.find(i => i.id === meta.item.id);
|
|
1039
1039
|
if (wi) retries = (wi._retryCount || 0); // Use fresh value from file
|
|
1040
1040
|
}
|
|
1041
|
-
} catch {}
|
|
1041
|
+
} catch { /* optional */ }
|
|
1042
1042
|
|
|
1043
1043
|
if (retries < 3) {
|
|
1044
1044
|
e.log('info', `Agent failed for ${meta.item.id} — auto-retry ${retries + 1}/3`);
|
|
@@ -1056,7 +1056,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1056
1056
|
shared.safeWrite(wiPath, items);
|
|
1057
1057
|
}
|
|
1058
1058
|
}
|
|
1059
|
-
} catch {}
|
|
1059
|
+
} catch (err) { e.log('warn', `Retry update: ${err.message}`); }
|
|
1060
1060
|
} else {
|
|
1061
1061
|
updateWorkItemStatus(meta, 'failed', 'Agent failed (3 retries exhausted)');
|
|
1062
1062
|
}
|
|
@@ -1071,7 +1071,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1071
1071
|
const wi = items.find(i => i.id === meta.item.id);
|
|
1072
1072
|
if (wi) { delete wi._decomposing; shared.safeWrite(wiPath, items); }
|
|
1073
1073
|
}
|
|
1074
|
-
} catch {}
|
|
1074
|
+
} catch (err) { e.log('warn', `Decompose cleanup: ${err.message}`); }
|
|
1075
1075
|
}
|
|
1076
1076
|
}
|
|
1077
1077
|
// Plan chaining removed — user must explicitly execute plan-to-prd after reviewing the plan
|
|
@@ -1188,7 +1188,7 @@ function syncPrdFromPrs(config) {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
} catch (err) {
|
|
1190
1190
|
// Non-fatal — log and continue
|
|
1191
|
-
try { engine().log('warn', `syncPrdFromPrs error: ${err?.message || err}`); } catch {}
|
|
1191
|
+
try { engine().log('warn', `syncPrdFromPrs error: ${err?.message || err}`); } catch { /* engine not available */ }
|
|
1192
1192
|
}
|
|
1193
1193
|
}
|
|
1194
1194
|
|
package/engine/llm.js
CHANGED
|
@@ -39,7 +39,7 @@ function trackEngineUsage(category, usage) {
|
|
|
39
39
|
daily.cacheRead += usage.cacheRead || 0;
|
|
40
40
|
|
|
41
41
|
safeWrite(metricsPath, metrics);
|
|
42
|
-
} catch {}
|
|
42
|
+
} catch (e) { console.error('metrics update:', e.message); }
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// ── Core LLM Call ───────────────────────────────────────────────────────────
|
|
@@ -72,7 +72,7 @@ function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', m
|
|
|
72
72
|
proc.stdout.on('data', d => { stdout += d.toString(); });
|
|
73
73
|
proc.stderr.on('data', d => { stderr += d.toString(); });
|
|
74
74
|
|
|
75
|
-
const timer = setTimeout(() => { try { proc.kill('SIGTERM'); } catch {} }, timeout);
|
|
75
|
+
const timer = setTimeout(() => { try { proc.kill('SIGTERM'); } catch { /* process may be dead */ } }, timeout);
|
|
76
76
|
|
|
77
77
|
proc.on('close', (code) => {
|
|
78
78
|
clearTimeout(timer);
|
package/engine/preflight.js
CHANGED
|
@@ -32,7 +32,7 @@ function findClaudeBinary() {
|
|
|
32
32
|
const resolved = path.join(basedir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
33
33
|
if (fs.existsSync(resolved)) return resolved;
|
|
34
34
|
}
|
|
35
|
-
} catch {}
|
|
35
|
+
} catch { /* optional */ }
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -180,7 +180,7 @@ function doctor(minionsHome) {
|
|
|
180
180
|
process.kill(control.pid, 0);
|
|
181
181
|
alive = true;
|
|
182
182
|
}
|
|
183
|
-
} catch {}
|
|
183
|
+
} catch { /* process may be dead */ }
|
|
184
184
|
runtimeResults.push({ name: 'Engine', ok: alive, message: alive ? `running (PID ${control.pid})` : `stale PID ${control.pid} — run: minions start` });
|
|
185
185
|
} else {
|
|
186
186
|
runtimeResults.push({ name: 'Engine', ok: 'warn', message: `${control.state || 'stopped'} — run: minions start` });
|
package/engine/queries.js
CHANGED
|
@@ -168,7 +168,7 @@ function getAgentStatus(agentId) {
|
|
|
168
168
|
started_at: latestInFlight.dispatched_at || latestInFlight.created || null,
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
|
-
} catch {}
|
|
171
|
+
} catch { /* optional */ }
|
|
172
172
|
|
|
173
173
|
return { status: 'idle', task: null, started_at: null, completed_at: null };
|
|
174
174
|
}
|
|
@@ -199,7 +199,7 @@ function getAgents(config) {
|
|
|
199
199
|
else if (s.status === 'error') lastAction = `Error: ${s.task}`;
|
|
200
200
|
else if (inboxFiles.length > 0) {
|
|
201
201
|
const lastOutput = path.join(INBOX_DIR, inboxFiles[inboxFiles.length - 1]);
|
|
202
|
-
try { lastAction = `Output: ${path.basename(lastOutput)} (${timeSince(fs.statSync(lastOutput).mtimeMs)})`; } catch {}
|
|
202
|
+
try { lastAction = `Output: ${path.basename(lastOutput)} (${timeSince(fs.statSync(lastOutput).mtimeMs)})`; } catch { /* optional */ }
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
const chartered = fs.existsSync(path.join(AGENTS_DIR, a.id, 'charter.md'));
|
|
@@ -237,7 +237,7 @@ function getAgentDetail(id) {
|
|
|
237
237
|
result: d.result || '', reason: d.reason || '',
|
|
238
238
|
completed_at: d.completed_at || '',
|
|
239
239
|
}));
|
|
240
|
-
} catch {}
|
|
240
|
+
} catch { /* optional */ }
|
|
241
241
|
|
|
242
242
|
return { charter, history, statusData, outputLog, inboxContents, recentDispatches };
|
|
243
243
|
}
|
|
@@ -302,7 +302,7 @@ function collectSkillFiles(config) {
|
|
|
302
302
|
seen.add(d);
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
|
-
} catch {}
|
|
305
|
+
} catch { /* optional */ }
|
|
306
306
|
|
|
307
307
|
// 1b. Installed plugin skills: ~/.claude/plugins/installed_plugins.json → cache/<marketplace>/<plugin>/<version>/commands/*.md
|
|
308
308
|
try {
|
|
@@ -321,9 +321,9 @@ function collectSkillFiles(config) {
|
|
|
321
321
|
skillFiles.push({ file: cmd, dir: commandsDir, scope: 'plugin', skillName: name });
|
|
322
322
|
seen.add(name);
|
|
323
323
|
}
|
|
324
|
-
} catch {}
|
|
324
|
+
} catch { /* optional */ }
|
|
325
325
|
}
|
|
326
|
-
} catch {}
|
|
326
|
+
} catch { /* optional */ }
|
|
327
327
|
|
|
328
328
|
// 2. Project-specific skills: <project>/.claude/skills/<name>.md or <name>/SKILL.md
|
|
329
329
|
for (const project of getProjects(config)) {
|
|
@@ -343,7 +343,7 @@ function collectSkillFiles(config) {
|
|
|
343
343
|
skillFiles.push({ file: entry, dir: projectSkillsDir, scope: 'project', projectName: project.name });
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
-
} catch {}
|
|
346
|
+
} catch { /* optional */ }
|
|
347
347
|
}
|
|
348
348
|
return skillFiles;
|
|
349
349
|
}
|
|
@@ -363,7 +363,7 @@ function getSkills(config) {
|
|
|
363
363
|
scope,
|
|
364
364
|
autoGenerated: isAutoGenerated,
|
|
365
365
|
});
|
|
366
|
-
} catch {}
|
|
366
|
+
} catch { /* optional */ }
|
|
367
367
|
}
|
|
368
368
|
return all;
|
|
369
369
|
}
|
|
@@ -541,7 +541,7 @@ function getPrdInfo(config) {
|
|
|
541
541
|
const sourceMtime = Math.floor(fs.statSync(path.join(PLANS_DIR, plan.source_plan)).mtimeMs);
|
|
542
542
|
const recorded = plan.sourcePlanModifiedAt ? new Date(plan.sourcePlanModifiedAt).getTime() : null;
|
|
543
543
|
if (recorded && sourceMtime > recorded) planStale = true;
|
|
544
|
-
} catch {}
|
|
544
|
+
} catch { /* optional */ }
|
|
545
545
|
}
|
|
546
546
|
for (const f of plan.missing_features) {
|
|
547
547
|
allPrdItems.push({
|
|
@@ -552,9 +552,9 @@ function getPrdInfo(config) {
|
|
|
552
552
|
_prdUpdatedAt: new Date(stat.mtimeMs).toISOString(),
|
|
553
553
|
});
|
|
554
554
|
}
|
|
555
|
-
} catch {}
|
|
555
|
+
} catch { /* optional */ }
|
|
556
556
|
}
|
|
557
|
-
} catch {}
|
|
557
|
+
} catch { /* optional */ }
|
|
558
558
|
}
|
|
559
559
|
|
|
560
560
|
if (allPrdItems.length === 0) return { progress: null, status: null };
|
|
@@ -568,7 +568,7 @@ function getPrdInfo(config) {
|
|
|
568
568
|
try {
|
|
569
569
|
const workItems = safeJson(projectWorkItemsPath(project)) || [];
|
|
570
570
|
for (const wi of workItems) { if (wi.sourcePlan) wiById[wi.id] = wi; }
|
|
571
|
-
} catch {}
|
|
571
|
+
} catch { /* optional */ }
|
|
572
572
|
}
|
|
573
573
|
|
|
574
574
|
// PR-to-PRD linking — primary source is pr-links.json (single-writer, never clobbered by polling)
|
|
@@ -629,7 +629,7 @@ function getPrdInfo(config) {
|
|
|
629
629
|
if (wi.completedAt) { const c = new Date(wi.completedAt).getTime(); if (!t.lastCompleted || c > t.lastCompleted) t.lastCompleted = c; }
|
|
630
630
|
if (wi.status !== 'done' && wi.status !== 'in-pr') t.allDone = false; // in-pr treated as done for backward compat
|
|
631
631
|
}
|
|
632
|
-
} catch {}
|
|
632
|
+
} catch { /* optional */ }
|
|
633
633
|
}
|
|
634
634
|
|
|
635
635
|
const progress = {
|
package/engine/shared.js
CHANGED
|
@@ -38,24 +38,24 @@ function safeWrite(p, data) {
|
|
|
38
38
|
} catch (e) {
|
|
39
39
|
if (e.code === 'EPERM' && attempt < 4) {
|
|
40
40
|
const delay = 50 * (attempt + 1); // 50, 100, 150, 200ms
|
|
41
|
-
try { const ab = new SharedArrayBuffer(4); Atomics.wait(new Int32Array(ab), 0, 0, delay); } catch { const start = Date.now(); while (Date.now() - start < delay) {} }
|
|
41
|
+
try { const ab = new SharedArrayBuffer(4); Atomics.wait(new Int32Array(ab), 0, 0, delay); } catch { /* fallback busy-wait */ const start = Date.now(); while (Date.now() - start < delay) {} }
|
|
42
42
|
continue;
|
|
43
43
|
}
|
|
44
44
|
// Final attempt failed — fall through to direct write
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
// All rename attempts failed — direct write as fallback (not atomic but won't lose data)
|
|
48
|
-
try { fs.unlinkSync(tmp); } catch {}
|
|
48
|
+
try { fs.unlinkSync(tmp); } catch { /* cleanup */ }
|
|
49
49
|
fs.writeFileSync(p, content);
|
|
50
50
|
} catch (err) {
|
|
51
51
|
// Even direct write failed — log and clean up tmp
|
|
52
52
|
console.error(`[safeWrite] FAILED to write ${p}: ${err.message}`);
|
|
53
|
-
try { fs.unlinkSync(tmp); } catch {}
|
|
53
|
+
try { fs.unlinkSync(tmp); } catch { /* cleanup */ }
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function safeUnlink(p) {
|
|
58
|
-
try { fs.unlinkSync(p); } catch {}
|
|
58
|
+
try { fs.unlinkSync(p); } catch { /* cleanup */ }
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function sleepMs(ms) {
|
|
@@ -90,8 +90,8 @@ function withFileLock(lockPath, fn, {
|
|
|
90
90
|
try {
|
|
91
91
|
return fn();
|
|
92
92
|
} finally {
|
|
93
|
-
try { fs.closeSync(fd); } catch {}
|
|
94
|
-
try { fs.unlinkSync(lockPath); } catch {}
|
|
93
|
+
try { fs.closeSync(fd); } catch { /* cleanup */ }
|
|
94
|
+
try { fs.unlinkSync(lockPath); } catch { /* cleanup */ }
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
package/engine/spawn-agent.js
CHANGED
|
@@ -45,7 +45,7 @@ if (!claudeBin) {
|
|
|
45
45
|
const basedir = path.dirname(which.replace(/^\/c\//, 'C:/').replace(/\//g, path.sep));
|
|
46
46
|
claudeBin = path.join(basedir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
47
47
|
}
|
|
48
|
-
} catch {}
|
|
48
|
+
} catch { /* optional */ }
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// Debug log
|
|
@@ -86,7 +86,7 @@ if (_sysPromptFileSupported === null) {
|
|
|
86
86
|
const { spawnSync } = require('child_process');
|
|
87
87
|
const testResult = spawnSync(process.execPath, [claudeBin, '--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
88
88
|
_sysPromptFileSupported = (testResult.stdout || '').includes('system-prompt-file');
|
|
89
|
-
try { fs.writeFileSync(capsCachePath, JSON.stringify({ claudeBin, sysPromptFile: _sysPromptFileSupported, checkedAt: new Date().toISOString() })); } catch {}
|
|
89
|
+
try { fs.writeFileSync(capsCachePath, JSON.stringify({ claudeBin, sysPromptFile: _sysPromptFileSupported, checkedAt: new Date().toISOString() })); } catch { /* optional */ }
|
|
90
90
|
} catch { _sysPromptFileSupported = true; /* assume supported */ }
|
|
91
91
|
}
|
|
92
92
|
if (!isResume) try {
|
|
@@ -131,7 +131,7 @@ if (!isResume && Buffer.byteLength(sysPrompt) >= 30000) {
|
|
|
131
131
|
proc.stdin.end();
|
|
132
132
|
|
|
133
133
|
// Clean up temp file (only created for non-resume sessions)
|
|
134
|
-
if (!isResume) setTimeout(() => { try { fs.unlinkSync(sysTmpPath); } catch {} }, 5000);
|
|
134
|
+
if (!isResume) setTimeout(() => { try { fs.unlinkSync(sysTmpPath); } catch { /* cleanup */ } }, 5000);
|
|
135
135
|
|
|
136
136
|
// Capture stderr separately for debugging
|
|
137
137
|
let stderrBuf = '';
|