@yemi33/minions 0.1.3 → 0.1.5
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 +13 -0
- package/engine/ado.js +6 -0
- package/engine/github.js +14 -3
- package/engine/lifecycle.js +1 -0
- package/engine.js +41 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/ado.js
CHANGED
|
@@ -339,6 +339,11 @@ async function reconcilePrs(config) {
|
|
|
339
339
|
// PR already tracked — write link to pr-links.json if we can extract an ID
|
|
340
340
|
if (confirmedItemId) {
|
|
341
341
|
addPrLink(prId, confirmedItemId);
|
|
342
|
+
const existing = existingPrs.find(p => p.id === prId);
|
|
343
|
+
if (existing && !(existing.prdItems || []).includes(confirmedItemId)) {
|
|
344
|
+
existing.prdItems = Array.isArray(existing.prdItems) ? existing.prdItems : [];
|
|
345
|
+
existing.prdItems.push(confirmedItemId);
|
|
346
|
+
}
|
|
342
347
|
projectUpdated++;
|
|
343
348
|
}
|
|
344
349
|
continue;
|
|
@@ -354,6 +359,7 @@ async function reconcilePrs(config) {
|
|
|
354
359
|
status: 'active',
|
|
355
360
|
created: (adoPr.creationDate || '').slice(0, 10) || e.dateStamp(),
|
|
356
361
|
url: prUrl,
|
|
362
|
+
prdItems: confirmedItemId ? [confirmedItemId] : [],
|
|
357
363
|
});
|
|
358
364
|
if (confirmedItemId) addPrLink(prId, confirmedItemId);
|
|
359
365
|
existingIds.add(prId);
|
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 } = shared;
|
|
8
|
+
const { exec, getProjects, projectPrPath, projectWorkItemsPath, safeJson, safeWrite, MINIONS_DIR, addPrLink } = shared;
|
|
9
9
|
const { getPrs } = require('./queries');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -285,14 +285,24 @@ async function reconcilePrs(config) {
|
|
|
285
285
|
|
|
286
286
|
for (const ghPr of ghPrs) {
|
|
287
287
|
const prId = `PR-${ghPr.number}`;
|
|
288
|
-
if (existingIds.has(prId)) continue;
|
|
289
|
-
|
|
290
288
|
const branch = ghPr.head?.ref || '';
|
|
291
289
|
const wiMatch = branch.match(/(P-[a-f0-9]{6,})/i) || branch.match(/(PL-W\d+)/i);
|
|
292
290
|
const linkedItemId = wiMatch ? wiMatch[1] : null;
|
|
293
291
|
const linkedItem = linkedItemId ? allItems.find(i => i.id === linkedItemId) : null;
|
|
294
292
|
const confirmedItemId = linkedItem ? linkedItemId : null;
|
|
295
293
|
|
|
294
|
+
if (existingIds.has(prId)) {
|
|
295
|
+
if (confirmedItemId) {
|
|
296
|
+
addPrLink(prId, confirmedItemId);
|
|
297
|
+
const existing = existingPrs.find(p => p.id === prId);
|
|
298
|
+
if (existing && !(existing.prdItems || []).includes(confirmedItemId)) {
|
|
299
|
+
existing.prdItems = Array.isArray(existing.prdItems) ? existing.prdItems : [];
|
|
300
|
+
existing.prdItems.push(confirmedItemId);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
296
306
|
const prUrl = project.prUrlBase ? project.prUrlBase + ghPr.number : ghPr.html_url || '';
|
|
297
307
|
|
|
298
308
|
existingPrs.push({
|
|
@@ -306,6 +316,7 @@ async function reconcilePrs(config) {
|
|
|
306
316
|
url: prUrl,
|
|
307
317
|
prdItems: confirmedItemId ? [confirmedItemId] : [],
|
|
308
318
|
});
|
|
319
|
+
if (confirmedItemId) addPrLink(prId, confirmedItemId);
|
|
309
320
|
existingIds.add(prId);
|
|
310
321
|
projectAdded++;
|
|
311
322
|
|
package/engine/lifecycle.js
CHANGED
|
@@ -586,6 +586,7 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
586
586
|
status: 'active',
|
|
587
587
|
created: e.dateStamp(),
|
|
588
588
|
url: targetProject.prUrlBase ? targetProject.prUrlBase + prId : '',
|
|
589
|
+
prdItems: meta?.item?.id ? [meta.item.id] : [],
|
|
589
590
|
sourcePlan: meta?.item?.sourcePlan || '',
|
|
590
591
|
itemType: meta?.item?.itemType || ''
|
|
591
592
|
});
|
package/engine.js
CHANGED
|
@@ -1075,7 +1075,17 @@ function completeDispatch(id, result = 'success', reason = '', resultSummary = '
|
|
|
1075
1075
|
if (result === 'error' && item.meta?.dispatchKey && retryableFailure) setCooldownFailure(item.meta.dispatchKey);
|
|
1076
1076
|
|
|
1077
1077
|
if (processWorkItemFailure && result === 'error' && item.meta?.item?.id) {
|
|
1078
|
-
|
|
1078
|
+
let retries = (item.meta.item._retryCount || 0);
|
|
1079
|
+
try {
|
|
1080
|
+
const wiPath = item.meta.source === 'central-work-item' || item.meta.source === 'central-work-item-fanout'
|
|
1081
|
+
? path.join(MINIONS_DIR, 'work-items.json')
|
|
1082
|
+
: item.meta.project?.name ? projectWorkItemsPath({ name: item.meta.project.name, localPath: item.meta.project.localPath }) : null;
|
|
1083
|
+
if (wiPath) {
|
|
1084
|
+
const items = safeJson(wiPath) || [];
|
|
1085
|
+
const wi = items.find(i => i.id === item.meta.item.id);
|
|
1086
|
+
if (wi) retries = wi._retryCount || 0;
|
|
1087
|
+
}
|
|
1088
|
+
} catch {}
|
|
1079
1089
|
if (retryableFailure && retries < 3) {
|
|
1080
1090
|
log('info', `Dispatch error for ${item.meta.item.id} — auto-retry ${retries + 1}/3`);
|
|
1081
1091
|
updateWorkItemStatus(item.meta, 'pending', '');
|
|
@@ -1083,9 +1093,8 @@ function completeDispatch(id, result = 'success', reason = '', resultSummary = '
|
|
|
1083
1093
|
if (item.meta?.dispatchKey) {
|
|
1084
1094
|
try {
|
|
1085
1095
|
mutateDispatch((dp) => {
|
|
1086
|
-
const before = Array.isArray(dp.completed) ? dp.completed.length : 0;
|
|
1087
1096
|
dp.completed = Array.isArray(dp.completed) ? dp.completed.filter(d => d.meta?.dispatchKey !== item.meta.dispatchKey) : [];
|
|
1088
|
-
return dp
|
|
1097
|
+
return dp;
|
|
1089
1098
|
});
|
|
1090
1099
|
} catch {}
|
|
1091
1100
|
}
|
|
@@ -1176,7 +1185,7 @@ function areDependenciesMet(item, config) {
|
|
|
1176
1185
|
log('warn', `Dependency ${depId} not found for ${item.id} (plan: ${sourcePlan}) — treating as unmet`);
|
|
1177
1186
|
return false;
|
|
1178
1187
|
}
|
|
1179
|
-
if (depItem.status === 'failed'
|
|
1188
|
+
if (depItem.status === 'failed') return 'failed';
|
|
1180
1189
|
if (depItem.status !== 'done' && depItem.status !== 'in-pr') return false; // Pending, dispatched, or retrying — wait (in-pr accepted for backward compat)
|
|
1181
1190
|
}
|
|
1182
1191
|
return true;
|
|
@@ -1211,17 +1220,22 @@ function writeInboxAlert(slug, content) {
|
|
|
1211
1220
|
} catch {}
|
|
1212
1221
|
}
|
|
1213
1222
|
|
|
1214
|
-
// Reconciles work items against known PRs
|
|
1215
|
-
//
|
|
1216
|
-
//
|
|
1223
|
+
// Reconciles work items against known PRs.
|
|
1224
|
+
// Primary linkage comes from prdItems in pull-requests.json; fallback linkage
|
|
1225
|
+
// uses engine/pr-links.json so matching does not depend on branch/title parsing.
|
|
1217
1226
|
// onlyIds: if provided, only items whose ID is in this Set are eligible.
|
|
1218
1227
|
function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
1228
|
+
const prLinks = shared.getPrLinks();
|
|
1219
1229
|
let reconciled = 0;
|
|
1220
1230
|
for (const wi of items) {
|
|
1221
1231
|
if (wi.status !== 'pending' || wi._pr) continue;
|
|
1222
1232
|
if (onlyIds && !onlyIds.has(wi.id)) continue;
|
|
1223
1233
|
|
|
1224
|
-
|
|
1234
|
+
let exactPr = allPrs.find(pr => (pr.prdItems || []).includes(wi.id));
|
|
1235
|
+
if (!exactPr) {
|
|
1236
|
+
const linkedPrId = Object.keys(prLinks).find(prId => prLinks[prId] === wi.id);
|
|
1237
|
+
if (linkedPrId) exactPr = allPrs.find(pr => pr.id === linkedPrId) || { id: linkedPrId };
|
|
1238
|
+
}
|
|
1225
1239
|
if (exactPr) {
|
|
1226
1240
|
wi.status = 'done';
|
|
1227
1241
|
wi._pr = exactPr.id;
|
|
@@ -2275,6 +2289,18 @@ function buildPrDispatch(agentId, config, project, pr, type, extraVars, taskLabe
|
|
|
2275
2289
|
};
|
|
2276
2290
|
}
|
|
2277
2291
|
|
|
2292
|
+
function clearPendingHumanFeedbackFlag(projectMeta, prId) {
|
|
2293
|
+
if (!prId) return;
|
|
2294
|
+
try {
|
|
2295
|
+
const prsPath = projectPrPath(projectMeta);
|
|
2296
|
+
const prs = safeJson(prsPath) || [];
|
|
2297
|
+
const target = prs.find(p => p.id === prId);
|
|
2298
|
+
if (!target?.humanFeedback?.pendingFix) return;
|
|
2299
|
+
target.humanFeedback.pendingFix = false;
|
|
2300
|
+
safeWrite(prsPath, prs);
|
|
2301
|
+
} catch {}
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2278
2304
|
/**
|
|
2279
2305
|
* Scan pull-requests.json for PRs needing review or fixes
|
|
2280
2306
|
*/
|
|
@@ -2347,7 +2373,7 @@ function discoverFromPrs(config, project) {
|
|
|
2347
2373
|
reviewer: 'Human Reviewer',
|
|
2348
2374
|
review_note: pr.humanFeedback.feedbackContent || 'See PR thread comments',
|
|
2349
2375
|
}, `Fix PR ${pr.id} — human feedback`, { dispatchKey: key, source: 'pr-human-feedback', pr, branch: pr.branch, project: projMeta });
|
|
2350
|
-
if (item) { newWork.push(item);
|
|
2376
|
+
if (item) { newWork.push(item); setCooldown(key); }
|
|
2351
2377
|
}
|
|
2352
2378
|
|
|
2353
2379
|
// PRs with build failures
|
|
@@ -2381,6 +2407,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
2381
2407
|
const items = safeJson(projectWorkItemsPath(project)) || [];
|
|
2382
2408
|
const cooldownMs = (src.cooldownMinutes || 0) * 60 * 1000;
|
|
2383
2409
|
const newWork = [];
|
|
2410
|
+
const prdSyncQueue = [];
|
|
2384
2411
|
const skipped = { gated: 0, noAgent: 0 };
|
|
2385
2412
|
let needsWrite = false;
|
|
2386
2413
|
|
|
@@ -2487,7 +2514,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
2487
2514
|
item.status = 'dispatched';
|
|
2488
2515
|
item.dispatched_at = ts();
|
|
2489
2516
|
item.dispatched_to = agentId;
|
|
2490
|
-
|
|
2517
|
+
prdSyncQueue.push({ id: item.id, sourcePlan: item.sourcePlan });
|
|
2491
2518
|
|
|
2492
2519
|
newWork.push({
|
|
2493
2520
|
type: workType,
|
|
@@ -2506,6 +2533,7 @@ function discoverFromWorkItems(config, project) {
|
|
|
2506
2533
|
if (newWork.length > 0) {
|
|
2507
2534
|
const workItemsPath = projectWorkItemsPath(project);
|
|
2508
2535
|
safeWrite(workItemsPath, items);
|
|
2536
|
+
for (const s of prdSyncQueue) syncPrdItemStatus(s.id, 'dispatched', s.sourcePlan);
|
|
2509
2537
|
}
|
|
2510
2538
|
|
|
2511
2539
|
if (needsWrite) safeWrite(projectWorkItemsPath(project), items);
|
|
@@ -2945,6 +2973,9 @@ function discoverWork(config) {
|
|
|
2945
2973
|
|
|
2946
2974
|
for (const item of allWork) {
|
|
2947
2975
|
addToDispatch(item);
|
|
2976
|
+
if (item.meta?.source === 'pr-human-feedback') {
|
|
2977
|
+
clearPendingHumanFeedbackFlag(item.meta.project, item.meta.pr?.id);
|
|
2978
|
+
}
|
|
2948
2979
|
}
|
|
2949
2980
|
|
|
2950
2981
|
if (allWork.length > 0) {
|
package/package.json
CHANGED