@yemi33/minions 0.1.320 → 0.1.321
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 +4 -1
- package/engine/ado.js +9 -9
- package/engine/cli.js +14 -14
- package/engine/github.js +8 -8
- package/engine/meeting.js +4 -4
- package/engine/pipeline.js +11 -11
- package/engine/playbook.js +7 -7
- package/engine/scheduler.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.321 (2026-04-03)
|
|
4
4
|
|
|
5
5
|
### Fixes
|
|
6
6
|
- project scan finds git repos — .git was in skipDirs
|
|
7
7
|
|
|
8
|
+
### Other
|
|
9
|
+
- refactor: replace magic strings in remaining engine files with constants
|
|
10
|
+
|
|
8
11
|
## 0.1.319 (2026-04-03)
|
|
9
12
|
|
|
10
13
|
### Other
|
package/engine/ado.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const shared = require('./shared');
|
|
8
|
-
const { exec, getAdoOrgBase, addPrLink, log, dateStamp } = shared;
|
|
8
|
+
const { exec, getAdoOrgBase, addPrLink, log, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
10
|
|
|
11
11
|
// Lazy require to avoid circular dependency — only needed for engine().handlePostMerge
|
|
@@ -80,7 +80,7 @@ async function forEachActivePr(config, token, callback) {
|
|
|
80
80
|
if (!project.adoOrg || !project.adoProject || !project.repositoryId) continue;
|
|
81
81
|
|
|
82
82
|
const prs = getPrs(project);
|
|
83
|
-
const activePrs = prs.filter(pr => pr.status ===
|
|
83
|
+
const activePrs = prs.filter(pr => pr.status === PR_STATUS.ACTIVE);
|
|
84
84
|
if (activePrs.length === 0) continue;
|
|
85
85
|
|
|
86
86
|
let projectUpdated = 0;
|
|
@@ -123,18 +123,18 @@ async function pollPrStatus(config) {
|
|
|
123
123
|
const prData = await adoFetch(`${repoBase}?api-version=7.1`, token);
|
|
124
124
|
|
|
125
125
|
let newStatus = pr.status;
|
|
126
|
-
if (prData.status === 'completed') newStatus =
|
|
127
|
-
else if (prData.status === 'abandoned') newStatus =
|
|
128
|
-
else if (prData.status === 'active') newStatus =
|
|
126
|
+
if (prData.status === 'completed') newStatus = PR_STATUS.MERGED;
|
|
127
|
+
else if (prData.status === 'abandoned') newStatus = PR_STATUS.ABANDONED;
|
|
128
|
+
else if (prData.status === 'active') newStatus = PR_STATUS.ACTIVE;
|
|
129
129
|
|
|
130
130
|
if (pr.status !== newStatus) {
|
|
131
131
|
log('info', `PR ${pr.id} status: ${pr.status} → ${newStatus}`);
|
|
132
132
|
pr.status = newStatus;
|
|
133
133
|
updated = true;
|
|
134
134
|
|
|
135
|
-
if (newStatus ===
|
|
135
|
+
if (newStatus === PR_STATUS.MERGED || newStatus === PR_STATUS.ABANDONED) {
|
|
136
136
|
if (pr.reviewStatus === 'waiting') {
|
|
137
|
-
pr.reviewStatus = newStatus ===
|
|
137
|
+
pr.reviewStatus = newStatus === PR_STATUS.MERGED ? 'approved' : 'pending';
|
|
138
138
|
log('info', `PR ${pr.id} reviewStatus: waiting → ${pr.reviewStatus} (${newStatus})`);
|
|
139
139
|
}
|
|
140
140
|
await engine().handlePostMerge(pr, project, config, newStatus);
|
|
@@ -157,7 +157,7 @@ async function pollPrStatus(config) {
|
|
|
157
157
|
.map(r => r.displayName)
|
|
158
158
|
.filter(Boolean);
|
|
159
159
|
// Fallback: if PR was merged and no decisive votes, use completedBy
|
|
160
|
-
if (!reviewedBy.length && newStatus ===
|
|
160
|
+
if (!reviewedBy.length && newStatus === PR_STATUS.MERGED && prData.closedBy?.displayName) {
|
|
161
161
|
reviewedBy.push(prData.closedBy.displayName);
|
|
162
162
|
}
|
|
163
163
|
if (JSON.stringify(pr.reviewedBy || []) !== JSON.stringify(reviewedBy)) {
|
|
@@ -184,7 +184,7 @@ async function pollPrStatus(config) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
if (newStatus !==
|
|
187
|
+
if (newStatus !== PR_STATUS.ACTIVE) return updated;
|
|
188
188
|
|
|
189
189
|
const statusData = await adoFetch(`${repoBase}/statuses?api-version=7.1`, token);
|
|
190
190
|
|
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 } = shared;
|
|
9
|
+
const { safeRead, safeJson, safeWrite, 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;
|
|
@@ -159,8 +159,8 @@ const commands = {
|
|
|
159
159
|
const wiPath = path.join(MINIONS_DIR, "projects", item.meta.project.name, "work-items.json");
|
|
160
160
|
const wiItems = safeJson(wiPath) || [];
|
|
161
161
|
const wi = wiItems.find(w => w.id === item.meta.item.id);
|
|
162
|
-
if (wi && wi.status !==
|
|
163
|
-
wi.status =
|
|
162
|
+
if (wi && wi.status !== WI_STATUS.DISPATCHED) {
|
|
163
|
+
wi.status = WI_STATUS.DISPATCHED;
|
|
164
164
|
wi.dispatched_to = wi.dispatched_to || agentId;
|
|
165
165
|
wi.dispatched_at = wi.dispatched_at || new Date().toISOString();
|
|
166
166
|
safeWrite(wiPath, wiItems);
|
|
@@ -210,7 +210,7 @@ const commands = {
|
|
|
210
210
|
if (!hasResult && !hasError) continue;
|
|
211
211
|
|
|
212
212
|
const isSuccess = hasResult && !hasError;
|
|
213
|
-
const result = isSuccess ?
|
|
213
|
+
const result = isSuccess ? DISPATCH_RESULT.SUCCESS : DISPATCH_RESULT.ERROR;
|
|
214
214
|
|
|
215
215
|
e.log('info', `Orphan recovery: ${agentId} (${item.id}) completed while engine was down — result: ${result}`);
|
|
216
216
|
|
|
@@ -222,7 +222,7 @@ const commands = {
|
|
|
222
222
|
|
|
223
223
|
// Update work item status
|
|
224
224
|
if (item.meta?.item?.id) {
|
|
225
|
-
const status = isSuccess ?
|
|
225
|
+
const status = isSuccess ? WI_STATUS.DONE : WI_STATUS.FAILED;
|
|
226
226
|
try {
|
|
227
227
|
lifecycle.updateWorkItemStatus(item.meta, status, isSuccess ? '' : 'Completed while engine was down');
|
|
228
228
|
} catch {
|
|
@@ -286,8 +286,8 @@ const commands = {
|
|
|
286
286
|
const items = safeJson(wiPath) || [];
|
|
287
287
|
let changed = false;
|
|
288
288
|
for (const item of items) {
|
|
289
|
-
if (item.status ===
|
|
290
|
-
item.status =
|
|
289
|
+
if (item.status === WI_STATUS.DISPATCHED && !activeIds.has(item.id)) {
|
|
290
|
+
item.status = WI_STATUS.PENDING;
|
|
291
291
|
delete item.dispatched_at;
|
|
292
292
|
delete item.dispatched_to;
|
|
293
293
|
changed = true;
|
|
@@ -656,7 +656,7 @@ const commands = {
|
|
|
656
656
|
id: `W${String(items.length + 1).padStart(3, '0')}`,
|
|
657
657
|
title: title,
|
|
658
658
|
type: opts.type || 'implement',
|
|
659
|
-
status:
|
|
659
|
+
status: WI_STATUS.QUEUED,
|
|
660
660
|
priority: opts.priority || 'medium',
|
|
661
661
|
complexity: opts.complexity || 'medium',
|
|
662
662
|
description: opts.description || title,
|
|
@@ -750,7 +750,7 @@ const commands = {
|
|
|
750
750
|
}
|
|
751
751
|
|
|
752
752
|
const id = e.addToDispatch({
|
|
753
|
-
type:
|
|
753
|
+
type: WORK_TYPE.PLAN_TO_PRD,
|
|
754
754
|
agent: agentId,
|
|
755
755
|
agentName: config.agents[agentId]?.name,
|
|
756
756
|
agentRole: config.agents[agentId]?.role,
|
|
@@ -815,13 +815,13 @@ const commands = {
|
|
|
815
815
|
}
|
|
816
816
|
if (exists && name === 'pullRequests') {
|
|
817
817
|
const prs = safeJson(filePath) || [];
|
|
818
|
-
const pending = prs.filter(p => p.status ===
|
|
819
|
-
const needsFix = prs.filter(p => p.status ===
|
|
818
|
+
const pending = prs.filter(p => p.status === PR_STATUS.ACTIVE && (p.reviewStatus === 'pending' || p.reviewStatus === 'waiting'));
|
|
819
|
+
const needsFix = prs.filter(p => p.status === PR_STATUS.ACTIVE && p.reviewStatus === 'changes-requested');
|
|
820
820
|
console.log(` PRs: ${pending.length} pending review, ${needsFix.length} need fixes`);
|
|
821
821
|
}
|
|
822
822
|
if (exists && name === 'workItems') {
|
|
823
823
|
const items = safeJson(filePath) || [];
|
|
824
|
-
const queued = items.filter(i => i.status ===
|
|
824
|
+
const queued = items.filter(i => i.status === WI_STATUS.QUEUED);
|
|
825
825
|
console.log(` Items: ${queued.length} queued`);
|
|
826
826
|
}
|
|
827
827
|
if (name === 'specs' || name === 'mergedDesignDocs') {
|
|
@@ -853,7 +853,7 @@ const commands = {
|
|
|
853
853
|
const killed = dispatch.active || [];
|
|
854
854
|
for (const item of killed) {
|
|
855
855
|
if (item.meta) {
|
|
856
|
-
e.updateWorkItemStatus(item.meta,
|
|
856
|
+
e.updateWorkItemStatus(item.meta, WI_STATUS.PENDING, '');
|
|
857
857
|
const itemId = item.meta.item?.id;
|
|
858
858
|
if (itemId) {
|
|
859
859
|
const wiPath = (item.meta.source === 'central-work-item' || item.meta.source === 'central-work-item-fanout')
|
|
@@ -865,7 +865,7 @@ const commands = {
|
|
|
865
865
|
const items = safeJson(wiPath) || [];
|
|
866
866
|
const target = items.find(i => i.id === itemId);
|
|
867
867
|
if (target) {
|
|
868
|
-
target.status =
|
|
868
|
+
target.status = WI_STATUS.PENDING;
|
|
869
869
|
delete target.dispatched_at;
|
|
870
870
|
delete target.dispatched_to;
|
|
871
871
|
delete target.failReason;
|
package/engine/github.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const shared = require('./shared');
|
|
8
|
-
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, dateStamp } = shared;
|
|
8
|
+
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink, getPrLinks, log, dateStamp, PR_STATUS } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -53,7 +53,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
53
53
|
if (!slug) continue;
|
|
54
54
|
|
|
55
55
|
const prs = getPrs(project);
|
|
56
|
-
const activePrs = prs.filter(pr => pr.status ===
|
|
56
|
+
const activePrs = prs.filter(pr => pr.status === PR_STATUS.ACTIVE);
|
|
57
57
|
if (activePrs.length === 0) continue;
|
|
58
58
|
|
|
59
59
|
let projectUpdated = 0;
|
|
@@ -79,7 +79,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
79
79
|
// Also poll manually-linked PRs from central pull-requests.json (extract slug from URL)
|
|
80
80
|
const centralPath = path.join(MINIONS_DIR, 'pull-requests.json');
|
|
81
81
|
const centralPrs = safeJson(centralPath) || [];
|
|
82
|
-
const activeCentral = centralPrs.filter(pr => pr.status ===
|
|
82
|
+
const activeCentral = centralPrs.filter(pr => pr.status === PR_STATUS.ACTIVE && pr.url);
|
|
83
83
|
let centralUpdated = 0;
|
|
84
84
|
for (const pr of activeCentral) {
|
|
85
85
|
const ghMatch = pr.url.match(/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
|
|
@@ -123,19 +123,19 @@ async function pollPrStatus(config) {
|
|
|
123
123
|
|
|
124
124
|
// Map GitHub PR state to minions status
|
|
125
125
|
let newStatus = pr.status;
|
|
126
|
-
if (prData.merged) newStatus =
|
|
127
|
-
else if (prData.state === 'closed') newStatus =
|
|
128
|
-
else if (prData.state === 'open') newStatus =
|
|
126
|
+
if (prData.merged) newStatus = PR_STATUS.MERGED;
|
|
127
|
+
else if (prData.state === 'closed') newStatus = PR_STATUS.ABANDONED;
|
|
128
|
+
else if (prData.state === 'open') newStatus = PR_STATUS.ACTIVE;
|
|
129
129
|
|
|
130
130
|
if (pr.status !== newStatus) {
|
|
131
131
|
log('info', `PR ${pr.id} status: ${pr.status} → ${newStatus}`);
|
|
132
132
|
pr.status = newStatus;
|
|
133
133
|
updated = true;
|
|
134
134
|
|
|
135
|
-
if (newStatus ===
|
|
135
|
+
if (newStatus === PR_STATUS.MERGED || newStatus === PR_STATUS.ABANDONED) {
|
|
136
136
|
// Resolve stale 'waiting' review status — won't be polled again after this
|
|
137
137
|
if (pr.reviewStatus === 'waiting') {
|
|
138
|
-
pr.reviewStatus = newStatus ===
|
|
138
|
+
pr.reviewStatus = newStatus === PR_STATUS.MERGED ? 'approved' : 'pending';
|
|
139
139
|
log('info', `PR ${pr.id} reviewStatus: waiting → ${pr.reviewStatus} (${newStatus})`);
|
|
140
140
|
}
|
|
141
141
|
await engine().handlePostMerge(pr, project, config, newStatus);
|
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 } = shared;
|
|
9
|
+
const { safeJson, safeWrite, safeRead, uid, log, 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');
|
|
@@ -118,7 +118,7 @@ function discoverMeetingWork(config) {
|
|
|
118
118
|
if (!prompt) continue;
|
|
119
119
|
|
|
120
120
|
work.push({
|
|
121
|
-
type:
|
|
121
|
+
type: WORK_TYPE.MEETING,
|
|
122
122
|
agent: concluder,
|
|
123
123
|
agentName: agents[concluder]?.name || concluder,
|
|
124
124
|
agentRole: agents[concluder]?.role || 'Agent',
|
|
@@ -165,7 +165,7 @@ function discoverMeetingWork(config) {
|
|
|
165
165
|
if (!prompt) continue;
|
|
166
166
|
|
|
167
167
|
work.push({
|
|
168
|
-
type:
|
|
168
|
+
type: WORK_TYPE.MEETING,
|
|
169
169
|
agent: agentId,
|
|
170
170
|
agentName: agents[agentId]?.name || agentId,
|
|
171
171
|
agentRole: agents[agentId]?.role || 'Agent',
|
|
@@ -278,7 +278,7 @@ function _killMeetingDispatches(meetingId) {
|
|
|
278
278
|
dp.active = (dp.active || []).filter(d => d.meta?.meetingId !== meetingId);
|
|
279
279
|
dp.completed = dp.completed || [];
|
|
280
280
|
for (const d of toKill) {
|
|
281
|
-
dp.completed.push({ ...d, result:
|
|
281
|
+
dp.completed.push({ ...d, result: DISPATCH_RESULT.ERROR, reason: 'Meeting ended/advanced by human', completed_at: new Date().toISOString() });
|
|
282
282
|
}
|
|
283
283
|
if (dp.completed.length > 100) dp.completed = dp.completed.slice(-100);
|
|
284
284
|
return dp;
|
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 } = shared;
|
|
10
|
+
const { safeJson, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS } = shared;
|
|
11
11
|
const { parseCronExpr, shouldRunNow } = require('./scheduler');
|
|
12
12
|
|
|
13
13
|
const PIPELINES_DIR = path.join(__dirname, '..', 'pipelines');
|
|
@@ -172,7 +172,7 @@ function executeTaskStage(stage, stageState, run, config) {
|
|
|
172
172
|
type: item.type || stage.taskType || 'explore',
|
|
173
173
|
priority: item.priority || stage.priority || 'medium',
|
|
174
174
|
agent: item.agent || stage.agent || '',
|
|
175
|
-
status:
|
|
175
|
+
status: WI_STATUS.PENDING,
|
|
176
176
|
created: ts(),
|
|
177
177
|
createdBy: 'pipeline:' + run.pipelineId,
|
|
178
178
|
branch: `pipeline/${run.pipelineId}/${stage.id}`,
|
|
@@ -243,9 +243,9 @@ function executePlanStage(stage, stageState, run, config) {
|
|
|
243
243
|
workItems.push({
|
|
244
244
|
id: wiId,
|
|
245
245
|
title: `Convert plan to PRD: ${path.basename(filePath)}`,
|
|
246
|
-
type:
|
|
246
|
+
type: WORK_TYPE.PLAN_TO_PRD,
|
|
247
247
|
priority: 'high',
|
|
248
|
-
status:
|
|
248
|
+
status: WI_STATUS.PENDING,
|
|
249
249
|
planFile: path.basename(filePath),
|
|
250
250
|
created: ts(),
|
|
251
251
|
createdBy: 'pipeline:' + run.pipelineId,
|
|
@@ -347,7 +347,7 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
347
347
|
if (ids.length === 0) return false;
|
|
348
348
|
return ids.every(id => {
|
|
349
349
|
const wi = workItems.find(w => w.id === id);
|
|
350
|
-
return !wi || wi.status ===
|
|
350
|
+
return !wi || wi.status === WI_STATUS.DONE || wi.status === WI_STATUS.FAILED; // missing = treat as done
|
|
351
351
|
});
|
|
352
352
|
}
|
|
353
353
|
case 'meeting': {
|
|
@@ -372,7 +372,7 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
372
372
|
const prdWiIds = artifacts.workItems || [];
|
|
373
373
|
const prdDone = prdWiIds.every(id => {
|
|
374
374
|
const wi = all.find(w => w.id === id);
|
|
375
|
-
return !wi || wi.status ===
|
|
375
|
+
return !wi || wi.status === WI_STATUS.DONE || wi.status === WI_STATUS.FAILED; // missing = treat as done
|
|
376
376
|
});
|
|
377
377
|
if (!prdDone) return false;
|
|
378
378
|
|
|
@@ -390,7 +390,7 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
390
390
|
}
|
|
391
391
|
// Find materialized work items for discovered PRDs
|
|
392
392
|
for (const prdFile of (artifacts.prds || [])) {
|
|
393
|
-
const prdItems = all.filter(w => w.sourcePlan === prdFile && w.type !==
|
|
393
|
+
const prdItems = all.filter(w => w.sourcePlan === prdFile && w.type !== WORK_TYPE.PLAN_TO_PRD);
|
|
394
394
|
for (const wi of prdItems) {
|
|
395
395
|
if (!(artifacts.workItems || []).includes(wi.id)) {
|
|
396
396
|
artifacts.workItems = artifacts.workItems || [];
|
|
@@ -405,8 +405,8 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
405
405
|
for (const prdFile of artifacts.prds) {
|
|
406
406
|
const prdPath = path.join(prdDir, prdFile);
|
|
407
407
|
const prd = safeJson(prdPath);
|
|
408
|
-
if (prd && prd.status ===
|
|
409
|
-
prd.status =
|
|
408
|
+
if (prd && prd.status === PLAN_STATUS.AWAITING_APPROVAL) {
|
|
409
|
+
prd.status = PLAN_STATUS.APPROVED;
|
|
410
410
|
prd.approvedAt = ts();
|
|
411
411
|
prd.approvedBy = 'pipeline:' + run.pipelineId;
|
|
412
412
|
safeWrite(prdPath, prd);
|
|
@@ -420,7 +420,7 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
420
420
|
if (implementIds.length === 0 && artifacts.prds?.length > 0) return false; // items not materialized yet
|
|
421
421
|
return implementIds.every(id => {
|
|
422
422
|
const wi = all.find(w => w.id === id);
|
|
423
|
-
return !wi || wi.status ===
|
|
423
|
+
return !wi || wi.status === WI_STATUS.DONE || wi.status === WI_STATUS.FAILED; // missing = treat as done
|
|
424
424
|
});
|
|
425
425
|
}
|
|
426
426
|
case 'merge-prs': {
|
|
@@ -431,7 +431,7 @@ function isStageComplete(stage, stageState, run, config) {
|
|
|
431
431
|
const prs = safeJson(shared.projectPrPath(project)) || [];
|
|
432
432
|
for (const prId of prIds) {
|
|
433
433
|
const pr = prs.find(p => p.id === prId);
|
|
434
|
-
if (pr && pr.status !==
|
|
434
|
+
if (pr && pr.status !== PR_STATUS.MERGED && pr.status !== PR_STATUS.ABANDONED) return false;
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
437
|
return true;
|
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 } = shared;
|
|
12
|
+
const { safeJson, safeRead, getProjects, log, 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, '..');
|
|
@@ -135,7 +135,7 @@ function resolveTaskContext(item, config) {
|
|
|
135
135
|
// Check work-items to find which plan file this agent created
|
|
136
136
|
const workItems = safeJson(path.join(MINIONS_DIR, 'work-items.json')) || [];
|
|
137
137
|
const agentPlanItems = workItems.filter(w =>
|
|
138
|
-
w.type ===
|
|
138
|
+
w.type === WORK_TYPE.PLAN && w.dispatched_to === agent.id && w.status === WI_STATUS.DONE && w._planFileName
|
|
139
139
|
).sort((a, b) => (b.completedAt || '').localeCompare(a.completedAt || ''));
|
|
140
140
|
|
|
141
141
|
if (agentPlanItems.length > 0) {
|
|
@@ -390,7 +390,7 @@ function buildAgentContext(agentId, config, project) {
|
|
|
390
390
|
|
|
391
391
|
// Recent completions (last 5, not 10)
|
|
392
392
|
const recentCompleted = (dispatch.completed || []).slice(-5).reverse().map(d =>
|
|
393
|
-
`- **${d.agent}** ${d.result ===
|
|
393
|
+
`- **${d.agent}** ${d.result === DISPATCH_RESULT.SUCCESS ? 'completed' : 'failed'}: ${(d.task || '').slice(0, 80)}${d.resultSummary ? ' — ' + d.resultSummary.slice(0, 100) : ''}`
|
|
394
394
|
);
|
|
395
395
|
if (recentCompleted.length > 0) {
|
|
396
396
|
context += `## Recently Completed\n\n${recentCompleted.join('\n')}\n\n`;
|
|
@@ -400,13 +400,13 @@ function buildAgentContext(agentId, config, project) {
|
|
|
400
400
|
const projects = getProjects(config);
|
|
401
401
|
const allPrs = [];
|
|
402
402
|
for (const p of projects) {
|
|
403
|
-
const prs = getPrs(p).filter(pr => pr.status ===
|
|
403
|
+
const prs = getPrs(p).filter(pr => pr.status === PR_STATUS.ACTIVE || pr.status === 'linked');
|
|
404
404
|
for (const pr of prs) allPrs.push({ ...pr, _project: p.name });
|
|
405
405
|
}
|
|
406
406
|
// Also check central pull-requests.json
|
|
407
407
|
try {
|
|
408
408
|
const centralPrs = safeJson(path.join(MINIONS_DIR, 'pull-requests.json')) || [];
|
|
409
|
-
for (const pr of centralPrs.filter(pr => pr.status ===
|
|
409
|
+
for (const pr of centralPrs.filter(pr => pr.status === PR_STATUS.ACTIVE || pr.status === 'linked')) {
|
|
410
410
|
if (!allPrs.some(p => p.id === pr.id)) allPrs.push({ ...pr, _project: 'central' });
|
|
411
411
|
}
|
|
412
412
|
} catch (e) { log('warn', 'read central pull-requests: ' + e.message); }
|
|
@@ -450,10 +450,10 @@ function buildBaseVars(agentId, config, project) {
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
function selectPlaybook(workType, item) {
|
|
453
|
-
if (item?.branchStrategy === 'shared-branch' && (workType ===
|
|
453
|
+
if (item?.branchStrategy === 'shared-branch' && (workType === WORK_TYPE.IMPLEMENT || workType === WORK_TYPE.IMPLEMENT_LARGE)) {
|
|
454
454
|
return 'implement-shared';
|
|
455
455
|
}
|
|
456
|
-
if (workType ===
|
|
456
|
+
if (workType === WORK_TYPE.REVIEW && !item?._pr && !item?.pr_id) {
|
|
457
457
|
return 'work-item';
|
|
458
458
|
}
|
|
459
459
|
const typeSpecificPlaybooks = ['explore', 'review', 'test', 'plan-to-prd', 'plan', 'ask', 'verify', 'decompose', 'meeting-investigate', 'meeting-debate', 'meeting-conclude'];
|
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 } = shared;
|
|
27
|
+
const { safeJson, safeWrite, mutateJsonFileLocked, WI_STATUS } = shared;
|
|
28
28
|
|
|
29
29
|
const SCHEDULE_RUNS_PATH = path.join(__dirname, 'schedule-runs.json');
|
|
30
30
|
|
|
@@ -125,7 +125,7 @@ function discoverScheduledWork(config) {
|
|
|
125
125
|
type: sched.type || 'implement',
|
|
126
126
|
priority: sched.priority || 'medium',
|
|
127
127
|
description: sched.description || sched.title,
|
|
128
|
-
status:
|
|
128
|
+
status: WI_STATUS.PENDING,
|
|
129
129
|
created: new Date().toISOString(),
|
|
130
130
|
createdBy: 'scheduler',
|
|
131
131
|
agent: sched.agent || null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.321",
|
|
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"
|