@yemi33/minions 0.1.306 → 0.1.308
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 +5 -0
- package/engine/lifecycle.js +52 -35
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/lifecycle.js
CHANGED
|
@@ -142,7 +142,8 @@ function checkPlanCompletion(meta, config) {
|
|
|
142
142
|
if (wrote) log('info', `PRD completion summary written to notes/inbox/`);
|
|
143
143
|
|
|
144
144
|
// Persist status and _completionNotified atomically BEFORE creating work items
|
|
145
|
-
plan._completionNotified
|
|
145
|
+
// NOTE: Do NOT set plan._completionNotified in-memory before persist —
|
|
146
|
+
// if persist fails, the in-memory flag would prevent retry on next tick.
|
|
146
147
|
mutateJsonFileLocked(planPath, (data) => {
|
|
147
148
|
data.status = 'completed';
|
|
148
149
|
data.completedAt = plan.completedAt;
|
|
@@ -154,7 +155,11 @@ function checkPlanCompletion(meta, config) {
|
|
|
154
155
|
// Resolve the primary project for writing new work items (PR, verify)
|
|
155
156
|
const projectName = plan.project;
|
|
156
157
|
const primaryProject = projectName
|
|
157
|
-
? projects.find(p => p.name?.toLowerCase() === projectName?.toLowerCase()) : projects[0];
|
|
158
|
+
? projects.find(p => p.name?.toLowerCase() === projectName?.toLowerCase()) : (projects[0] || null);
|
|
159
|
+
if (!primaryProject) {
|
|
160
|
+
log('warn', `checkPlanCompletion: no project available (projects array ${projects.length === 0 ? 'empty' : 'no match for ' + projectName}) — skipping PR/verify creation for ${planFile}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
158
163
|
const wiPath = primaryProject ? shared.projectWorkItemsPath(primaryProject) : null;
|
|
159
164
|
const workItems = wiPath ? (safeJson(wiPath) || []) : [];
|
|
160
165
|
|
|
@@ -424,6 +429,10 @@ function chainPlanToPrd(dispatchItem, meta, config) {
|
|
|
424
429
|
|
|
425
430
|
const projectName = meta?.item?.project || meta?.project?.name;
|
|
426
431
|
const projects = shared.getProjects(config);
|
|
432
|
+
if (projects.length === 0) {
|
|
433
|
+
log('error', 'Plan chaining: no projects configured — cannot chain plan to PRD');
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
427
436
|
const targetProject = projectName
|
|
428
437
|
? projects.find(p => p.name === projectName) || projects[0]
|
|
429
438
|
: projects[0];
|
|
@@ -584,7 +593,11 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
584
593
|
if (prMatches.size === 0) return 0;
|
|
585
594
|
|
|
586
595
|
const projects = shared.getProjects(config);
|
|
587
|
-
|
|
596
|
+
if (projects.length === 0 && !meta?.project?.name) {
|
|
597
|
+
log('warn', `syncPrsFromOutput: no projects configured and no project in meta — cannot sync PRs`);
|
|
598
|
+
return 0;
|
|
599
|
+
}
|
|
600
|
+
const defaultProject = (meta?.project?.name && projects.find(p => p.name === meta.project.name)) || (projects[0] || null);
|
|
588
601
|
const useCentral = !defaultProject;
|
|
589
602
|
|
|
590
603
|
// Match each PR to its correct project by finding which repo URL appears near the PR number in output
|
|
@@ -612,47 +625,51 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
612
625
|
const agentName = config.agents?.[agentId]?.name || agentId;
|
|
613
626
|
let added = 0;
|
|
614
627
|
const centralPrPath = path.join(MINIONS_DIR, 'pull-requests.json');
|
|
615
|
-
// Track which PR files need writing — keyed by target name
|
|
616
|
-
const dirtyTargets = new Map(); // name -> { prs, prPath }
|
|
617
628
|
|
|
629
|
+
// Group PR matches by target file so we take one lock per target
|
|
630
|
+
const targetPrIds = new Map(); // targetName -> { prPath, prIds: [{ prId, fullId }] }
|
|
618
631
|
for (const prId of prMatches) {
|
|
619
632
|
const fullId = `PR-${prId}`;
|
|
620
633
|
const targetProject = useCentral ? null : resolveProjectForPr(prId);
|
|
621
634
|
const targetName = targetProject ? targetProject.name : '_central';
|
|
622
635
|
const prPath = targetProject ? shared.projectPrPath(targetProject) : centralPrPath;
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
if (!dirtyTargets.has(targetName)) {
|
|
626
|
-
dirtyTargets.set(targetName, { prs: safeJson(prPath) || [], prPath });
|
|
627
|
-
}
|
|
628
|
-
const entry = dirtyTargets.get(targetName);
|
|
629
|
-
if (entry.prs.some(p => p.id === fullId || String(p.id) === String(prId))) continue;
|
|
630
|
-
|
|
631
|
-
let title = meta?.item?.title || '';
|
|
632
|
-
const titleMatch = output.match(new RegExp(`${prId}[^\\n]*?[—–-]\\s*([^\\n]+)`, 'i'));
|
|
633
|
-
if (titleMatch) title = titleMatch[1].trim();
|
|
634
|
-
if (title.includes('session_id') || title.includes('is_error') || title.includes('uuid') || title.length > 120) {
|
|
635
|
-
title = meta?.item?.title || '';
|
|
636
|
+
if (!targetPrIds.has(targetName)) {
|
|
637
|
+
targetPrIds.set(targetName, { prPath, prIds: [] });
|
|
636
638
|
}
|
|
637
|
-
|
|
638
|
-
id: fullId,
|
|
639
|
-
title: (title || `PR created by ${agentName}`).slice(0, 120),
|
|
640
|
-
agent: agentName,
|
|
641
|
-
branch: meta?.branch || '',
|
|
642
|
-
reviewStatus: 'pending',
|
|
643
|
-
status: 'active',
|
|
644
|
-
created: dateStamp(),
|
|
645
|
-
url: extractPrUrl(prId),
|
|
646
|
-
prdItems: meta?.item?.id ? [meta.item.id] : [],
|
|
647
|
-
sourcePlan: meta?.item?.sourcePlan || '',
|
|
648
|
-
itemType: meta?.item?.itemType || ''
|
|
649
|
-
});
|
|
650
|
-
if (meta?.item?.id) addPrLink(fullId, meta.item.id);
|
|
651
|
-
added++;
|
|
639
|
+
targetPrIds.get(targetName).prIds.push({ prId, fullId });
|
|
652
640
|
}
|
|
653
641
|
|
|
654
|
-
|
|
655
|
-
|
|
642
|
+
// For each target, read+modify+write inside a single locked callback
|
|
643
|
+
for (const [name, { prPath, prIds }] of targetPrIds) {
|
|
644
|
+
mutateJsonFileLocked(prPath, (prs) => {
|
|
645
|
+
if (!Array.isArray(prs)) prs = [];
|
|
646
|
+
for (const { prId, fullId } of prIds) {
|
|
647
|
+
if (prs.some(p => p.id === fullId || String(p.id) === String(prId))) continue;
|
|
648
|
+
|
|
649
|
+
let title = meta?.item?.title || '';
|
|
650
|
+
const titleMatch = output.match(new RegExp(`${prId}[^\\n]*?[—–-]\\s*([^\\n]+)`, 'i'));
|
|
651
|
+
if (titleMatch) title = titleMatch[1].trim();
|
|
652
|
+
if (title.includes('session_id') || title.includes('is_error') || title.includes('uuid') || title.length > 120) {
|
|
653
|
+
title = meta?.item?.title || '';
|
|
654
|
+
}
|
|
655
|
+
prs.push({
|
|
656
|
+
id: fullId,
|
|
657
|
+
title: (title || `PR created by ${agentName}`).slice(0, 120),
|
|
658
|
+
agent: agentName,
|
|
659
|
+
branch: meta?.branch || '',
|
|
660
|
+
reviewStatus: 'pending',
|
|
661
|
+
status: 'active',
|
|
662
|
+
created: dateStamp(),
|
|
663
|
+
url: extractPrUrl(prId),
|
|
664
|
+
prdItems: meta?.item?.id ? [meta.item.id] : [],
|
|
665
|
+
sourcePlan: meta?.item?.sourcePlan || '',
|
|
666
|
+
itemType: meta?.item?.itemType || ''
|
|
667
|
+
});
|
|
668
|
+
if (meta?.item?.id) addPrLink(fullId, meta.item.id);
|
|
669
|
+
added++;
|
|
670
|
+
}
|
|
671
|
+
return prs;
|
|
672
|
+
}, { defaultValue: [] });
|
|
656
673
|
log('info', `Synced PR(s) from ${agentName}'s output to ${name === '_central' ? 'central' : name}/pull-requests.json`);
|
|
657
674
|
}
|
|
658
675
|
return added;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.308",
|
|
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"
|