@yemi33/minions 0.1.2263 → 0.1.2265

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.
@@ -184,7 +184,8 @@
184
184
  }
185
185
  var prdBySource = {};
186
186
  for (var j = 0; j < prds.length; j++) {
187
- if (prds[j].sourcePlan) prdBySource[prds[j].sourcePlan] = prds[j];
187
+ var sp = prds[j].sourcePlan;
188
+ if (sp) prdBySource[sp.replace(/^plans\//, '')] = prds[j];
188
189
  }
189
190
  var cards = [];
190
191
  var claimedPrd = {};
@@ -371,9 +371,19 @@ function checkPlanCompletion(meta, config) {
371
371
  return `### ${w.id}: ${(w.title || 'Untitled').replace('Implement: ', '')}\n${criteria ? '**Acceptance Criteria:**\n' + criteria : ''}`;
372
372
  }).join('\n\n');
373
373
 
374
- const prSummary = prs.map(pr =>
375
- `- ${pr.id}: ${pr.title || ''} (branch: \`${pr.branch || '?'}\`) ${pr.url || ''}`
376
- ).join('\n') || '_No PRs surfaced for this project — verify the worktree directly._';
374
+ // Supplement the active-PR scan with WI-level _prUrl entries
375
+ // (e.g. PRs that were merged and left the active filter but had
376
+ // _prUrl stamped via stampWiPrRef at completion time).
377
+ const prUrlsAlreadyListed = new Set(prs.map(pr => pr.url).filter(Boolean));
378
+ const wiPrLines = itemsForProject
379
+ .filter(w => w._prUrl && !prUrlsAlreadyListed.has(w._prUrl))
380
+ .map(w => `- ${w._pr || w._prUrl}: (branch: \`${w.branch || '?'}\`) ${w._prUrl}`);
381
+ const prSummary = [
382
+ ...prs.map(pr =>
383
+ `- ${pr.id}: ${pr.title || ''} (branch: \`${pr.branch || '?'}\`) ${pr.url || ''}`
384
+ ),
385
+ ...wiPrLines,
386
+ ].join('\n') || '_No PRs surfaced for this project — verify the worktree directly._';
377
387
 
378
388
  const sharedBranchNote = isSharedBranch
379
389
  ? `\n**Shared-branch plan** — all changes are already on branch \`${plan.feature_branch}\`. Use this branch directly for the E2E PR instead of creating a new \`e2e/\` branch. Check if a PR already exists for this branch before creating one.\n`
@@ -767,6 +777,26 @@ function syncPrdItemStatus(itemId, status, sourcePlan) {
767
777
  } catch (err) { log('warn', `PRD status sync: ${err.message}`); }
768
778
  }
769
779
 
780
+ // W-mqtplpk6001oe6d5 — stamp workItemId back onto the PRD item when its
781
+ // materialised WI completes. dashboard/render-prd.js reads
782
+ // i.workItemId || i._workItemId || i.work_item_id || i.id — writing this
783
+ // field makes the back-reference explicit and avoids relying on id equality.
784
+ function stampPrdItemWorkItemId(itemId, sourcePlan) {
785
+ if (!itemId || !sourcePlan) return;
786
+ try {
787
+ const fpath = path.join(PRD_DIR, sourcePlan);
788
+ if (!fs.existsSync(fpath)) return;
789
+ const plan = safeJsonNoRestore(fpath);
790
+ const feature = plan?.missing_features?.find(f => f.id === itemId);
791
+ if (!feature || feature.workItemId === itemId) return;
792
+ mutateJsonFileLocked(fpath, (fresh) => {
793
+ const f = fresh?.missing_features?.find(x => x.id === itemId);
794
+ if (f && f.workItemId !== itemId) f.workItemId = itemId;
795
+ return fresh;
796
+ }, { skipWriteIfUnchanged: true });
797
+ } catch (err) { log('warn', `stampPrdItemWorkItemId: ${err.message}`); }
798
+ }
799
+
770
800
  // ─── PRD Backward-Scan Reconciliation (#929, #984) ─────────────────────────
771
801
  // Proactive counterpart to syncPrdItemStatus. Scans all active PRDs and:
772
802
  // 1. Promotes "missing" items to "updated" when a done work item already exists (#929)
@@ -1363,6 +1393,80 @@ async function _ensurePrEnrollmentForCompletedItem(meta, config) {
1363
1393
  }
1364
1394
  }
1365
1395
 
1396
+ // W-mqtplpk6001oe6d5 — stamp _pr/_prUrl/_prNumber onto the WI record at
1397
+ // completion time so the PRD-progress view shows the linked PR and
1398
+ // enforceVerifyPrContract's fast-path (item._pr || item._prUrl) succeeds
1399
+ // without waiting for the next reconcileItemsWithPrs tick.
1400
+ function stampWiPrRef(meta, config) {
1401
+ if (!meta || !meta.item || !meta.item.id) return;
1402
+ // No-op when already stamped — preserve any value set by earlier paths.
1403
+ if (meta.item._pr && meta.item._prUrl) return;
1404
+ const itemId = meta.item.id;
1405
+ const projects = (config && Array.isArray(config.projects))
1406
+ ? config.projects : shared.getProjects(config || {});
1407
+
1408
+ // 1. Search each project's pull-requests.json.
1409
+ let found = null;
1410
+ for (const p of projects) {
1411
+ const pr = safeJsonArr(projectPrPath(p)).find(
1412
+ r => Array.isArray(r.prdItems) && r.prdItems.includes(itemId)
1413
+ );
1414
+ if (pr) { found = pr; break; }
1415
+ }
1416
+
1417
+ // 2. Fall back to central pull-requests.json.
1418
+ if (!found) {
1419
+ found = safeJsonArr(path.join(MINIONS_DIR, 'pull-requests.json')).find(
1420
+ r => Array.isArray(r.prdItems) && r.prdItems.includes(itemId)
1421
+ );
1422
+ }
1423
+
1424
+ // 3. Fall back to pr-links index (catches cases where prdItems wasn't
1425
+ // populated but a pr-links entry links the PR to this WI).
1426
+ if (!found) {
1427
+ const prLinks = getPrLinks();
1428
+ const linkedPrId = Object.keys(prLinks).find(id => (prLinks[id] || []).includes(itemId));
1429
+ if (linkedPrId) {
1430
+ for (const p of projects) {
1431
+ const pr = safeJsonArr(projectPrPath(p)).find(r => r.id === linkedPrId);
1432
+ if (pr) { found = pr; break; }
1433
+ }
1434
+ if (!found) {
1435
+ found = safeJsonArr(path.join(MINIONS_DIR, 'pull-requests.json')).find(r => r.id === linkedPrId);
1436
+ }
1437
+ // Worst case: only the canonical ID is known — at least stamp _pr.
1438
+ if (!found) found = { id: linkedPrId };
1439
+ }
1440
+ }
1441
+
1442
+ if (!found) return;
1443
+
1444
+ const prId = found.id;
1445
+ const prUrl = found.url;
1446
+ const prNumber = found.prNumber ?? found.number ?? null;
1447
+
1448
+ // Stamp the on-disk WI record.
1449
+ const wiPath = resolveWorkItemPath(meta);
1450
+ if (wiPath) {
1451
+ try {
1452
+ mutateWorkItems(wiPath, items => {
1453
+ const wi = items.find(i => i.id === itemId);
1454
+ if (wi && !wi._pr) {
1455
+ if (prId) wi._pr = prId;
1456
+ if (prUrl) wi._prUrl = prUrl;
1457
+ if (prNumber != null) wi._prNumber = prNumber;
1458
+ }
1459
+ return items;
1460
+ });
1461
+ } catch (err) { log('warn', `stampWiPrRef on-disk: ${err.message}`); }
1462
+ }
1463
+
1464
+ // Also update meta.item in-memory so enforceVerifyPrContract fast-path passes.
1465
+ if (!meta.item._pr && prId) meta.item._pr = prId;
1466
+ if (!meta.item._prUrl && prUrl) meta.item._prUrl = prUrl;
1467
+ if (!meta.item._prNumber && prNumber != null) meta.item._prNumber = prNumber;
1468
+ }
1469
+
1366
1470
  function isPrAttachmentRequired(type, item, meta = {}) {
1367
1471
  if (!item?.id || item.skipPr) return false;
1368
1472
  // SETUP (W-mpbi6f2q00104957) is implicitly PR-exempt — the type itself
@@ -4933,6 +5037,9 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
4933
5037
  await _ensurePrEnrollmentForCompletedItem(meta, config);
4934
5038
  } catch (err) { log('warn', `PR enrollment back-fill: ${err.message}`); }
4935
5039
 
5040
+ // W-mqtplpk6001oe6d5 — stamp _pr/_prUrl/_prNumber onto WI at completion time.
5041
+ try { stampWiPrRef(meta, config); } catch (err) { log('warn', `stampWiPrRef: ${err.message}`); }
5042
+
4936
5043
  // Structured completion may report PR even when regex didn't find it
4937
5044
  const scHasPr = structuredCompletion && structuredCompletion.pr && structuredCompletion.pr !== 'N/A';
4938
5045
  if (scHasPr && prsCreatedCount === 0) {
@@ -5241,6 +5348,10 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
5241
5348
  meta._noopReason = noopRationale.slice(0, 500);
5242
5349
  }
5243
5350
  updateWorkItemStatus(meta, WI_STATUS.DONE, '');
5351
+ // W-mqtplpk6001oe6d5 — back-stamp workItemId onto the PRD item now that WI is done.
5352
+ if (meta.item.sourcePlan) {
5353
+ try { stampPrdItemWorkItemId(meta.item.id, meta.item.sourcePlan); } catch (err) { log('warn', `stampPrdItemWorkItemId: ${err.message}`); }
5354
+ }
5244
5355
  promoteCompletionArtifacts(meta, agentId, dispatchItem.id, structuredCompletion, { resultSummary });
5245
5356
  }
5246
5357
  // Failure retry is handled by completeDispatch in dispatch.js — not duplicated here.
@@ -6084,4 +6195,7 @@ module.exports = {
6084
6195
  // W-mq5uzmc6001d708f — post-hoc PR enrollment for orphan `_pr` pointers.
6085
6196
  enrollPrFromCanonicalId,
6086
6197
  _setEnrollmentGhRunnerForTest,
6198
+ // W-mqtplpk6001oe6d5 — stamp PR ref + workItemId onto WI and PRD item at completion time.
6199
+ stampWiPrRef,
6200
+ stampPrdItemWorkItemId,
6087
6201
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2263",
3
+ "version": "0.1.2265",
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"