@yemi33/minions 0.1.525 → 0.1.526
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 +3 -1
- package/engine/ado.js +1 -1
- package/engine/cli.js +4 -4
- package/engine/consolidation.js +2 -2
- package/engine/github.js +1 -1
- package/engine/lifecycle.js +3 -3
- package/engine/llm.js +2 -2
- package/engine/meeting.js +25 -25
- package/engine/playbook.js +2 -2
- package/engine/scheduler.js +3 -3
- package/engine/spawn-agent.js +3 -3
- package/engine.js +5 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.526 (2026-04-07)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
|
+
- Replace new Date().toISOString() with ts() from shared.js (#411)
|
|
7
|
+
- Add Concurrency & Lock Ordering section to CLAUDE.md (#409)
|
|
6
8
|
- 9 test isolation verification tests — run last to detect pollution
|
|
7
9
|
|
|
8
10
|
### Fixes
|
package/engine/ado.js
CHANGED
|
@@ -173,7 +173,7 @@ async function pollPrStatus(config) {
|
|
|
173
173
|
const headCommit = prData.lastMergeSourceCommit?.commitId || prData.sourceRefName || '';
|
|
174
174
|
if (headCommit && pr._adoHeadCommit !== headCommit) {
|
|
175
175
|
if (pr._adoHeadCommit) { // skip first detection — only track changes
|
|
176
|
-
pr.lastPushedAt =
|
|
176
|
+
pr.lastPushedAt = ts();
|
|
177
177
|
}
|
|
178
178
|
pr._adoHeadCommit = headCommit;
|
|
179
179
|
updated = true;
|
package/engine/cli.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const shared = require('./shared');
|
|
9
|
-
const { safeRead, safeJson, safeWrite, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
9
|
+
const { safeRead, safeJson, safeWrite, ts, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
10
10
|
const queries = require('./queries');
|
|
11
11
|
const { getConfig, getControl, getDispatch, getAgentStatus,
|
|
12
12
|
MINIONS_DIR, ENGINE_DIR, AGENTS_DIR, PLANS_DIR, PRD_DIR, CONTROL_PATH, DISPATCH_PATH } = queries;
|
|
@@ -174,7 +174,7 @@ const commands = {
|
|
|
174
174
|
if (wi && wi.status !== WI_STATUS.DISPATCHED) {
|
|
175
175
|
wi.status = WI_STATUS.DISPATCHED;
|
|
176
176
|
wi.dispatched_to = wi.dispatched_to || agentId;
|
|
177
|
-
wi.dispatched_at = wi.dispatched_at ||
|
|
177
|
+
wi.dispatched_at = wi.dispatched_at || ts();
|
|
178
178
|
safeWrite(wiPath, wiItems);
|
|
179
179
|
}
|
|
180
180
|
} catch (err) { console.log(` Warning: failed to sync work item status: ${err.message}`); }
|
|
@@ -273,8 +273,8 @@ const commands = {
|
|
|
273
273
|
const wi = items.find(w => w.id === item.meta.item.id);
|
|
274
274
|
if (wi) {
|
|
275
275
|
wi.status = status;
|
|
276
|
-
if (isSuccess) { wi.completedAt =
|
|
277
|
-
else { wi.failedAt =
|
|
276
|
+
if (isSuccess) { wi.completedAt = ts(); delete wi.failReason; }
|
|
277
|
+
else { wi.failedAt = ts(); wi.failReason = 'Completed while engine was down'; }
|
|
278
278
|
safeWrite(wiPath, items);
|
|
279
279
|
}
|
|
280
280
|
}
|
package/engine/consolidation.js
CHANGED
|
@@ -9,7 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const crypto = require('crypto');
|
|
10
10
|
const shared = require('./shared');
|
|
11
11
|
const { safeRead, safeWrite, safeUnlink, runFile, cleanChildEnv,
|
|
12
|
-
parseStreamJsonOutput, classifyInboxItem, KB_CATEGORIES, log, dateStamp } = shared;
|
|
12
|
+
parseStreamJsonOutput, classifyInboxItem, KB_CATEGORIES, log, ts, dateStamp } = shared;
|
|
13
13
|
const { trackEngineUsage } = require('./llm');
|
|
14
14
|
const queries = require('./queries');
|
|
15
15
|
const { getInboxFiles, getNotes, INBOX_DIR, ENGINE_DIR, MINIONS_DIR,
|
|
@@ -440,7 +440,7 @@ function classifyToKnowledgeBase(items) {
|
|
|
440
440
|
const dir = path.join(KNOWLEDGE_DIR, cat);
|
|
441
441
|
if (fs.existsSync(dir)) count += fs.readdirSync(dir).length;
|
|
442
442
|
}
|
|
443
|
-
safeWrite(path.join(ENGINE_DIR, 'kb-checkpoint.json'), JSON.stringify({ count, updatedAt:
|
|
443
|
+
safeWrite(path.join(ENGINE_DIR, 'kb-checkpoint.json'), JSON.stringify({ count, updatedAt: ts() }));
|
|
444
444
|
} catch (err) { log('warn', `KB checkpoint: ${err.message}`); }
|
|
445
445
|
}
|
|
446
446
|
|
package/engine/github.js
CHANGED
|
@@ -217,7 +217,7 @@ async function pollPrStatus(config) {
|
|
|
217
217
|
// Track head SHA changes to detect new pushes (used for review re-dispatch gating)
|
|
218
218
|
if (prData.head?.sha && pr.headSha !== prData.head.sha) {
|
|
219
219
|
pr.headSha = prData.head.sha;
|
|
220
|
-
pr.lastPushedAt =
|
|
220
|
+
pr.lastPushedAt = ts();
|
|
221
221
|
updated = true;
|
|
222
222
|
}
|
|
223
223
|
|
package/engine/lifecycle.js
CHANGED
|
@@ -121,7 +121,7 @@ function checkPlanCompletion(meta, config) {
|
|
|
121
121
|
``,
|
|
122
122
|
`**Project:** ${plan.project || 'Unknown'}`,
|
|
123
123
|
`**Strategy:** ${plan.branch_strategy || 'parallel'}`,
|
|
124
|
-
`**Completed:** ${
|
|
124
|
+
`**Completed:** ${ts().slice(0, 16).replace('T', ' ')}`,
|
|
125
125
|
`**Runtime:** ${runtimeMin >= 60 ? Math.floor(runtimeMin / 60) + 'h ' + (runtimeMin % 60) + 'm' : runtimeMin + 'm'}`,
|
|
126
126
|
``,
|
|
127
127
|
`## Results`,
|
|
@@ -1136,7 +1136,7 @@ function handleDecompositionResult(stdout, meta, config) {
|
|
|
1136
1136
|
sourcePlan: p.sourcePlan,
|
|
1137
1137
|
branchStrategy: p.branchStrategy,
|
|
1138
1138
|
featureBranch: p.featureBranch,
|
|
1139
|
-
created:
|
|
1139
|
+
created: ts(),
|
|
1140
1140
|
createdBy: 'decomposition',
|
|
1141
1141
|
});
|
|
1142
1142
|
}
|
|
@@ -1162,7 +1162,7 @@ function runPostCompletionHooks(dispatchItem, agentId, code, stdout, config) {
|
|
|
1162
1162
|
if (isSuccess && sessionId && agentId && !agentId.startsWith('temp-')) {
|
|
1163
1163
|
try {
|
|
1164
1164
|
shared.safeWrite(path.join(AGENTS_DIR, agentId, 'session.json'), {
|
|
1165
|
-
sessionId, dispatchId: dispatchItem.id, savedAt:
|
|
1165
|
+
sessionId, dispatchId: dispatchItem.id, savedAt: ts(),
|
|
1166
1166
|
branch: dispatchItem.meta?.branch || null,
|
|
1167
1167
|
});
|
|
1168
1168
|
} catch (err) { log('warn', `Session save: ${err.message}`); }
|
package/engine/llm.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const shared = require('./shared');
|
|
8
|
-
const { safeWrite, safeUnlink, uid, runFile, cleanChildEnv, parseStreamJsonOutput, mutateJsonFileLocked } = shared;
|
|
8
|
+
const { safeWrite, safeUnlink, uid, ts, runFile, cleanChildEnv, parseStreamJsonOutput, mutateJsonFileLocked } = shared;
|
|
9
9
|
|
|
10
10
|
const MINIONS_DIR = shared.MINIONS_DIR;
|
|
11
11
|
const ENGINE_DIR = path.join(MINIONS_DIR, 'engine');
|
|
@@ -28,7 +28,7 @@ function trackEngineUsage(category, usage) {
|
|
|
28
28
|
cat.cacheRead += usage.cacheRead || 0;
|
|
29
29
|
cat.cacheCreation = (cat.cacheCreation || 0) + (usage.cacheCreation || 0);
|
|
30
30
|
|
|
31
|
-
const today =
|
|
31
|
+
const today = ts().slice(0, 10);
|
|
32
32
|
if (!metrics._daily) metrics._daily = {};
|
|
33
33
|
if (!metrics._daily[today]) metrics._daily[today] = { costUsd: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, tasks: 0 };
|
|
34
34
|
const daily = metrics._daily[today];
|
package/engine/meeting.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const shared = require('./shared');
|
|
9
|
-
const { safeJson, safeWrite, safeRead, uid, log, ENGINE_DEFAULTS, WORK_TYPE, DISPATCH_RESULT } = shared;
|
|
9
|
+
const { safeJson, safeWrite, safeRead, uid, log, ts, ENGINE_DEFAULTS, WORK_TYPE, DISPATCH_RESULT } = shared;
|
|
10
10
|
const queries = require('./queries');
|
|
11
11
|
const { getDispatch, getConfig } = queries;
|
|
12
12
|
const { renderPlaybook } = require('./playbook');
|
|
@@ -51,8 +51,8 @@ function createMeeting({ title, agenda, participants }) {
|
|
|
51
51
|
round: 1,
|
|
52
52
|
participants: participants || [],
|
|
53
53
|
createdBy: 'human',
|
|
54
|
-
createdAt:
|
|
55
|
-
roundStartedAt:
|
|
54
|
+
createdAt: ts(),
|
|
55
|
+
roundStartedAt: ts(),
|
|
56
56
|
findings: {},
|
|
57
57
|
debate: {},
|
|
58
58
|
conclusion: null,
|
|
@@ -209,16 +209,16 @@ function collectMeetingFindings(meetingId, agentId, roundName, output) {
|
|
|
209
209
|
const content = rawContent;
|
|
210
210
|
|
|
211
211
|
if (roundName === 'investigate') {
|
|
212
|
-
meeting.findings[agentId] = { content, submittedAt:
|
|
213
|
-
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'finding', content, at:
|
|
212
|
+
meeting.findings[agentId] = { content, submittedAt: ts() };
|
|
213
|
+
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'finding', content, at: ts() });
|
|
214
214
|
} else if (roundName === 'debate') {
|
|
215
|
-
meeting.debate[agentId] = { content, submittedAt:
|
|
216
|
-
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'debate', content, at:
|
|
215
|
+
meeting.debate[agentId] = { content, submittedAt: ts() };
|
|
216
|
+
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'debate', content, at: ts() });
|
|
217
217
|
} else if (roundName === 'conclude') {
|
|
218
|
-
meeting.conclusion = { content, agent: agentId, submittedAt:
|
|
219
|
-
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'conclusion', content, at:
|
|
218
|
+
meeting.conclusion = { content, agent: agentId, submittedAt: ts() };
|
|
219
|
+
meeting.transcript.push({ round: meeting.round, agent: agentId, type: 'conclusion', content, at: ts() });
|
|
220
220
|
meeting.status = 'completed';
|
|
221
|
-
meeting.completedAt =
|
|
221
|
+
meeting.completedAt = ts();
|
|
222
222
|
|
|
223
223
|
// Write transcript to inbox so agents learn from it (slug-based dedup)
|
|
224
224
|
try {
|
|
@@ -246,12 +246,12 @@ function collectMeetingFindings(meetingId, agentId, roundName, output) {
|
|
|
246
246
|
if (meeting.status === 'investigating') {
|
|
247
247
|
meeting.status = 'debating';
|
|
248
248
|
meeting.round = 2;
|
|
249
|
-
meeting.roundStartedAt =
|
|
249
|
+
meeting.roundStartedAt = ts();
|
|
250
250
|
log('info', `Meeting ${meetingId}: all findings in — advancing to debate`);
|
|
251
251
|
} else if (meeting.status === 'debating') {
|
|
252
252
|
meeting.status = 'concluding';
|
|
253
253
|
meeting.round = 3;
|
|
254
|
-
meeting.roundStartedAt =
|
|
254
|
+
meeting.roundStartedAt = ts();
|
|
255
255
|
log('info', `Meeting ${meetingId}: all debate responses in — advancing to conclusion`);
|
|
256
256
|
}
|
|
257
257
|
}
|
|
@@ -263,7 +263,7 @@ function addMeetingNote(meetingId, note) {
|
|
|
263
263
|
const meeting = getMeeting(meetingId);
|
|
264
264
|
if (!meeting) return null;
|
|
265
265
|
meeting.humanNotes.push(note);
|
|
266
|
-
meeting.transcript.push({ round: meeting.round, agent: 'human', type: 'note', content: note, at:
|
|
266
|
+
meeting.transcript.push({ round: meeting.round, agent: 'human', type: 'note', content: note, at: ts() });
|
|
267
267
|
saveMeeting(meeting);
|
|
268
268
|
return meeting;
|
|
269
269
|
}
|
|
@@ -279,7 +279,7 @@ function _killMeetingDispatches(meetingId) {
|
|
|
279
279
|
dp.active = (dp.active || []).filter(d => d.meta?.meetingId !== meetingId);
|
|
280
280
|
dp.completed = dp.completed || [];
|
|
281
281
|
for (const d of toKill) {
|
|
282
|
-
dp.completed.push({ ...d, result: DISPATCH_RESULT.ERROR, reason: 'Meeting ended/advanced by human', completed_at:
|
|
282
|
+
dp.completed.push({ ...d, result: DISPATCH_RESULT.ERROR, reason: 'Meeting ended/advanced by human', completed_at: ts() });
|
|
283
283
|
}
|
|
284
284
|
if (dp.completed.length > 100) dp.completed = dp.completed.slice(-100);
|
|
285
285
|
return dp;
|
|
@@ -295,9 +295,9 @@ function advanceMeetingRound(meetingId) {
|
|
|
295
295
|
_killMeetingDispatches(meetingId);
|
|
296
296
|
if (meeting.status === 'investigating') { meeting.status = 'debating'; meeting.round = 2; }
|
|
297
297
|
else if (meeting.status === 'debating') { meeting.status = 'concluding'; meeting.round = 3; }
|
|
298
|
-
else if (meeting.status === 'concluding') { meeting.status = 'completed'; meeting.completedAt =
|
|
298
|
+
else if (meeting.status === 'concluding') { meeting.status = 'completed'; meeting.completedAt = ts(); }
|
|
299
299
|
else return meeting; // no change
|
|
300
|
-
meeting.roundStartedAt =
|
|
300
|
+
meeting.roundStartedAt = ts();
|
|
301
301
|
saveMeeting(meeting);
|
|
302
302
|
return meeting;
|
|
303
303
|
}
|
|
@@ -307,7 +307,7 @@ function endMeeting(meetingId) {
|
|
|
307
307
|
if (!meeting) return null;
|
|
308
308
|
_killMeetingDispatches(meetingId);
|
|
309
309
|
meeting.status = 'completed';
|
|
310
|
-
meeting.completedAt =
|
|
310
|
+
meeting.completedAt = ts();
|
|
311
311
|
saveMeeting(meeting);
|
|
312
312
|
return meeting;
|
|
313
313
|
}
|
|
@@ -316,7 +316,7 @@ function archiveMeeting(id) {
|
|
|
316
316
|
const meeting = getMeeting(id);
|
|
317
317
|
if (!meeting) return null;
|
|
318
318
|
meeting.status = 'archived';
|
|
319
|
-
meeting.archivedAt =
|
|
319
|
+
meeting.archivedAt = ts();
|
|
320
320
|
saveMeeting(meeting);
|
|
321
321
|
return meeting;
|
|
322
322
|
}
|
|
@@ -364,17 +364,17 @@ function checkMeetingTimeouts(config) {
|
|
|
364
364
|
|
|
365
365
|
if (meeting.status === 'investigating') {
|
|
366
366
|
log('warn', `Meeting ${meeting.id}: round 1 timed out after ${Math.round(elapsed / 60000)}min — ${respondedCount}/${totalCount} responded, advancing to debate`);
|
|
367
|
-
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'timeout', content: `Round 1 timed out — ${respondedCount}/${totalCount} findings received`, at:
|
|
367
|
+
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'timeout', content: `Round 1 timed out — ${respondedCount}/${totalCount} findings received`, at: ts() });
|
|
368
368
|
meeting.status = 'debating';
|
|
369
369
|
meeting.round = 2;
|
|
370
|
-
meeting.roundStartedAt =
|
|
370
|
+
meeting.roundStartedAt = ts();
|
|
371
371
|
saveMeeting(meeting);
|
|
372
372
|
} else if (meeting.status === 'debating') {
|
|
373
373
|
log('warn', `Meeting ${meeting.id}: round 2 timed out after ${Math.round(elapsed / 60000)}min — ${respondedCount}/${totalCount} responded, advancing to conclusion`);
|
|
374
|
-
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'timeout', content: `Round 2 timed out — ${respondedCount}/${totalCount} debate responses received`, at:
|
|
374
|
+
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'timeout', content: `Round 2 timed out — ${respondedCount}/${totalCount} debate responses received`, at: ts() });
|
|
375
375
|
meeting.status = 'concluding';
|
|
376
376
|
meeting.round = 3;
|
|
377
|
-
meeting.roundStartedAt =
|
|
377
|
+
meeting.roundStartedAt = ts();
|
|
378
378
|
saveMeeting(meeting);
|
|
379
379
|
} else if (meeting.status === 'concluding') {
|
|
380
380
|
log('warn', `Meeting ${meeting.id}: conclusion round timed out after ${Math.round(elapsed / 60000)}min — auto-summarizing`);
|
|
@@ -386,10 +386,10 @@ function checkMeetingTimeouts(config) {
|
|
|
386
386
|
`**${(config.agents || {})[agent]?.name || agent}**: ${(d.content || '').slice(0, 200)}`
|
|
387
387
|
).join('\n');
|
|
388
388
|
const autoConclusion = `*Auto-generated — conclusion round timed out.*\n\n## Key Findings\n${findingsSummary || '(none)'}\n\n## Debate Summary\n${debateSummary || '(none)'}`;
|
|
389
|
-
meeting.conclusion = { content: autoConclusion, agent: 'system', submittedAt:
|
|
390
|
-
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'conclusion', content: autoConclusion, at:
|
|
389
|
+
meeting.conclusion = { content: autoConclusion, agent: 'system', submittedAt: ts() };
|
|
390
|
+
meeting.transcript.push({ round: meeting.round, agent: 'system', type: 'conclusion', content: autoConclusion, at: ts() });
|
|
391
391
|
meeting.status = 'completed';
|
|
392
|
-
meeting.completedAt =
|
|
392
|
+
meeting.completedAt = ts();
|
|
393
393
|
|
|
394
394
|
// Write transcript to inbox (same as normal conclusion path)
|
|
395
395
|
try {
|
package/engine/playbook.js
CHANGED
|
@@ -9,7 +9,7 @@ const path = require('path');
|
|
|
9
9
|
const shared = require('./shared');
|
|
10
10
|
const queries = require('./queries');
|
|
11
11
|
|
|
12
|
-
const { safeJson, safeRead, getProjects, log, dateStamp, WI_STATUS, WORK_TYPE, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
12
|
+
const { safeJson, safeRead, getProjects, log, ts, dateStamp, WI_STATUS, WORK_TYPE, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
13
13
|
const { getConfig, getDispatch, getNotes, getAgentCharter, getPrs, AGENTS_DIR } = queries;
|
|
14
14
|
|
|
15
15
|
const MINIONS_DIR = path.resolve(__dirname, '..');
|
|
@@ -241,7 +241,7 @@ function renderPlaybook(type, vars) {
|
|
|
241
241
|
content += `**Never delete, move, or overwrite files in \`knowledge/\`.** The sweep (consolidation engine) is the only process that writes to \`knowledge/\`. If you think a KB file is wrong, note it in your learnings file — do not touch \`knowledge/\` directly.\n`;
|
|
242
242
|
|
|
243
243
|
// Inject learnings requirement
|
|
244
|
-
const timeStamp =
|
|
244
|
+
const timeStamp = ts().slice(11, 16).replace(':', '');
|
|
245
245
|
const inboxSlug = [vars.agent_id || 'agent', vars.task_id || '', dateStamp(), timeStamp].filter(Boolean).join('-');
|
|
246
246
|
content += `\n\n---\n\n## REQUIRED: Write Learnings\n\n`;
|
|
247
247
|
content += `After completing your task, write **one** findings file to:\n`;
|
package/engine/scheduler.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
const fs = require('fs');
|
|
25
25
|
const path = require('path');
|
|
26
26
|
const shared = require('./shared');
|
|
27
|
-
const { safeJson, safeWrite, mutateJsonFileLocked, WI_STATUS } = shared;
|
|
27
|
+
const { safeJson, safeWrite, mutateJsonFileLocked, ts, WI_STATUS } = shared;
|
|
28
28
|
|
|
29
29
|
const SCHEDULE_RUNS_PATH = path.join(__dirname, 'schedule-runs.json');
|
|
30
30
|
|
|
@@ -126,7 +126,7 @@ function discoverScheduledWork(config) {
|
|
|
126
126
|
priority: sched.priority || 'medium',
|
|
127
127
|
description: sched.description || sched.title,
|
|
128
128
|
status: WI_STATUS.PENDING,
|
|
129
|
-
created:
|
|
129
|
+
created: ts(),
|
|
130
130
|
createdBy: 'scheduler',
|
|
131
131
|
agent: sched.agent || null,
|
|
132
132
|
project: sched.project || null,
|
|
@@ -134,7 +134,7 @@ function discoverScheduledWork(config) {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// Record run time inside the lock
|
|
137
|
-
runs[sched.id] =
|
|
137
|
+
runs[sched.id] = ts();
|
|
138
138
|
}
|
|
139
139
|
}, { defaultValue: {} });
|
|
140
140
|
|
package/engine/spawn-agent.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
|
-
const { exec, runFile, cleanChildEnv, killGracefully, killImmediate } = require('./shared');
|
|
11
|
+
const { exec, runFile, cleanChildEnv, killGracefully, killImmediate, ts } = require('./shared');
|
|
12
12
|
|
|
13
13
|
const [,, promptFile, sysPromptFile, ...extraArgs] = process.argv;
|
|
14
14
|
|
|
@@ -83,7 +83,7 @@ if (!claudeBin) {
|
|
|
83
83
|
const tmpDir = path.join(__dirname, 'tmp');
|
|
84
84
|
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true });
|
|
85
85
|
const debugPath = path.join(tmpDir, 'spawn-debug.log');
|
|
86
|
-
fs.writeFileSync(debugPath, `spawn-agent.js at ${
|
|
86
|
+
fs.writeFileSync(debugPath, `spawn-agent.js at ${ts()}\nclaudeBin=${claudeBin || 'not found'}\nnative=${claudeIsNative}\nprompt=${promptFile}\nsysPrompt=${sysPromptFile}\nextraArgs=${extraArgs.join(' ')}\n`);
|
|
87
87
|
|
|
88
88
|
// When resuming a session, skip system prompt (it's baked into the session)
|
|
89
89
|
const isResume = extraArgs.includes('--resume');
|
|
@@ -119,7 +119,7 @@ if (_sysPromptFileSupported === null) {
|
|
|
119
119
|
? spawnSync(claudeBin, ['--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true })
|
|
120
120
|
: spawnSync(process.execPath, [claudeBin, '--help'], { encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
121
121
|
_sysPromptFileSupported = (testResult.stdout || '').includes('system-prompt-file');
|
|
122
|
-
try { fs.writeFileSync(capsCachePath, JSON.stringify({ claudeBin, sysPromptFile: _sysPromptFileSupported, checkedAt:
|
|
122
|
+
try { fs.writeFileSync(capsCachePath, JSON.stringify({ claudeBin, sysPromptFile: _sysPromptFileSupported, checkedAt: ts() })); } catch { /* optional */ }
|
|
123
123
|
} catch { _sysPromptFileSupported = true; /* assume supported */ }
|
|
124
124
|
}
|
|
125
125
|
if (!isResume) try {
|
package/engine.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
const fs = require('fs');
|
|
25
25
|
const path = require('path');
|
|
26
26
|
const shared = require('./engine/shared');
|
|
27
|
-
const { exec, execSilent, runFile, ENGINE_DEFAULTS: DEFAULTS,
|
|
27
|
+
const { exec, execSilent, runFile, ts, ENGINE_DEFAULTS: DEFAULTS,
|
|
28
28
|
WI_STATUS, DONE_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
29
29
|
const queries = require('./engine/queries');
|
|
30
30
|
|
|
@@ -542,7 +542,7 @@ function spawnAgent(dispatchItem, config) {
|
|
|
542
542
|
if (obj.session_id) {
|
|
543
543
|
procInfo.sessionId = obj.session_id;
|
|
544
544
|
safeWrite(path.join(AGENTS_DIR, agentId, 'session.json'), {
|
|
545
|
-
sessionId: obj.session_id, dispatchId: id, savedAt:
|
|
545
|
+
sessionId: obj.session_id, dispatchId: id, savedAt: ts(), branch: branchName
|
|
546
546
|
});
|
|
547
547
|
break;
|
|
548
548
|
}
|
|
@@ -1098,7 +1098,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1098
1098
|
log('info', `Source plan ${plan.source_plan} updated — re-syncing PRD ${file}`);
|
|
1099
1099
|
autoCleanPrdWorkItems(file, config);
|
|
1100
1100
|
plan.sourcePlanModifiedAt = new Date(sourceMtime).toISOString();
|
|
1101
|
-
plan.lastSyncedFromPlan =
|
|
1101
|
+
plan.lastSyncedFromPlan = ts();
|
|
1102
1102
|
|
|
1103
1103
|
// Handle PRD based on current status
|
|
1104
1104
|
const prdStatus = plan.status || (plan.requires_approval ? 'awaiting-approval' : null);
|
|
@@ -1170,7 +1170,7 @@ function materializePlansAsWorkItems(config) {
|
|
|
1170
1170
|
if (planStatus === 'awaiting-approval') {
|
|
1171
1171
|
if (config.engine?.autoApprovePlans) {
|
|
1172
1172
|
plan.status = 'approved';
|
|
1173
|
-
plan.approvedAt =
|
|
1173
|
+
plan.approvedAt = ts();
|
|
1174
1174
|
plan.approvedBy = 'auto-mode';
|
|
1175
1175
|
safeWrite(path.join(PRD_DIR, file), plan);
|
|
1176
1176
|
log('info', `Auto-approved plan: ${file}`);
|
|
@@ -1452,7 +1452,7 @@ function discoverFromPrs(config, project) {
|
|
|
1452
1452
|
if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) {
|
|
1453
1453
|
// Coalesce: save feedback for next dispatch
|
|
1454
1454
|
if (pr.humanFeedback?.feedbackContent) {
|
|
1455
|
-
setCooldownWithContext(key, { feedbackContent: pr.humanFeedback.feedbackContent, timestamp:
|
|
1455
|
+
setCooldownWithContext(key, { feedbackContent: pr.humanFeedback.feedbackContent, timestamp: ts() });
|
|
1456
1456
|
}
|
|
1457
1457
|
continue;
|
|
1458
1458
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.526",
|
|
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"
|