@yemi33/minions 0.1.454 → 0.1.456
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 +10 -0
- package/engine/ado.js +28 -0
- package/engine/github.js +28 -0
- package/engine/playbook.js +4 -3
- package/engine.js +23 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.456 (2026-04-07)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
- pre-dispatch live vote check prevents reviewing approved PRs
|
|
7
|
+
|
|
8
|
+
## 0.1.455 (2026-04-07)
|
|
9
|
+
|
|
10
|
+
### Fixes
|
|
11
|
+
- agents write one inbox file per task, not two
|
|
12
|
+
|
|
3
13
|
## 0.1.454 (2026-04-07)
|
|
4
14
|
|
|
5
15
|
### Fixes
|
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/playbook.js
CHANGED
|
@@ -241,12 +241,13 @@ function renderPlaybook(type, vars) {
|
|
|
241
241
|
content += `**Never delete, move, or overwrite files in \`knowledge/\`.** The sweep (consolidation engine) is the only process that writes to \`knowledge/\`. If you think a KB file is wrong, note it in your learnings file — do not touch \`knowledge/\` directly.\n`;
|
|
242
242
|
|
|
243
243
|
// Inject learnings requirement
|
|
244
|
-
content += `\n\n---\n\n## REQUIRED: Write Learnings\n\n`;
|
|
245
|
-
content += `After completing your task, you MUST write a findings/learnings file to:\n`;
|
|
246
244
|
const timeStamp = new Date().toISOString().slice(11, 16).replace(':', '');
|
|
247
245
|
const inboxSlug = [vars.agent_id || 'agent', vars.task_id || '', dateStamp(), timeStamp].filter(Boolean).join('-');
|
|
246
|
+
content += `\n\n---\n\n## REQUIRED: Write Learnings\n\n`;
|
|
247
|
+
content += `After completing your task, write **one** findings file to:\n`;
|
|
248
248
|
content += `\`${MINIONS_DIR}/notes/inbox/${inboxSlug}.md\`\n\n`;
|
|
249
|
-
content +=
|
|
249
|
+
content += `**IMPORTANT: Write exactly ONE inbox file per task.** If the playbook above already specifies an inbox path, use THAT path instead and include your learnings in the same document. Do NOT create a second file — duplicates clog consolidation.\n\n`;
|
|
250
|
+
content += `Include in your findings file:\n`;
|
|
250
251
|
content += `- What you learned about the codebase\n`;
|
|
251
252
|
content += `- Patterns you discovered or established\n`;
|
|
252
253
|
content += `- Gotchas or warnings for future agents\n`;
|
package/engine.js
CHANGED
|
@@ -830,8 +830,8 @@ function reconcileItemsWithPrs(items, allPrs, { onlyIds } = {}) {
|
|
|
830
830
|
// ─── Inbox Consolidation (extracted to engine/consolidation.js) ──────────────
|
|
831
831
|
|
|
832
832
|
const { consolidateInbox } = require('./engine/consolidation');
|
|
833
|
-
const { pollPrStatus, pollPrHumanComments, reconcilePrs } = require('./engine/ado');
|
|
834
|
-
const { pollPrStatus: ghPollPrStatus, pollPrHumanComments: ghPollPrHumanComments, reconcilePrs: ghReconcilePrs } = require('./engine/github');
|
|
833
|
+
const { pollPrStatus, pollPrHumanComments, reconcilePrs, checkLiveReviewStatus: adoCheckLiveReview } = require('./engine/ado');
|
|
834
|
+
const { pollPrStatus: ghPollPrStatus, pollPrHumanComments: ghPollPrHumanComments, reconcilePrs: ghReconcilePrs, checkLiveReviewStatus: ghCheckLiveReview } = require('./engine/github');
|
|
835
835
|
|
|
836
836
|
// ─── State Snapshot ─────────────────────────────────────────────────────────
|
|
837
837
|
|
|
@@ -1291,6 +1291,27 @@ function discoverFromPrs(config, project) {
|
|
|
1291
1291
|
if (needsReview) {
|
|
1292
1292
|
const key = `review-${project?.name || 'default'}-${pr.id}`;
|
|
1293
1293
|
if (isAlreadyDispatched(key) || isOnCooldown(key, cooldownMs)) continue;
|
|
1294
|
+
|
|
1295
|
+
// Pre-dispatch live vote check — cached reviewStatus may be stale (poll lag ~6 min)
|
|
1296
|
+
try {
|
|
1297
|
+
const checkFn = project.repoHost === 'github' ? ghCheckLiveReview : adoCheckLiveReview;
|
|
1298
|
+
const liveStatus = checkFn(pr, project);
|
|
1299
|
+
if (liveStatus && liveStatus !== 'pending') {
|
|
1300
|
+
log('info', `Pre-dispatch vote check: ${pr.id} is ${liveStatus} (cached was pending) — skipping review`);
|
|
1301
|
+
pr.reviewStatus = liveStatus;
|
|
1302
|
+
// Persist so next tick doesn't re-check
|
|
1303
|
+
try {
|
|
1304
|
+
mutateJsonFileLocked(projectPrPath(project), data => {
|
|
1305
|
+
if (!Array.isArray(data)) return data;
|
|
1306
|
+
const target = data.find(p => p.id === pr.id);
|
|
1307
|
+
if (target) target.reviewStatus = liveStatus;
|
|
1308
|
+
return data;
|
|
1309
|
+
});
|
|
1310
|
+
} catch {}
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
} catch (e) { log('warn', `Pre-dispatch vote check for ${pr.id}: ${e.message}`); }
|
|
1314
|
+
|
|
1294
1315
|
const agentId = resolveAgent('review', config);
|
|
1295
1316
|
if (!agentId) continue;
|
|
1296
1317
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.456",
|
|
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"
|