@yemi33/minions 0.1.527 → 0.1.529
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/engine/cleanup.js +17 -17
- package/engine/cli.js +56 -59
- package/engine/dispatch.js +14 -15
- package/engine/lifecycle.js +51 -35
- package/engine/pipeline.js +41 -41
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.529 (2026-04-07)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- auto-dispatch fix when review agent requests changes
|
|
7
|
+
|
|
8
|
+
## 0.1.528 (2026-04-07)
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
- Convert engine/ safeWrite calls on work-items.json to mutateWorkItems() (#415)
|
|
12
|
+
|
|
3
13
|
## 0.1.527 (2026-04-07)
|
|
4
14
|
|
|
5
15
|
### Features
|
package/engine/cleanup.js
CHANGED
|
@@ -9,7 +9,7 @@ const shared = require('./shared');
|
|
|
9
9
|
const queries = require('./queries');
|
|
10
10
|
|
|
11
11
|
const { exec, execSilent, log, ts } = shared;
|
|
12
|
-
const { safeJson, safeWrite, safeReadDir, getProjects, projectWorkItemsPath, projectPrPath,
|
|
12
|
+
const { safeJson, safeWrite, safeReadDir, mutateWorkItems, getProjects, projectWorkItemsPath, projectPrPath,
|
|
13
13
|
sanitizeBranch, KB_CATEGORIES } = shared;
|
|
14
14
|
const { getDispatch, getAgentStatus } = queries;
|
|
15
15
|
|
|
@@ -454,17 +454,17 @@ function runCleanup(config, verbose = false) {
|
|
|
454
454
|
for (const project of projects) {
|
|
455
455
|
try {
|
|
456
456
|
const wiPath = projectWorkItemsPath(project);
|
|
457
|
-
const items = safeJson(wiPath) || [];
|
|
458
457
|
let migrated = 0;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
item.status
|
|
462
|
-
|
|
463
|
-
|
|
458
|
+
mutateWorkItems(wiPath, items => {
|
|
459
|
+
for (const item of items) {
|
|
460
|
+
if (LEGACY_DONE_ALIASES.has(item.status)) {
|
|
461
|
+
item.status = shared.WI_STATUS.DONE;
|
|
462
|
+
delete item._pendingReason;
|
|
463
|
+
migrated++;
|
|
464
|
+
}
|
|
464
465
|
}
|
|
465
|
-
}
|
|
466
|
+
});
|
|
466
467
|
if (migrated > 0) {
|
|
467
|
-
safeWrite(wiPath, items);
|
|
468
468
|
log('info', `Migrated ${migrated} legacy status(es) → done in ${project.name} work items`);
|
|
469
469
|
}
|
|
470
470
|
} catch (e) { log('warn', 'migrate legacy statuses: ' + e.message); }
|
|
@@ -472,17 +472,17 @@ function runCleanup(config, verbose = false) {
|
|
|
472
472
|
// Central work items
|
|
473
473
|
try {
|
|
474
474
|
const centralPath = path.join(MINIONS_DIR, 'work-items.json');
|
|
475
|
-
const centralItems = safeJson(centralPath) || [];
|
|
476
475
|
let migrated = 0;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
item.status
|
|
480
|
-
|
|
481
|
-
|
|
476
|
+
mutateWorkItems(centralPath, items => {
|
|
477
|
+
for (const item of items) {
|
|
478
|
+
if (LEGACY_DONE_ALIASES.has(item.status)) {
|
|
479
|
+
item.status = shared.WI_STATUS.DONE;
|
|
480
|
+
delete item._pendingReason;
|
|
481
|
+
migrated++;
|
|
482
|
+
}
|
|
482
483
|
}
|
|
483
|
-
}
|
|
484
|
+
});
|
|
484
485
|
if (migrated > 0) {
|
|
485
|
-
safeWrite(centralPath, centralItems);
|
|
486
486
|
log('info', `Migrated ${migrated} legacy status(es) → done in central work items`);
|
|
487
487
|
}
|
|
488
488
|
} catch (e) { log('warn', 'migrate central legacy statuses: ' + e.message); }
|
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, ts, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT } = shared;
|
|
9
|
+
const { safeRead, safeJson, safeWrite, mutateWorkItems, 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;
|
|
@@ -165,18 +165,18 @@ const commands = {
|
|
|
165
165
|
if (sj?.sessionId) sessionId = sj.sessionId;
|
|
166
166
|
} catch {}
|
|
167
167
|
e.activeProcesses.set(item.id, { proc: { pid: agentPid > 0 ? agentPid : null }, agentId, startedAt: item.created_at, reattached: true, sessionId });
|
|
168
|
-
// Sync work item status to dispatched —
|
|
168
|
+
// Sync work item status to dispatched — atomic write to avoid lifecycle lazy init issues
|
|
169
169
|
if (item.meta?.item?.id && item.meta?.project?.localPath) {
|
|
170
170
|
try {
|
|
171
171
|
const wiPath = path.join(MINIONS_DIR, "projects", item.meta.project.name, "work-items.json");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
172
|
+
mutateWorkItems(wiPath, items => {
|
|
173
|
+
const wi = items.find(w => w.id === item.meta.item.id);
|
|
174
|
+
if (wi && wi.status !== WI_STATUS.DISPATCHED) {
|
|
175
|
+
wi.status = WI_STATUS.DISPATCHED;
|
|
176
|
+
wi.dispatched_to = wi.dispatched_to || agentId;
|
|
177
|
+
wi.dispatched_at = wi.dispatched_at || ts();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
180
|
} catch (err) { console.log(` Warning: failed to sync work item status: ${err.message}`); }
|
|
181
181
|
}
|
|
182
182
|
reattached++;
|
|
@@ -264,19 +264,19 @@ const commands = {
|
|
|
264
264
|
try {
|
|
265
265
|
lifecycle.updateWorkItemStatus(item.meta, status, isSuccess ? '' : 'Completed while engine was down');
|
|
266
266
|
} catch {
|
|
267
|
-
//
|
|
267
|
+
// Atomic file write fallback
|
|
268
268
|
try {
|
|
269
269
|
const projName = item.meta.project?.name;
|
|
270
270
|
if (projName) {
|
|
271
271
|
const wiPath = path.join(MINIONS_DIR, 'projects', projName, 'work-items.json');
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
272
|
+
mutateWorkItems(wiPath, items => {
|
|
273
|
+
const wi = items.find(w => w.id === item.meta.item.id);
|
|
274
|
+
if (wi) {
|
|
275
|
+
wi.status = status;
|
|
276
|
+
if (isSuccess) { wi.completedAt = ts(); delete wi.failReason; }
|
|
277
|
+
else { wi.failedAt = ts(); wi.failReason = 'Completed while engine was down'; }
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
280
|
}
|
|
281
281
|
} catch (err) { e.log('warn', `Orphan WI fallback: ${err.message}`); }
|
|
282
282
|
}
|
|
@@ -321,19 +321,17 @@ const commands = {
|
|
|
321
321
|
}
|
|
322
322
|
for (const wiPath of allWiPaths) {
|
|
323
323
|
try {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
e.log('info', `Recovery: reset stuck item ${item.id} from dispatched → pending`);
|
|
324
|
+
mutateWorkItems(wiPath, items => {
|
|
325
|
+
for (const item of items) {
|
|
326
|
+
if (item.status === WI_STATUS.DISPATCHED && !activeIds.has(item.id)) {
|
|
327
|
+
item.status = WI_STATUS.PENDING;
|
|
328
|
+
delete item.dispatched_at;
|
|
329
|
+
delete item.dispatched_to;
|
|
330
|
+
fixes++;
|
|
331
|
+
e.log('info', `Recovery: reset stuck item ${item.id} from dispatched → pending`);
|
|
332
|
+
}
|
|
334
333
|
}
|
|
335
|
-
}
|
|
336
|
-
if (changed) safeWrite(wiPath, items);
|
|
334
|
+
});
|
|
337
335
|
} catch (err) { e.log('warn', `Recovery WI reset: ${err.message}`); }
|
|
338
336
|
}
|
|
339
337
|
|
|
@@ -691,24 +689,23 @@ const commands = {
|
|
|
691
689
|
? projects.find(p => p.name?.toLowerCase() === opts.project?.toLowerCase()) || projects[0]
|
|
692
690
|
: projects[0];
|
|
693
691
|
const wiPath = projectWorkItemsPath(targetProject);
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
safeWrite(wiPath, items);
|
|
692
|
+
let item;
|
|
693
|
+
mutateWorkItems(wiPath, items => {
|
|
694
|
+
item = {
|
|
695
|
+
id: `W${String(items.length + 1).padStart(3, '0')}`,
|
|
696
|
+
title: title,
|
|
697
|
+
type: opts.type || 'implement',
|
|
698
|
+
status: WI_STATUS.QUEUED,
|
|
699
|
+
priority: opts.priority || 'medium',
|
|
700
|
+
complexity: opts.complexity || 'medium',
|
|
701
|
+
description: opts.description || title,
|
|
702
|
+
agent: opts.agent || null,
|
|
703
|
+
branch: opts.branch || null,
|
|
704
|
+
prompt: opts.prompt || null,
|
|
705
|
+
created_at: e.ts()
|
|
706
|
+
};
|
|
707
|
+
items.push(item);
|
|
708
|
+
});
|
|
712
709
|
|
|
713
710
|
console.log(`Queued work item: ${item.id} — ${item.title} (project: ${targetProject.name || 'default'})`);
|
|
714
711
|
console.log(` Type: ${item.type} | Priority: ${item.priority} | Agent: ${item.agent || 'auto'}`);
|
|
@@ -903,16 +900,16 @@ const commands = {
|
|
|
903
900
|
? shared.projectWorkItemsPath({ localPath: item.meta.project.localPath, name: item.meta.project.name, workSources: config.projects?.find(p => p.name === item.meta.project.name)?.workSources })
|
|
904
901
|
: null;
|
|
905
902
|
if (wiPath) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
}
|
|
903
|
+
mutateWorkItems(wiPath, items => {
|
|
904
|
+
const target = items.find(i => i.id === itemId);
|
|
905
|
+
if (target) {
|
|
906
|
+
target.status = WI_STATUS.PENDING;
|
|
907
|
+
delete target.dispatched_at;
|
|
908
|
+
delete target.dispatched_to;
|
|
909
|
+
delete target.failReason;
|
|
910
|
+
delete target.failedAt;
|
|
911
|
+
}
|
|
912
|
+
});
|
|
916
913
|
}
|
|
917
914
|
}
|
|
918
915
|
}
|
package/engine/dispatch.js
CHANGED
|
@@ -9,7 +9,7 @@ const shared = require('./shared');
|
|
|
9
9
|
const queries = require('./queries');
|
|
10
10
|
const { setCooldownFailure } = require('./cooldown');
|
|
11
11
|
|
|
12
|
-
const { safeJson, safeWrite, safeReadDir, mutateJsonFileLocked,
|
|
12
|
+
const { safeJson, safeWrite, safeReadDir, mutateJsonFileLocked, mutateWorkItems,
|
|
13
13
|
getProjects, projectWorkItemsPath, log, ts, dateStamp,
|
|
14
14
|
WI_STATUS, DISPATCH_RESULT, ENGINE_DEFAULTS } = shared;
|
|
15
15
|
const { getConfig, getDispatch, DISPATCH_PATH, INBOX_DIR } = queries;
|
|
@@ -146,20 +146,19 @@ function completeDispatch(id, result = DISPATCH_RESULT.SUCCESS, reason = '', res
|
|
|
146
146
|
try {
|
|
147
147
|
const wiPath = lifecycle().resolveWorkItemPath(item.meta);
|
|
148
148
|
if (wiPath) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
149
|
+
mutateWorkItems(wiPath, items => {
|
|
150
|
+
const wi = items.find(i => i.id === item.meta.item.id);
|
|
151
|
+
if (wi && wi.status !== WI_STATUS.PAUSED && wi.status !== WI_STATUS.DONE && !wi.completedAt) {
|
|
152
|
+
wi._retryCount = retries + 1;
|
|
153
|
+
wi.status = WI_STATUS.PENDING;
|
|
154
|
+
wi._lastRetryReason = reason || '';
|
|
155
|
+
wi._lastRetryAt = ts();
|
|
156
|
+
delete wi.failReason;
|
|
157
|
+
delete wi.failedAt;
|
|
158
|
+
delete wi.dispatched_at;
|
|
159
|
+
delete wi.dispatched_to;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
163
162
|
}
|
|
164
163
|
} catch (e) { log('warn', 'increment retry counter: ' + e.message); }
|
|
165
164
|
} else {
|
package/engine/lifecycle.js
CHANGED
|
@@ -7,7 +7,7 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const shared = require('./shared');
|
|
10
|
-
const { safeRead, safeJson, safeWrite, mutateJsonFileLocked, execSilent, projectPrPath, getPrLinks, addPrLink,
|
|
10
|
+
const { safeRead, safeJson, safeWrite, mutateJsonFileLocked, mutateWorkItems, execSilent, projectPrPath, getPrLinks, addPrLink,
|
|
11
11
|
log, ts, dateStamp, WI_STATUS, DONE_STATUSES, WORK_TYPE, PLAN_STATUS, PR_STATUS, DISPATCH_RESULT,
|
|
12
12
|
ENGINE_DEFAULTS } = shared;
|
|
13
13
|
const { trackEngineUsage } = require('./llm');
|
|
@@ -145,7 +145,6 @@ function checkPlanCompletion(meta, config) {
|
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
147
|
const wiPath = shared.projectWorkItemsPath(primaryProject);
|
|
148
|
-
const workItems = safeJson(wiPath) || [];
|
|
149
148
|
|
|
150
149
|
// 3. For shared-branch plans, create PR work item
|
|
151
150
|
if (plan.branch_strategy === 'shared-branch' && plan.feature_branch && wiPath) {
|
|
@@ -155,15 +154,16 @@ function checkPlanCompletion(meta, config) {
|
|
|
155
154
|
const featureBranch = plan.feature_branch;
|
|
156
155
|
const mainBranch = shared.resolveMainBranch(primaryProject.localPath, primaryProject.mainBranch);
|
|
157
156
|
const itemSummary = doneItems.map(w => '- ' + w.id + ': ' + w.title.replace('Implement: ', '')).join('\n');
|
|
158
|
-
workItems
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
mutateWorkItems(wiPath, workItems => {
|
|
158
|
+
workItems.push({
|
|
159
|
+
id, title: `Create PR for plan: ${plan.plan_summary || planFile}`,
|
|
160
|
+
type: 'implement', priority: 'high',
|
|
161
|
+
description: `All plan items from \`${planFile}\` are complete on branch \`${featureBranch}\`.\n\n**Branch:** \`${featureBranch}\`\n**Target:** \`${mainBranch}\`\n\n## Completed Items\n${itemSummary}`,
|
|
162
|
+
status: WI_STATUS.PENDING, created: ts(), createdBy: 'engine:plan-completion',
|
|
163
|
+
sourcePlan: planFile, itemType: 'pr',
|
|
164
|
+
branch: featureBranch, branchStrategy: 'shared-branch', project: projectName,
|
|
165
|
+
});
|
|
165
166
|
});
|
|
166
|
-
shared.safeWrite(wiPath, workItems);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -244,20 +244,21 @@ function checkPlanCompletion(meta, config) {
|
|
|
244
244
|
prSummary,
|
|
245
245
|
].join('\n');
|
|
246
246
|
|
|
247
|
-
workItems
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
247
|
+
mutateWorkItems(wiPath, workItems => {
|
|
248
|
+
workItems.push({
|
|
249
|
+
id: verifyId,
|
|
250
|
+
title: `Verify plan: ${(plan.plan_summary || planFile).slice(0, 80)}`,
|
|
251
|
+
type: 'verify',
|
|
252
|
+
priority: 'high',
|
|
253
|
+
description,
|
|
254
|
+
status: WI_STATUS.PENDING,
|
|
255
|
+
created: ts(),
|
|
256
|
+
createdBy: 'engine:plan-verification',
|
|
257
|
+
sourcePlan: planFile,
|
|
258
|
+
itemType: 'verify',
|
|
259
|
+
project: projectName,
|
|
260
|
+
});
|
|
259
261
|
});
|
|
260
|
-
shared.safeWrite(wiPath, workItems);
|
|
261
262
|
log('info', `Created verification work item ${verifyId} for plan ${planFile}`);
|
|
262
263
|
}
|
|
263
264
|
|
|
@@ -697,13 +698,27 @@ function updatePrAfterReview(agentId, pr, project, config) {
|
|
|
697
698
|
const dispatch = getDispatch();
|
|
698
699
|
const completedEntry = (dispatch.completed || []).find(d => d.agent === agentId && d.type === 'review');
|
|
699
700
|
|
|
701
|
+
// Check actual review status from the platform (agent may have approved or requested changes)
|
|
702
|
+
let postReviewStatus = 'waiting';
|
|
703
|
+
try {
|
|
704
|
+
const projectObj = project || shared.getProjects(config)[0];
|
|
705
|
+
if (projectObj) {
|
|
706
|
+
const host = projectObj.repoHost || 'ado';
|
|
707
|
+
const checkFn = host === 'github'
|
|
708
|
+
? require('./github').checkLiveReviewStatus
|
|
709
|
+
: require('./ado').checkLiveReviewStatus;
|
|
710
|
+
const liveStatus = checkFn(pr, projectObj);
|
|
711
|
+
if (liveStatus) postReviewStatus = liveStatus;
|
|
712
|
+
}
|
|
713
|
+
} catch (e) { log('warn', `Post-review status check for ${pr.id}: ${e.message}`); }
|
|
714
|
+
|
|
700
715
|
const prPath = project ? shared.projectPrPath(project) : path.join(path.resolve(MINIONS_DIR, '..'), '.minions', 'pull-requests.json');
|
|
701
716
|
let updatedTarget = null;
|
|
702
717
|
shared.mutateJsonFileLocked(prPath, (prs) => {
|
|
703
718
|
if (!Array.isArray(prs)) return prs;
|
|
704
719
|
const target = prs.find(p => p.id === pr.id);
|
|
705
720
|
if (!target) return prs;
|
|
706
|
-
target.reviewStatus =
|
|
721
|
+
target.reviewStatus = postReviewStatus;
|
|
707
722
|
target.lastReviewedAt = ts();
|
|
708
723
|
target.minionsReview = {
|
|
709
724
|
reviewer: reviewerName,
|
|
@@ -811,17 +826,18 @@ async function handlePostMerge(pr, project, config, newStatus) {
|
|
|
811
826
|
for (const p of shared.getProjects(config)) wiPaths.push(shared.projectWorkItemsPath(p));
|
|
812
827
|
for (const wiPath of wiPaths) {
|
|
813
828
|
try {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}
|
|
829
|
+
let found = false;
|
|
830
|
+
mutateWorkItems(wiPath, items => {
|
|
831
|
+
const item = items.find(i => i.id === mergedItemId);
|
|
832
|
+
if (item && item.status !== WI_STATUS.DONE) {
|
|
833
|
+
log('info', `Post-merge: marking work item ${mergedItemId} as done (was ${item.status}) for ${pr.id}`);
|
|
834
|
+
item.status = WI_STATUS.DONE;
|
|
835
|
+
item.completedAt = ts();
|
|
836
|
+
item._mergedVia = pr.id;
|
|
837
|
+
found = true;
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
if (found) break;
|
|
825
841
|
} catch (err) { log('warn', `Post-merge work item update: ${err.message}`); }
|
|
826
842
|
}
|
|
827
843
|
}
|
package/engine/pipeline.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const shared = require('./shared');
|
|
10
|
-
const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS } = shared;
|
|
10
|
+
const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, mutateWorkItems, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS } = shared;
|
|
11
11
|
const http = require('http');
|
|
12
12
|
const { parseCronExpr, shouldRunNow } = require('./scheduler');
|
|
13
13
|
|
|
@@ -159,32 +159,32 @@ function executeTaskStage(stage, stageState, run, config) {
|
|
|
159
159
|
const items = stage.items || [{ title: stage.title, description: stage.description || '', type: stage.taskType || 'explore', agent: stage.agent }];
|
|
160
160
|
const count = stage.count || items.length;
|
|
161
161
|
const wiPath = path.join(__dirname, '..', 'work-items.json');
|
|
162
|
-
const workItems = safeJson(wiPath) || [];
|
|
163
162
|
const createdIds = [];
|
|
164
163
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
164
|
+
mutateWorkItems(wiPath, workItems => {
|
|
165
|
+
for (let i = 0; i < count; i++) {
|
|
166
|
+
const item = items[i % items.length];
|
|
167
|
+
const id = `PL-${run.runId.slice(4, 12)}-${stage.id}-${i}`;
|
|
168
|
+
if (workItems.some(w => w.id === id)) { createdIds.push(id); continue; }
|
|
169
|
+
workItems.push({
|
|
170
|
+
id,
|
|
171
|
+
title: item.title || stage.title,
|
|
172
|
+
description: item.description || stage.description || '',
|
|
173
|
+
type: item.type || stage.taskType || 'explore',
|
|
174
|
+
priority: item.priority || stage.priority || 'medium',
|
|
175
|
+
// Only set agent if explicitly specified — otherwise engine routing assigns any available agent
|
|
176
|
+
...(item.agent || stage.agent ? { agent: item.agent || stage.agent } : {}),
|
|
177
|
+
status: WI_STATUS.PENDING,
|
|
178
|
+
created: ts(),
|
|
179
|
+
createdBy: 'pipeline:' + run.pipelineId,
|
|
180
|
+
branch: `pipeline/${run.pipelineId}/${stage.id}`,
|
|
181
|
+
_pipelineRun: run.runId,
|
|
182
|
+
_pipelineStage: stage.id,
|
|
183
|
+
});
|
|
184
|
+
createdIds.push(id);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
186
187
|
|
|
187
|
-
safeWrite(wiPath, workItems);
|
|
188
188
|
return { status: PIPELINE_STATUS.RUNNING, artifacts: { workItems: createdIds } };
|
|
189
189
|
}
|
|
190
190
|
|
|
@@ -280,24 +280,24 @@ async function executePlanStage(stage, stageState, run, config) {
|
|
|
280
280
|
|
|
281
281
|
// Create plan-to-prd work item
|
|
282
282
|
const wiPath = path.join(__dirname, '..', 'work-items.json');
|
|
283
|
-
const workItems = safeJson(wiPath) || [];
|
|
284
283
|
const wiId = `PL-${run.runId.slice(4, 12)}-${stage.id}-prd`;
|
|
285
|
-
|
|
286
|
-
workItems.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
284
|
+
mutateWorkItems(wiPath, workItems => {
|
|
285
|
+
if (!workItems.some(w => w.id === wiId)) {
|
|
286
|
+
workItems.push({
|
|
287
|
+
id: wiId,
|
|
288
|
+
title: `Convert plan to PRD: ${path.basename(filePath)}`,
|
|
289
|
+
type: WORK_TYPE.PLAN_TO_PRD,
|
|
290
|
+
priority: 'high',
|
|
291
|
+
status: WI_STATUS.PENDING,
|
|
292
|
+
planFile: path.basename(filePath),
|
|
293
|
+
created: ts(),
|
|
294
|
+
createdBy: 'pipeline:' + run.pipelineId,
|
|
295
|
+
branch: `pipeline/${run.pipelineId}/${stage.id}`,
|
|
296
|
+
_pipelineRun: run.runId,
|
|
297
|
+
_pipelineStage: stage.id,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
301
|
|
|
302
302
|
return {
|
|
303
303
|
status: PIPELINE_STATUS.RUNNING,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.529",
|
|
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"
|