@yemi33/minions 0.1.2264 → 0.1.2266
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/dashboard.js +145 -4
- package/engine/lifecycle.js +117 -3
- package/package.json +1 -1
package/dashboard.js
CHANGED
|
@@ -590,6 +590,126 @@ let _prRefVerifierOverride = null; // test seam
|
|
|
590
590
|
// (prRef, project) and returns true | false | null.
|
|
591
591
|
function _setPrRefVerifierForTest(fn) { _prRefVerifierOverride = (typeof fn === 'function') ? fn : null; }
|
|
592
592
|
|
|
593
|
+
// ── Live PR fetch helpers (W-mqtrnp7y00056bc8) ───────────────────────────────
|
|
594
|
+
//
|
|
595
|
+
// When GET /api/prs/:id is called for a PR that is not in the local tracker
|
|
596
|
+
// (e.g. merged before the engine started tracking, or from an unpolled repo),
|
|
597
|
+
// attempt a live fetch from the platform API and return a shaped record with
|
|
598
|
+
// _liveOnly: true. Falls back to the existing 404 when both lookups fail.
|
|
599
|
+
|
|
600
|
+
const LIVE_PR_FETCH_TIMEOUT_MS = 8000;
|
|
601
|
+
|
|
602
|
+
// Test seam: when set, replaces the real _fetchLivePrRecord call so unit tests
|
|
603
|
+
// can inject a mock without standing up a real gh CLI or ADO endpoint.
|
|
604
|
+
let _livePrFetchForTest = null;
|
|
605
|
+
function _setLivePrFetchForTest(fn) {
|
|
606
|
+
_livePrFetchForTest = typeof fn === 'function' ? fn : null;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function _mapGhPrToRecord(ghPr, canonicalId) {
|
|
610
|
+
const isMerged = !!ghPr.merged_at;
|
|
611
|
+
const status = isMerged ? 'merged' : ghPr.state === 'closed' ? 'closed' : 'active';
|
|
612
|
+
return {
|
|
613
|
+
id: canonicalId,
|
|
614
|
+
prNumber: ghPr.number,
|
|
615
|
+
title: (ghPr.title || `PR #${ghPr.number}`).slice(0, 120),
|
|
616
|
+
agent: (ghPr.user && ghPr.user.login) ? String(ghPr.user.login).toLowerCase() : 'unknown',
|
|
617
|
+
branch: (ghPr.head && ghPr.head.ref) || '',
|
|
618
|
+
status,
|
|
619
|
+
url: ghPr.html_url || '',
|
|
620
|
+
description: typeof ghPr.body === 'string' ? ghPr.body.slice(0, 500) : '',
|
|
621
|
+
_liveOnly: true,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function _mapAdoPrToRecord(adoPr, canonicalId, ref) {
|
|
626
|
+
const status = adoPr.status === 'completed' ? 'merged'
|
|
627
|
+
: adoPr.status === 'abandoned' ? 'abandoned'
|
|
628
|
+
: 'active';
|
|
629
|
+
const branch = typeof adoPr.sourceRefName === 'string'
|
|
630
|
+
? adoPr.sourceRefName.replace(/^refs\/heads\//, '')
|
|
631
|
+
: '';
|
|
632
|
+
const author = (adoPr.createdBy && (adoPr.createdBy.uniqueName || adoPr.createdBy.displayName)) || 'unknown';
|
|
633
|
+
const prUrl = `https://dev.azure.com/${encodeURIComponent(ref.org)}/${encodeURIComponent(ref.project)}/_git/${encodeURIComponent(ref.repo)}/pullrequest/${ref.number}`;
|
|
634
|
+
return {
|
|
635
|
+
id: canonicalId,
|
|
636
|
+
prNumber: adoPr.pullRequestId || ref.number,
|
|
637
|
+
title: (adoPr.title || `PR #${ref.number}`).slice(0, 120),
|
|
638
|
+
agent: String(author).toLowerCase(),
|
|
639
|
+
branch,
|
|
640
|
+
status,
|
|
641
|
+
url: prUrl,
|
|
642
|
+
description: typeof adoPr.description === 'string' ? adoPr.description.slice(0, 500) : '',
|
|
643
|
+
_liveOnly: true,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Exported for unit testing — fetch a PR live from the platform API and return
|
|
648
|
+
// a tracker-shaped record. Returns null when the canonical id is not parseable
|
|
649
|
+
// or the host is unrecognized. Throws on fetch errors so callers can decide
|
|
650
|
+
// whether to log-and-degrade or propagate.
|
|
651
|
+
//
|
|
652
|
+
// opts: test seams only — production callers pass no opts.
|
|
653
|
+
// _resolveTokenForSlug(slug) → token string or null
|
|
654
|
+
// _shellSafeGh(args, opts) → stdout string (argv-form gh CLI)
|
|
655
|
+
// _adoFetch(url, token) → parsed JSON object
|
|
656
|
+
// _adoToken → ADO bearer token string (skips ado.getAdoToken())
|
|
657
|
+
async function _fetchLivePrRecord(canonicalId, opts) {
|
|
658
|
+
const o = opts || {};
|
|
659
|
+
const parsed = shared.parseCanonicalPrId(canonicalId);
|
|
660
|
+
if (!parsed) return null; // bare number or unknown format
|
|
661
|
+
const { scope, prNumber } = parsed;
|
|
662
|
+
const colonIdx = scope.indexOf(':');
|
|
663
|
+
const host = scope.slice(0, colonIdx).toLowerCase();
|
|
664
|
+
const scopeSlug = scope.slice(colonIdx + 1);
|
|
665
|
+
|
|
666
|
+
if (host === 'github') {
|
|
667
|
+
const [owner, repo] = scopeSlug.split('/');
|
|
668
|
+
if (!owner || !repo) return null;
|
|
669
|
+
const ghSlug = shared.validateGhSlug(`${owner}/${repo}`);
|
|
670
|
+
const num = String(prNumber);
|
|
671
|
+
const resolveToken = o._resolveTokenForSlug || ghToken.resolveTokenForSlug;
|
|
672
|
+
const token = resolveToken(`${owner}/${repo}`);
|
|
673
|
+
const ghOpts = { timeout: LIVE_PR_FETCH_TIMEOUT_MS };
|
|
674
|
+
if (token) ghOpts.env = { ...process.env, GH_TOKEN: token };
|
|
675
|
+
const shellGh = o._shellSafeGh || shared.shellSafeGh;
|
|
676
|
+
const raw = await shellGh(['api', `repos/${ghSlug}/pulls/${num}`], ghOpts);
|
|
677
|
+
const ghPr = JSON.parse(raw);
|
|
678
|
+
return _mapGhPrToRecord(ghPr, canonicalId);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (host === 'ado') {
|
|
682
|
+
const segs = scopeSlug.split('/');
|
|
683
|
+
if (segs.length < 3) return null;
|
|
684
|
+
const [org, project, repo] = segs;
|
|
685
|
+
const ref = { host: 'ado', slug: scopeSlug, org, project, repo, number: prNumber, id: canonicalId };
|
|
686
|
+
const doFetch = o._adoFetch || (async (url, adoToken) => {
|
|
687
|
+
const res = await fetch(url, {
|
|
688
|
+
headers: { Authorization: `Bearer ${adoToken}`, 'Content-Type': 'application/json' },
|
|
689
|
+
signal: AbortSignal.timeout(LIVE_PR_FETCH_TIMEOUT_MS),
|
|
690
|
+
});
|
|
691
|
+
if (!res.ok) throw new Error(`ADO API ${res.status}: ${res.statusText}`);
|
|
692
|
+
const text = await res.text();
|
|
693
|
+
if (!text || text.trimStart().startsWith('<')) {
|
|
694
|
+
throw new Error(`ADO returned HTML instead of JSON for ${url.split('?')[0]}`);
|
|
695
|
+
}
|
|
696
|
+
return JSON.parse(text);
|
|
697
|
+
});
|
|
698
|
+
const adoTok = o._adoToken != null ? o._adoToken : await ado.getAdoToken();
|
|
699
|
+
if (!adoTok) throw new Error(`Could not acquire ADO token for ${canonicalId}`);
|
|
700
|
+
const orgBase = `https://dev.azure.com/${encodeURIComponent(org)}`;
|
|
701
|
+
const projEnc = encodeURIComponent(project);
|
|
702
|
+
const repoEnc = encodeURIComponent(repo);
|
|
703
|
+
const adoPr = await doFetch(
|
|
704
|
+
`${orgBase}/${projEnc}/_apis/git/repositories/${repoEnc}/pullRequests/${prNumber}?api-version=7.1`,
|
|
705
|
+
adoTok,
|
|
706
|
+
);
|
|
707
|
+
return _mapAdoPrToRecord(adoPr, canonicalId, ref);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return null; // unknown host
|
|
711
|
+
}
|
|
712
|
+
|
|
593
713
|
function _findTrackedPrRecord(prRef, project) {
|
|
594
714
|
if (!project) return null;
|
|
595
715
|
try {
|
|
@@ -6398,16 +6518,35 @@ const server = http.createServer(async (req, res) => {
|
|
|
6398
6518
|
|
|
6399
6519
|
// GET /api/prs/<id> — return a single fully-enriched PR record by canonical
|
|
6400
6520
|
// id (`<host>:<slug>#<number>`) or by bare number (P-79b47b0c). The in-stack
|
|
6401
|
-
// PR modal (renderPrs.openPrDetail) calls this on demand.
|
|
6402
|
-
// record
|
|
6521
|
+
// PR modal (renderPrs.openPrDetail) calls this on demand. Returns the local
|
|
6522
|
+
// tracker record when found; falls back to a live platform fetch (GitHub REST
|
|
6523
|
+
// or ADO) for PRs that were never added to the tracker. The live record
|
|
6524
|
+
// carries _liveOnly: true so the UI can optionally surface a "start tracking"
|
|
6525
|
+
// offer. Returns {"error":"pr not found"} when both lookups fail.
|
|
6403
6526
|
async function handlePrsById(req, res, match) {
|
|
6404
6527
|
try {
|
|
6405
6528
|
const id = decodeURIComponent(match[1] || '').trim();
|
|
6406
6529
|
if (!id) return jsonReply(res, 400, { error: 'id required' });
|
|
6407
6530
|
const prs = queries.getPullRequests();
|
|
6408
6531
|
const found = prs.find(p => p && (p.id === id || String(p.number) === id));
|
|
6409
|
-
if (
|
|
6410
|
-
|
|
6532
|
+
if (found) return jsonReply(res, 200, { pr: found });
|
|
6533
|
+
|
|
6534
|
+
// Not in local tracker — attempt live fetch from platform API.
|
|
6535
|
+
const liveFetch = _livePrFetchForTest || _fetchLivePrRecord;
|
|
6536
|
+
let liveRecord = null;
|
|
6537
|
+
try {
|
|
6538
|
+
liveRecord = await Promise.race([
|
|
6539
|
+
liveFetch(id),
|
|
6540
|
+
new Promise((_, rej) => {
|
|
6541
|
+
const t = setTimeout(() => rej(new Error(`live PR fetch timed out for ${id}`)), LIVE_PR_FETCH_TIMEOUT_MS + 1000);
|
|
6542
|
+
if (t.unref) t.unref();
|
|
6543
|
+
}),
|
|
6544
|
+
]);
|
|
6545
|
+
} catch (e) {
|
|
6546
|
+
shared.log('warn', `handlePrsById: live fetch failed for ${id}: ${e.message}`);
|
|
6547
|
+
}
|
|
6548
|
+
if (liveRecord) return jsonReply(res, 200, { pr: liveRecord });
|
|
6549
|
+
return jsonReply(res, 404, { error: 'pr not found' });
|
|
6411
6550
|
} catch (e) { return jsonReply(res, 500, { error: e.message }); }
|
|
6412
6551
|
}
|
|
6413
6552
|
|
|
@@ -14166,6 +14305,8 @@ function _installCrashHandlers() {
|
|
|
14166
14305
|
module.exports = {
|
|
14167
14306
|
getMcpServers,
|
|
14168
14307
|
_setPrRefVerifierForTest, // issue #246 — inject a fake loose-PR-ref verifier in handler tests
|
|
14308
|
+
_setLivePrFetchForTest, // W-mqtrnp7y00056bc8 — inject a mock live fetch for unit tests
|
|
14309
|
+
_fetchLivePrRecord, // W-mqtrnp7y00056bc8 — exported for direct unit testing
|
|
14169
14310
|
_parseClaudeMcpListLine,
|
|
14170
14311
|
_parseCopilotMcpListJson,
|
|
14171
14312
|
_readWorkspaceMcpServers,
|
package/engine/lifecycle.js
CHANGED
|
@@ -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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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.
|
|
3
|
+
"version": "0.1.2266",
|
|
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"
|