@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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.5 (2026-03-24)
4
+
5
+ ### Engine
6
+ - engine.js
7
+
8
+ ## 0.1.4 (2026-03-24)
9
+
10
+ ### Engine
11
+ - engine.js
12
+ - engine/ado.js
13
+ - engine/github.js
14
+ - engine/lifecycle.js
15
+
3
16
  ## 0.1.3 (2026-03-24)
4
17
 
5
18
  ### Engine
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
 
@@ -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
- const retries = (item.meta.item._retryCount || 0);
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.completed.length !== before ? dp : undefined;
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' && (depItem._retryCount || 0) >= 3) return 'failed'; // Only cascade after retries exhausted
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 via exact prdItems match only.
1215
- // reconcilePrs (ado.js) and syncPrsFromOutput (lifecycle.js) are responsible for
1216
- // correctly populating prdItems on PRs this function just reads that linkage.
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
- const exactPr = allPrs.find(pr => (pr.prdItems || []).includes(wi.id));
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); pr.humanFeedback.pendingFix = false; setCooldown(key); }
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
- syncPrdItemStatus(item.id, 'dispatched', item.sourcePlan);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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"