@yemi33/minions 0.1.889 → 0.1.890
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 +3 -1
- package/engine/lifecycle.js +92 -24
- package/engine/playbook.js +6 -5
- package/package.json +1 -1
- package/playbooks/review.md +12 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.890 (2026-04-11)
|
|
4
4
|
|
|
5
5
|
### Features
|
|
6
6
|
- add 13 missing CC action types for full dashboard parity
|
|
7
7
|
|
|
8
8
|
### Fixes
|
|
9
|
+
- parse review verdict from agent output for GitHub self-approval (#912) (#918)
|
|
10
|
+
- verify PRD file exists before marking plan-to-prd done (#893) (#911)
|
|
9
11
|
- re-open done work items when PRD item reset to missing (#906) (#910)
|
|
10
12
|
- require explicit vote step in review playbook
|
|
11
13
|
- CC queued message silently retries on 429 after abort
|
package/engine/lifecycle.js
CHANGED
|
@@ -779,6 +779,27 @@ function syncPrsFromOutput(output, agentId, meta, config) {
|
|
|
779
779
|
|
|
780
780
|
// ─── Post-Completion Hooks ──────────────────────────────────────────────────
|
|
781
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Parse review verdict from agent output text.
|
|
784
|
+
* Looks for "VERDICT: APPROVE" or "VERDICT: REQUEST_CHANGES" markers that the
|
|
785
|
+
* review playbook instructs agents to include. This is the primary mechanism for
|
|
786
|
+
* GitHub repos where formal --approve/--request-changes votes are blocked by
|
|
787
|
+
* self-approval restrictions.
|
|
788
|
+
* @param {string} text - Agent output / resultSummary
|
|
789
|
+
* @returns {'approved'|'changes-requested'|null}
|
|
790
|
+
*/
|
|
791
|
+
function parseReviewVerdict(text) {
|
|
792
|
+
if (!text) return null;
|
|
793
|
+
// Match "VERDICT: APPROVE" or "VERDICT: REQUEST_CHANGES" (case-insensitive, optional markdown bold)
|
|
794
|
+
const verdictMatch = text.match(/VERDICT[:\s]+\*{0,2}(APPROVE|REQUEST[_\s-]?CHANGES)\*{0,2}/i);
|
|
795
|
+
if (verdictMatch) {
|
|
796
|
+
const v = verdictMatch[1].toUpperCase().replace(/[\s-]/g, '_');
|
|
797
|
+
if (v === 'APPROVE') return 'approved';
|
|
798
|
+
if (v.includes('CHANGES')) return 'changes-requested';
|
|
799
|
+
}
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
|
|
782
803
|
async function updatePrAfterReview(agentId, pr, project, config, resultSummary) {
|
|
783
804
|
|
|
784
805
|
if (!pr?.id) return;
|
|
@@ -804,6 +825,15 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary)
|
|
|
804
825
|
}
|
|
805
826
|
} catch (e) { log('warn', `Post-review status check for ${pr.id}: ${e.message}`); }
|
|
806
827
|
|
|
828
|
+
// Fallback: if live check returned pending (e.g., GitHub self-approval blocked), parse verdict from agent output
|
|
829
|
+
if (!postReviewStatus) {
|
|
830
|
+
const verdict = parseReviewVerdict(resultSummary);
|
|
831
|
+
if (verdict) {
|
|
832
|
+
postReviewStatus = verdict;
|
|
833
|
+
log('info', `Parsed review verdict from agent output for ${pr.id}: ${verdict}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
807
837
|
const prPath = project ? shared.projectPrPath(project) : path.join(path.resolve(MINIONS_DIR, '..'), '.minions', 'pull-requests.json');
|
|
808
838
|
let updatedTarget = null;
|
|
809
839
|
shared.mutateJsonFileLocked(prPath, (prs) => {
|
|
@@ -839,7 +869,7 @@ async function updatePrAfterReview(agentId, pr, project, config, resultSummary)
|
|
|
839
869
|
}, { defaultValue: {} });
|
|
840
870
|
}
|
|
841
871
|
|
|
842
|
-
log('info', `Updated ${pr.id} → minions review: waiting by ${reviewerName}`);
|
|
872
|
+
log('info', `Updated ${pr.id} → minions review: ${postReviewStatus || 'waiting'} by ${reviewerName}`);
|
|
843
873
|
if (updatedTarget) createReviewFeedbackForAuthor(agentId, updatedTarget, config);
|
|
844
874
|
}
|
|
845
875
|
|
|
@@ -1499,6 +1529,54 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
1499
1529
|
// If decomposition produced nothing, fall through to mark parent as done
|
|
1500
1530
|
}
|
|
1501
1531
|
|
|
1532
|
+
// Verify plan-to-prd actually created the PRD file before marking done (#893)
|
|
1533
|
+
// Must run BEFORE updateWorkItemStatus(DONE) — otherwise _retryCount is deleted and retries never advance
|
|
1534
|
+
if (effectiveSuccess && type === WORK_TYPE.PLAN_TO_PRD && meta?.item?.id) {
|
|
1535
|
+
let prdFound = false;
|
|
1536
|
+
const expectedFile = meta.item._prdFilename;
|
|
1537
|
+
if (expectedFile) {
|
|
1538
|
+
prdFound = fs.existsSync(path.join(PRD_DIR, expectedFile));
|
|
1539
|
+
}
|
|
1540
|
+
if (!prdFound && meta.item.planFile) {
|
|
1541
|
+
try {
|
|
1542
|
+
for (const f of fs.readdirSync(PRD_DIR)) {
|
|
1543
|
+
if (!f.endsWith('.json')) continue;
|
|
1544
|
+
try {
|
|
1545
|
+
const prd = safeJson(path.join(PRD_DIR, f));
|
|
1546
|
+
if (prd && prd.source_plan === meta.item.planFile) { prdFound = true; break; }
|
|
1547
|
+
} catch {}
|
|
1548
|
+
}
|
|
1549
|
+
} catch {}
|
|
1550
|
+
}
|
|
1551
|
+
if (!prdFound) {
|
|
1552
|
+
skipDoneStatus = true;
|
|
1553
|
+
const wiPath = resolveWorkItemPath(meta);
|
|
1554
|
+
if (wiPath) {
|
|
1555
|
+
try {
|
|
1556
|
+
mutateJsonFileLocked(wiPath, data => {
|
|
1557
|
+
if (!Array.isArray(data)) return data;
|
|
1558
|
+
const w = data.find(i => i.id === meta.item.id);
|
|
1559
|
+
if (!w) return data;
|
|
1560
|
+
const retries = w._retryCount || 0;
|
|
1561
|
+
if (retries < ENGINE_DEFAULTS.maxRetries) {
|
|
1562
|
+
w.status = WI_STATUS.PENDING;
|
|
1563
|
+
w._retryCount = retries + 1;
|
|
1564
|
+
delete w.dispatched_at;
|
|
1565
|
+
delete w.completedAt;
|
|
1566
|
+
log('warn', `plan-to-prd ${meta.item.id} completed without PRD file — auto-retry ${retries + 1}/${ENGINE_DEFAULTS.maxRetries}`);
|
|
1567
|
+
} else {
|
|
1568
|
+
w.status = WI_STATUS.FAILED;
|
|
1569
|
+
w.failReason = 'PRD file not written after ' + ENGINE_DEFAULTS.maxRetries + ' attempts';
|
|
1570
|
+
w.failedAt = ts();
|
|
1571
|
+
log('warn', `plan-to-prd ${meta.item.id} failed — no PRD file after ${ENGINE_DEFAULTS.maxRetries} retries`);
|
|
1572
|
+
}
|
|
1573
|
+
return data;
|
|
1574
|
+
});
|
|
1575
|
+
} catch (err) { log('warn', `plan-to-prd PRD check: ${err.message}`); }
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1502
1580
|
if (effectiveSuccess && meta?.item?.id && !skipDoneStatus) {
|
|
1503
1581
|
meta._agentId = agentId;
|
|
1504
1582
|
updateWorkItemStatus(meta, WI_STATUS.DONE, '');
|
|
@@ -1622,26 +1700,15 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
1622
1700
|
}
|
|
1623
1701
|
}
|
|
1624
1702
|
|
|
1625
|
-
//
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
if (!
|
|
1634
|
-
try {
|
|
1635
|
-
for (const f of fs.readdirSync(PRD_DIR)) {
|
|
1636
|
-
if (!f.endsWith('.json')) continue;
|
|
1637
|
-
try {
|
|
1638
|
-
const prd = safeJson(path.join(PRD_DIR, f));
|
|
1639
|
-
if (prd && prd.source_plan === meta.item.planFile) { prdFound = true; break; }
|
|
1640
|
-
} catch {}
|
|
1641
|
-
}
|
|
1642
|
-
} catch {}
|
|
1643
|
-
}
|
|
1644
|
-
if (!prdFound) {
|
|
1703
|
+
// Old plan-to-prd PRD check removed — moved before updateWorkItemStatus(DONE) to fix #893
|
|
1704
|
+
// (retryCount was being deleted by done-marking before the check could read it)
|
|
1705
|
+
|
|
1706
|
+
// Verify review work items include a verdict — mirrors the PRD file existence check pattern.
|
|
1707
|
+
// If agent completed (exit 0) but output has no VERDICT: APPROVE/REQUEST_CHANGES, the review
|
|
1708
|
+
// is incomplete. Auto-retry up to maxRetries, then fail.
|
|
1709
|
+
if (effectiveSuccess && type === WORK_TYPE.REVIEW && meta?.item?.id) {
|
|
1710
|
+
const verdict = parseReviewVerdict(resultSummary);
|
|
1711
|
+
if (!verdict) {
|
|
1645
1712
|
const wiPath = resolveWorkItemPath(meta);
|
|
1646
1713
|
if (wiPath) {
|
|
1647
1714
|
mutateJsonFileLocked(wiPath, data => {
|
|
@@ -1653,11 +1720,11 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
|
|
|
1653
1720
|
w.status = WI_STATUS.PENDING;
|
|
1654
1721
|
w._retryCount = retries + 1;
|
|
1655
1722
|
delete w.dispatched_at;
|
|
1656
|
-
log('warn', `
|
|
1723
|
+
log('warn', `Review ${meta.item.id} completed without verdict — auto-retry ${retries + 1}/${ENGINE_DEFAULTS.maxRetries}`);
|
|
1657
1724
|
} else {
|
|
1658
1725
|
w.status = WI_STATUS.FAILED;
|
|
1659
|
-
w.failReason = '
|
|
1660
|
-
log('warn', `
|
|
1726
|
+
w.failReason = 'No review verdict after ' + ENGINE_DEFAULTS.maxRetries + ' attempts';
|
|
1727
|
+
log('warn', `Review ${meta.item.id} failed — no verdict after ${ENGINE_DEFAULTS.maxRetries} retries`);
|
|
1661
1728
|
}
|
|
1662
1729
|
return data;
|
|
1663
1730
|
});
|
|
@@ -1819,6 +1886,7 @@ module.exports = {
|
|
|
1819
1886
|
createReviewFeedbackForAuthor,
|
|
1820
1887
|
updateMetrics,
|
|
1821
1888
|
parseAgentOutput,
|
|
1889
|
+
parseReviewVerdict,
|
|
1822
1890
|
parseStructuredCompletion,
|
|
1823
1891
|
runPostCompletionHooks,
|
|
1824
1892
|
syncPrdFromPrs,
|
package/engine/playbook.js
CHANGED
|
@@ -81,13 +81,14 @@ function getPrVoteInstructions(project) {
|
|
|
81
81
|
if (host === 'github') {
|
|
82
82
|
const org = project?.adoOrg || '';
|
|
83
83
|
const repo = project?.repoName || '';
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
`-
|
|
87
|
-
`-
|
|
84
|
+
return `**IMPORTANT: GitHub blocks self-approval** — all agents share the same credentials, so \`--approve\` and \`--request-changes\` will fail with "can't approve your own PR." Use \`--comment\` instead.\n\n` +
|
|
85
|
+
`Submit your review verdict using \`gh pr review\` with \`--comment\`:\n` +
|
|
86
|
+
`- Approve: \`gh pr review <number> --comment --body "VERDICT: APPROVE\\n\\n<your review details>" --repo ${org}/${repo}\`\n` +
|
|
87
|
+
`- Request changes: \`gh pr review <number> --comment --body "VERDICT: REQUEST_CHANGES\\n\\n<your review details>" --repo ${org}/${repo}\`\n` +
|
|
88
88
|
`- Replace <number> with the PR number\n` +
|
|
89
89
|
`- Always set --repo to \`${org}/${repo}\` to target the correct repository\n` +
|
|
90
|
-
`-
|
|
90
|
+
`- **Your comment body MUST start with \`VERDICT: APPROVE\` or \`VERDICT: REQUEST_CHANGES\`** on its own line — the engine parses this to record your vote\n` +
|
|
91
|
+
`- Do NOT use \`--approve\` or \`--request-changes\` flags — they will fail`;
|
|
91
92
|
}
|
|
92
93
|
return `Use \`mcp__azure-ado__repo_update_pull_request_reviewers\`:\n- repositoryId: \`${repoId}\`\n- Set your reviewer vote on the PR (10=approve, 5=approve-with-suggestions, -10=reject)`;
|
|
93
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.890",
|
|
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"
|
package/playbooks/review.md
CHANGED
|
@@ -40,28 +40,22 @@ Use subagents only for genuinely parallel, independent tasks (e.g., reviewing un
|
|
|
40
40
|
- Verdict: **APPROVE**
|
|
41
41
|
- Note any minor non-blocking suggestions
|
|
42
42
|
|
|
43
|
-
## Post Review —
|
|
43
|
+
## Post Review — Submit your verdict
|
|
44
44
|
|
|
45
|
-
You MUST
|
|
45
|
+
You MUST post a review comment with a clear verdict. The engine parses your verdict to update PR status — **a review without a verdict line is incomplete and will be retried.**
|
|
46
46
|
|
|
47
|
-
###
|
|
48
|
-
|
|
49
|
-
{{pr_comment_instructions}}
|
|
50
|
-
- content: Your full review with verdict, findings, and sign-off
|
|
51
|
-
- Sign: `Review by Minions ({{agent_name}} — {{agent_role}})`
|
|
52
|
-
|
|
53
|
-
### Step 2: Submit a formal review vote — THIS IS REQUIRED
|
|
54
|
-
|
|
55
|
-
**This is a separate action from Step 1.** Posting a comment does NOT submit a vote. You must explicitly run the vote command:
|
|
47
|
+
### Post your review with verdict
|
|
56
48
|
|
|
57
49
|
{{pr_vote_instructions}}
|
|
58
|
-
- If your verdict is **APPROVE**: use `--approve`
|
|
59
|
-
- If your verdict is **REQUEST_CHANGES**: use `--request-changes`
|
|
60
|
-
- If you have minor non-blocking suggestions only: use `--approve` with a note
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
Your review body **MUST** start with one of these verdict lines (exactly as shown):
|
|
52
|
+
- `VERDICT: APPROVE` — if the code is ready to merge
|
|
53
|
+
- `VERDICT: REQUEST_CHANGES` — if there are issues that must be fixed
|
|
54
|
+
|
|
55
|
+
Follow the verdict line with your detailed review findings, then sign off:
|
|
56
|
+
- Sign: `Review by Minions ({{agent_name}} — {{agent_role}})`
|
|
63
57
|
|
|
64
|
-
After running the
|
|
58
|
+
After running the command, confirm it succeeded (check the command output for errors). If it fails, retry once.
|
|
65
59
|
|
|
66
60
|
## Handling Merge Conflicts
|
|
67
61
|
If you encounter merge conflicts (e.g., the PR shows conflicts):
|
|
@@ -71,9 +65,7 @@ If you encounter merge conflicts (e.g., the PR shows conflicts):
|
|
|
71
65
|
|
|
72
66
|
## When to Stop
|
|
73
67
|
|
|
74
|
-
Your task is complete
|
|
75
|
-
1. Review comment posted (Step 1)
|
|
76
|
-
2. Formal vote submitted via `gh pr review --approve` or `--request-changes` (Step 2)
|
|
68
|
+
Your task is complete when your review comment (with `VERDICT: APPROVE` or `VERDICT: REQUEST_CHANGES` on the first line) has been posted successfully.
|
|
77
69
|
|
|
78
|
-
Do NOT stop
|
|
70
|
+
Do NOT stop before posting the review. Do NOT continue reading unrelated files after posting.
|
|
79
71
|
|