@yemi33/minions 0.1.455 → 0.1.457
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 +6 -0
- package/engine/ado.js +28 -0
- package/engine/github.js +28 -0
- package/engine.js +28 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/engine/ado.js
CHANGED
|
@@ -482,11 +482,39 @@ async function reconcilePrs(config) {
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Fetch live review status for a single PR from ADO (synchronous).
|
|
487
|
+
* Returns 'approved', 'changes-requested', 'waiting', or 'pending'.
|
|
488
|
+
* Returns null if the check fails (token unavailable, API error).
|
|
489
|
+
* Used as a pre-dispatch gate to avoid dispatching reviews for already-approved PRs.
|
|
490
|
+
*/
|
|
491
|
+
function checkLiveReviewStatus(pr, project) {
|
|
492
|
+
try {
|
|
493
|
+
const token = getAdoToken();
|
|
494
|
+
if (!token) return null;
|
|
495
|
+
const orgBase = shared.getAdoOrgBase(project);
|
|
496
|
+
const prNum = (pr.id || '').replace(/^PR-/, '');
|
|
497
|
+
const url = `${orgBase}/${project.adoProject}/_apis/git/repositories/${project.repositoryId}/pullrequests/${prNum}?api-version=7.1`;
|
|
498
|
+
const result = exec(`curl -s -H "Authorization: Bearer ${token}" "${url}"`, { encoding: 'utf-8', timeout: 15000, windowsHide: true });
|
|
499
|
+
const prData = JSON.parse(result);
|
|
500
|
+
const votes = (prData.reviewers || []).map(r => r.vote).filter(v => v !== undefined);
|
|
501
|
+
if (votes.length === 0) return 'pending';
|
|
502
|
+
if (votes.some(v => v === -10)) return 'changes-requested';
|
|
503
|
+
if (votes.some(v => v >= 5)) return 'approved';
|
|
504
|
+
if (votes.some(v => v === -5)) return 'waiting';
|
|
505
|
+
return 'pending';
|
|
506
|
+
} catch (e) {
|
|
507
|
+
log('warn', `Live review check for ${pr.id}: ${e.message}`);
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
485
512
|
module.exports = {
|
|
486
513
|
getAdoToken,
|
|
487
514
|
adoFetch,
|
|
488
515
|
pollPrStatus,
|
|
489
516
|
pollPrHumanComments,
|
|
490
517
|
reconcilePrs,
|
|
518
|
+
checkLiveReviewStatus,
|
|
491
519
|
};
|
|
492
520
|
|
package/engine/github.js
CHANGED
|
@@ -466,9 +466,37 @@ async function reconcilePrs(config) {
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Fetch live review status for a single PR from GitHub. Returns 'approved', 'changes-requested',
|
|
471
|
+
* 'waiting', or 'pending'. Returns null if the check fails.
|
|
472
|
+
*/
|
|
473
|
+
function checkLiveReviewStatus(pr, project) {
|
|
474
|
+
try {
|
|
475
|
+
const slug = getRepoSlug(project);
|
|
476
|
+
if (!slug) return null;
|
|
477
|
+
const prNum = (pr.id || '').replace(/^PR-/, '');
|
|
478
|
+
const reviews = ghApi(`/pulls/${prNum}/reviews`, slug);
|
|
479
|
+
if (!reviews || !Array.isArray(reviews)) return null;
|
|
480
|
+
const latestByUser = new Map();
|
|
481
|
+
for (const r of reviews) {
|
|
482
|
+
if (r.state === 'COMMENTED') continue;
|
|
483
|
+
latestByUser.set(r.user?.login || '', r.state);
|
|
484
|
+
}
|
|
485
|
+
const states = [...latestByUser.values()];
|
|
486
|
+
if (states.some(s => s === 'CHANGES_REQUESTED')) return 'changes-requested';
|
|
487
|
+
if (states.some(s => s === 'APPROVED')) return 'approved';
|
|
488
|
+
if (states.length > 0) return 'pending';
|
|
489
|
+
return 'pending';
|
|
490
|
+
} catch (e) {
|
|
491
|
+
log('warn', `Live review check for ${pr.id}: ${e.message}`);
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
469
496
|
module.exports = {
|
|
470
497
|
pollPrStatus,
|
|
471
498
|
pollPrHumanComments,
|
|
472
499
|
reconcilePrs,
|
|
500
|
+
checkLiveReviewStatus,
|
|
473
501
|
};
|
|
474
502
|
|
package/engine.js
CHANGED
|
@@ -319,6 +319,11 @@ function spawnAgent(dispatchItem, config) {
|
|
|
319
319
|
log('info', `Shared branch ${branchName} already checked out at ${existingWtPath} — reusing`);
|
|
320
320
|
worktreePath = existingWtPath;
|
|
321
321
|
} else { throw eShared; }
|
|
322
|
+
} else if (eShared.message?.includes('invalid reference') || eShared.message?.includes('not a valid ref')) {
|
|
323
|
+
// Branch doesn't exist yet (first item in plan) — create it from main
|
|
324
|
+
const mainRef = sanitizeBranch(shared.resolveMainBranch(rootDir, project.mainBranch));
|
|
325
|
+
log('info', `Shared branch ${branchName} not found — creating from ${mainRef}`);
|
|
326
|
+
runWorktreeAdd(rootDir, worktreePath, `-b "${branchName}" ${mainRef}`, _worktreeGitOpts, worktreeCreateRetries);
|
|
322
327
|
} else { throw eShared; }
|
|
323
328
|
}
|
|
324
329
|
} else {
|
|
@@ -830,8 +835,8 @@ function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
|
830
835
|
// ─── Inbox Consolidation (extracted to engine/consolidation.js) ──────────────
|
|
831
836
|
|
|
832
837
|
const { consolidateInbox } = require('./engine/consolidation');
|
|
833
|
-
const { pollPrStatus, pollPrHumanComments, reconcilePrs } = require('./engine/ado');
|
|
834
|
-
const { pollPrStatus: ghPollPrStatus, pollPrHumanComments: ghPollPrHumanComments, reconcilePrs: ghReconcilePrs } = require('./engine/github');
|
|
838
|
+
const { pollPrStatus, pollPrHumanComments, reconcilePrs, checkLiveReviewStatus: adoCheckLiveReview } = require('./engine/ado');
|
|
839
|
+
const { pollPrStatus: ghPollPrStatus, pollPrHumanComments: ghPollPrHumanComments, reconcilePrs: ghReconcilePrs, checkLiveReviewStatus: ghCheckLiveReview } = require('./engine/github');
|
|
835
840
|
|
|
836
841
|
// ─── State Snapshot ─────────────────────────────────────────────────────────
|
|
837
842
|
|
|
@@ -1291,6 +1296,27 @@ function discoverFromPrs(config, project) {
|
|
|
1291
1296
|
if (needsReview) {
|
|
1292
1297
|
const key = `review-${project?.name || 'default'}-${pr.id}`;
|
|
1293
1298
|
if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
|
|
1299
|
+
|
|
1300
|
+
// Pre-dispatch live vote check — cached reviewStatus may be stale (poll lag ~6 min)
|
|
1301
|
+
try {
|
|
1302
|
+
const checkFn = project.repoHost === 'github' ? ghCheckLiveReview : adoCheckLiveReview;
|
|
1303
|
+
const liveStatus = checkFn(pr, project);
|
|
1304
|
+
if (liveStatus && liveStatus !== 'pending') {
|
|
1305
|
+
log('info', `Pre-dispatch vote check: ${pr.id} is ${liveStatus} (cached was pending) — skipping review`);
|
|
1306
|
+
pr.reviewStatus = liveStatus;
|
|
1307
|
+
// Persist so next tick doesn't re-check
|
|
1308
|
+
try {
|
|
1309
|
+
mutateJsonFileLocked(projectPrPath(project), data => {
|
|
1310
|
+
if (!Array.isArray(data)) return data;
|
|
1311
|
+
const target = data.find(p => p.id === pr.id);
|
|
1312
|
+
if (target) target.reviewStatus = liveStatus;
|
|
1313
|
+
return data;
|
|
1314
|
+
});
|
|
1315
|
+
} catch {}
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
} catch (e) { log('warn', `Pre-dispatch vote check for ${pr.id}: ${e.message}`); }
|
|
1319
|
+
|
|
1294
1320
|
const agentId = resolveAgent('review', config);
|
|
1295
1321
|
if (!agentId) continue;
|
|
1296
1322
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.457",
|
|
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"
|