@yemi33/minions 0.1.2228 → 0.1.2229
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/bin/minions.js +1 -0
- package/docs/harness-transparency.md +9 -5
- package/engine/cli.js +78 -3
- package/engine/github.js +3 -6
- package/engine/shared.js +27 -0
- package/package.json +1 -1
- package/playbooks/shared-rules.md +6 -2
package/bin/minions.js
CHANGED
|
@@ -1168,6 +1168,7 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
1168
1168
|
minions cleanup Clean temp files, worktrees, zombies
|
|
1169
1169
|
minions pr comment <repo> <n> Post a marker-prepended PR comment via gh
|
|
1170
1170
|
--agent <id> --kind <k> [--wi <id>] (--body-file <f> | --body <text>)
|
|
1171
|
+
[--harness-file <f> | --harness-json <j>] folds in a "Harnesses used" section
|
|
1171
1172
|
minions nuke --confirm Factory reset (delete state, reset config to defaults)
|
|
1172
1173
|
minions uninstall --confirm Remove everything + uninstall npm package
|
|
1173
1174
|
|
|
@@ -110,11 +110,15 @@ evaluation pass) can see what tooling drove a dispatch:
|
|
|
110
110
|
and ⚠️-marked), and returns `''` for an empty/absent record. The GitHub
|
|
111
111
|
comment path folds it in via `engine/gh-comment.js#buildMinionsCommentBody`
|
|
112
112
|
(optional `harnessUsed` arg, threaded through `postPrComment` /
|
|
113
|
-
`postPrReviewComment` / `postPrReview`); the Azure DevOps path
|
|
114
|
-
engine-
|
|
115
|
-
|
|
116
|
-
`
|
|
117
|
-
|
|
113
|
+
`postPrReviewComment` / `postPrReview`); the Azure DevOps path mirrors it via
|
|
114
|
+
`engine/ado-comment.js#postAdoPrComment` (same builder, same `harnessUsed`
|
|
115
|
+
arg). Both posters are reached from the `minions pr comment` CLI, which turns
|
|
116
|
+
`--harness-file` / `--harness-json` into the grounded record and folds the
|
|
117
|
+
**byte-identical** section in for the agent. Only the raw `gh pr comment` /
|
|
118
|
+
`az repos pr comment` / REST fallbacks bypass that chokepoint, so on those
|
|
119
|
+
paths review/fix agents append the section themselves per
|
|
120
|
+
`playbooks/shared-rules.md` → "Harness transparency / self-report". Every
|
|
121
|
+
surface consumes the one renderer, so there is no second formatter to drift.
|
|
118
122
|
2. **notes/inbox digest** — harness usage is summarized into the learnings /
|
|
119
123
|
inbox stream that feeds consolidation, so cross-task patterns ("everyone
|
|
120
124
|
reaches for skill X on Android fixes") become visible to the team-memory
|
package/engine/cli.js
CHANGED
|
@@ -250,7 +250,7 @@ const CLI_COMMAND_DOCS = Object.freeze({
|
|
|
250
250
|
'mcp-sync': { args: '', summary: 'Print harness propagation diagnostic (same source as `minions doctor --harness`; read-only, no writes)' },
|
|
251
251
|
doctor: { args: '[--harness]', summary: 'Check prerequisites and runtime health (--harness: print harness propagation diagnostic)' },
|
|
252
252
|
config: { args: 'set-cli <R> [--model M]', summary: 'Persist defaultCli/defaultModel without starting' },
|
|
253
|
-
pr: { args: 'comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] [--body-file <f>|--body <text>]', summary: 'Post a marker-prepended PR comment via gh' },
|
|
253
|
+
pr: { args: 'comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] [--harness-file <f>|--harness-json <j>] [--body-file <f>|--body <text>]', summary: 'Post a marker-prepended PR comment via gh' },
|
|
254
254
|
bridge: { args: 'status|health|enable|disable', summary: 'Constellation bridge: toggle and inspect the read-only cross-repo feed' },
|
|
255
255
|
});
|
|
256
256
|
|
|
@@ -413,6 +413,68 @@ function _applyRuntimeFlags({ cli, model, modelExplicit }) {
|
|
|
413
413
|
return { warnings, applied: true };
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
// resolveHarnessUsedForComment(flags) — P-7a3c9e21. Turn the `minions pr comment`
|
|
417
|
+
// harness flags into a grounded `harnessUsed` record suitable for the posters'
|
|
418
|
+
// `harnessUsed` param (engine/gh-comment.js / engine/ado-comment.js), which fold
|
|
419
|
+
// it via buildMinionsCommentBody -> buildHarnessUsedSection.
|
|
420
|
+
//
|
|
421
|
+
// Source precedence: `--harness-file <path>` (JSON on disk) wins over the inline
|
|
422
|
+
// `--harness-json <json>` convenience; neither supplied -> returns undefined so
|
|
423
|
+
// the comment body is byte-identical to today (no section). Read/parse failures
|
|
424
|
+
// are HARD errors (process.exit(2) with the offending message) — never a silent
|
|
425
|
+
// drop. Grounding uses the spawn-time `_harnessPropagated` manifest located by
|
|
426
|
+
// dispatch id (explicit `--dispatch-id` -> else basename(MINIONS_COMPLETION_REPORT,
|
|
427
|
+
// '.json')); an unresolvable manifest passes null, marking every entry
|
|
428
|
+
// grounded:false — the honest "no record" state, not a fatal condition.
|
|
429
|
+
function resolveHarnessUsedForComment(flags) {
|
|
430
|
+
const fileArg = flags['harness-file'];
|
|
431
|
+
const jsonArg = flags['harness-json'];
|
|
432
|
+
|
|
433
|
+
let raw;
|
|
434
|
+
if (fileArg !== undefined) {
|
|
435
|
+
try {
|
|
436
|
+
raw = fs.readFileSync(fileArg, 'utf8');
|
|
437
|
+
} catch (e) {
|
|
438
|
+
console.error(`error: could not read --harness-file ${fileArg}: ${e.message}`);
|
|
439
|
+
process.exit(2);
|
|
440
|
+
}
|
|
441
|
+
} else if (jsonArg !== undefined) {
|
|
442
|
+
raw = String(jsonArg);
|
|
443
|
+
} else {
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let parsed;
|
|
448
|
+
try {
|
|
449
|
+
parsed = JSON.parse(raw);
|
|
450
|
+
} catch (e) {
|
|
451
|
+
const src = fileArg !== undefined ? `--harness-file ${fileArg}` : '--harness-json';
|
|
452
|
+
console.error(`error: invalid JSON in ${src}: ${e.message}`);
|
|
453
|
+
process.exit(2);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Resolve the grounding manifest best-effort: explicit id -> completion-report
|
|
457
|
+
// basename. A missing/unresolvable manifest is non-fatal (groundHarnessUsed
|
|
458
|
+
// with null propagated marks everything grounded:false).
|
|
459
|
+
const dispatchId = flags['dispatch-id']
|
|
460
|
+
|| path.basename(process.env.MINIONS_COMPLETION_REPORT || '', '.json')
|
|
461
|
+
|| undefined;
|
|
462
|
+
let propagated = null;
|
|
463
|
+
if (dispatchId) {
|
|
464
|
+
try {
|
|
465
|
+
const dispatch = getDispatch();
|
|
466
|
+
for (const queue of ['active', 'completed', 'pending']) {
|
|
467
|
+
const list = Array.isArray(dispatch?.[queue]) ? dispatch[queue] : null;
|
|
468
|
+
if (!list) continue;
|
|
469
|
+
const found = list.find(d => d && d.id === dispatchId);
|
|
470
|
+
if (found && found._harnessPropagated) { propagated = found._harnessPropagated; break; }
|
|
471
|
+
}
|
|
472
|
+
} catch { propagated = null; }
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return shared.groundHarnessUsed(parsed, propagated);
|
|
476
|
+
}
|
|
477
|
+
|
|
416
478
|
const commands = {
|
|
417
479
|
start(...startArgs) {
|
|
418
480
|
// Apply --cli / --model fleet flags before any engine wiring touches
|
|
@@ -1982,6 +2044,11 @@ const commands = {
|
|
|
1982
2044
|
console.log(' GitHub: minions pr comment <repo> <prNumber> --agent <id> --kind <k> [--wi <id>] (--body-file <path> | --body <text>)');
|
|
1983
2045
|
console.log(' ADO: minions pr comment <prNumber> --host ado --ado-org <org> --ado-project <proj> --repo-id <id> --agent <id> --kind <k> [--wi <id>] (--body-file <path> | --body <text>)');
|
|
1984
2046
|
console.log('');
|
|
2047
|
+
console.log('Optional harness self-report (folded into the collapsible "Harnesses used" section):');
|
|
2048
|
+
console.log(' --harness-file <path> JSON file with the { skills, mcpServers, commands, docs } record (wins over --harness-json)');
|
|
2049
|
+
console.log(' --harness-json <json> same record inline as a JSON string');
|
|
2050
|
+
console.log(' --dispatch-id <id> grounding manifest override (else basename of $MINIONS_COMPLETION_REPORT)');
|
|
2051
|
+
console.log('');
|
|
1985
2052
|
console.log('Posts a PR comment with the hidden minions marker, the collapsible');
|
|
1986
2053
|
console.log('"Harnesses used" section, and the brand link folded in by the shared');
|
|
1987
2054
|
console.log('builder — so engine classifiers identify agent-authored comments by');
|
|
@@ -2040,6 +2107,12 @@ const commands = {
|
|
|
2040
2107
|
process.exit(2);
|
|
2041
2108
|
}
|
|
2042
2109
|
|
|
2110
|
+
// P-7a3c9e21 — thread the consulted-sources footprint into both posters so
|
|
2111
|
+
// the auto-folded "Harnesses used" <details> section the help already
|
|
2112
|
+
// promises is actually rendered. Resolved once (host-neutral); absent flags
|
|
2113
|
+
// -> undefined -> body byte-identical to today.
|
|
2114
|
+
const harnessUsed = resolveHarnessUsedForComment(flags);
|
|
2115
|
+
|
|
2043
2116
|
// ── Azure DevOps: <prNumber> --host ado --ado-org --ado-project --repo-id ──
|
|
2044
2117
|
if (isAdo) {
|
|
2045
2118
|
const [prNumberRaw] = positional;
|
|
@@ -2059,7 +2132,7 @@ const commands = {
|
|
|
2059
2132
|
const orgBase = flags['org-base'] || `https://dev.azure.com/${adoOrg}`;
|
|
2060
2133
|
const adoComment = require('./ado-comment');
|
|
2061
2134
|
adoComment.postAdoPrComment({
|
|
2062
|
-
orgBase, project, repositoryId, prNumber, body, agentId, kind, workItemId,
|
|
2135
|
+
orgBase, project, repositoryId, prNumber, body, agentId, kind, workItemId, harnessUsed,
|
|
2063
2136
|
}).then((result) => {
|
|
2064
2137
|
if (result && result.threadId) console.log(`ADO thread ${result.threadId} created`);
|
|
2065
2138
|
}).catch((e) => {
|
|
@@ -2084,7 +2157,7 @@ const commands = {
|
|
|
2084
2157
|
|
|
2085
2158
|
try {
|
|
2086
2159
|
const result = ghComment.postPrComment({
|
|
2087
|
-
repo, prNumber, body, agentId, kind, workItemId,
|
|
2160
|
+
repo, prNumber, body, agentId, kind, workItemId, harnessUsed,
|
|
2088
2161
|
});
|
|
2089
2162
|
if (result.output) console.log(result.output);
|
|
2090
2163
|
} catch (e) {
|
|
@@ -2223,6 +2296,8 @@ module.exports = {
|
|
|
2223
2296
|
_readDispatchPid: readDispatchPid,
|
|
2224
2297
|
_normalizeSessionBranch: normalizeSessionBranch,
|
|
2225
2298
|
_dispatchSessionBranch: dispatchSessionBranch,
|
|
2299
|
+
// P-7a3c9e21 — harness-flag resolver exported for CLI-threading tests.
|
|
2300
|
+
_resolveHarnessUsedForComment: resolveHarnessUsedForComment,
|
|
2226
2301
|
// W-mpcyvff6000pf828 (#2653) — heartbeat writer + factory exported for tests
|
|
2227
2302
|
_writeHeartbeatNow: writeHeartbeatNow,
|
|
2228
2303
|
_createHeartbeatInterval: createHeartbeatInterval,
|
package/engine/github.js
CHANGED
|
@@ -633,11 +633,11 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
633
633
|
if (updated) {
|
|
634
634
|
// Also update title/author/branch if still placeholder
|
|
635
635
|
const currentTitle = pr.title || '';
|
|
636
|
-
if (
|
|
636
|
+
if (shared.isPlaceholderPrTitle(currentTitle) || pr.agent === 'human' || pr.description === undefined) {
|
|
637
637
|
const prData = await ghApi(`/pulls/${prNum}`, slug);
|
|
638
638
|
if (prData) {
|
|
639
639
|
const latestTitle = pr.title || '';
|
|
640
|
-
if (
|
|
640
|
+
if (shared.isPlaceholderPrTitle(latestTitle)) {
|
|
641
641
|
pr.title = (prData.title || latestTitle).slice(0, 120);
|
|
642
642
|
}
|
|
643
643
|
if (pr.description === undefined) pr.description = (prData.body || '').slice(0, 500);
|
|
@@ -752,10 +752,7 @@ async function pollPrStatus(config) {
|
|
|
752
752
|
// stay stuck on "PR #N (polling...)" forever. `prData` is already in
|
|
753
753
|
// hand from line 622 — no extra API call needed.
|
|
754
754
|
const currentTitleForBackfill = pr.title || '';
|
|
755
|
-
if (
|
|
756
|
-
|| currentTitleForBackfill.includes('polling...')
|
|
757
|
-
|| /[{}"\[\]]/.test(currentTitleForBackfill)
|
|
758
|
-
|| /^[0-9a-f-]{8,}$/i.test(currentTitleForBackfill)) {
|
|
755
|
+
if (shared.isPlaceholderPrTitle(currentTitleForBackfill)) {
|
|
759
756
|
if (prData.title) {
|
|
760
757
|
const nextTitle = String(prData.title).slice(0, 120);
|
|
761
758
|
if (pr.title !== nextTitle) {
|
package/engine/shared.js
CHANGED
|
@@ -6553,6 +6553,32 @@ function findPrRecord(prs, prRef, project = null) {
|
|
|
6553
6553
|
return numberMatches.length === 1 ? numberMatches[0] : null;
|
|
6554
6554
|
}
|
|
6555
6555
|
|
|
6556
|
+
// Issue #289: single source of truth for "is this stored PR title a
|
|
6557
|
+
// placeholder/fallback that should be backfilled from the live platform
|
|
6558
|
+
// title on the next poll?". Recognizes:
|
|
6559
|
+
// - empty / missing titles
|
|
6560
|
+
// - the link-time "...(polling...)" placeholder
|
|
6561
|
+
// - serialized-JSON / agent-output leakage (contains {}"[] chars)
|
|
6562
|
+
// - bare hex/uuid-ish ids (>= 8 hex/dash chars)
|
|
6563
|
+
// - the fallback WRITER shapes "PR created by <agent>" (engine/lifecycle.js)
|
|
6564
|
+
// and bare "PR #<n>" (engine/ado.js central poller)
|
|
6565
|
+
// The WRITER (engine/lifecycle.js) and the DETECTORS (engine/github.js,
|
|
6566
|
+
// engine/ado.js) MUST agree — route every site through this helper so they
|
|
6567
|
+
// cannot drift apart again (the github poller previously never backfilled a
|
|
6568
|
+
// frozen "PR created by Ripley" title because its detector didn't know the shape).
|
|
6569
|
+
function isPlaceholderPrTitle(title) {
|
|
6570
|
+
const t = typeof title === 'string'
|
|
6571
|
+
? title.trim()
|
|
6572
|
+
: (title == null ? '' : String(title).trim());
|
|
6573
|
+
if (!t) return true;
|
|
6574
|
+
if (t.includes('polling...')) return true;
|
|
6575
|
+
if (/[{}"\[\]]/.test(t)) return true;
|
|
6576
|
+
if (/^[0-9a-f-]{8,}$/i.test(t)) return true;
|
|
6577
|
+
if (/^PR created by /i.test(t)) return true;
|
|
6578
|
+
if (/^PR #\d+$/i.test(t)) return true;
|
|
6579
|
+
return false;
|
|
6580
|
+
}
|
|
6581
|
+
|
|
6556
6582
|
function snapshotPrRecord(pr) {
|
|
6557
6583
|
if (pr === undefined) return undefined;
|
|
6558
6584
|
return JSON.parse(JSON.stringify(pr));
|
|
@@ -8834,6 +8860,7 @@ module.exports = {
|
|
|
8834
8860
|
isAdoPrScopeCompatible,
|
|
8835
8861
|
getCanonicalPrId,
|
|
8836
8862
|
findPrRecord,
|
|
8863
|
+
isPlaceholderPrTitle,
|
|
8837
8864
|
snapshotPrRecord,
|
|
8838
8865
|
applyPrFieldDelta,
|
|
8839
8866
|
normalizePrRecord,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2229",
|
|
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"
|
|
@@ -153,7 +153,11 @@ Copyable worked example:
|
|
|
153
153
|
|
|
154
154
|
Entries you report are **cross-checked** against what the engine propagated into your worktree and annotated `grounded: true | false` — never dropped — so a `grounded: false` entry surfaces a discrepancy for a human rather than being hidden. Full contract: `docs/harness-transparency.md`.
|
|
155
155
|
|
|
156
|
-
When you author a **PR review / fix summary comment
|
|
156
|
+
When you author a **PR review / fix summary comment**, the same harness footprint belongs in the comment body as a collapsible "Harnesses used" section so reviewers see which skills / MCPs / docs informed the change.
|
|
157
|
+
|
|
158
|
+
**Preferred — `minions pr comment` (GitHub and Azure DevOps `--host ado`): do NOT hand-render the section.** Write your `harnessUsed` record — the same `{ skills, mcpServers, commands, docs }` shape as the completion field above — to a JSON file and pass `--harness-file <path>` (or inline via `--harness-json <json>`). The shared builder (`engine/comment-format.js#buildHarnessUsedSection`, consumed by both `engine/gh-comment.js` and `engine/ado-comment.js`) folds the byte-identical `<details>` section into the body for you, so agent-authored and engine-authored comments match. Each entry is cross-checked against the harness manifest the engine propagated into your worktree and annotated `grounded: true | false` (never dropped); a `grounded: false`, ⚠️-marked bullet means **the engine couldn't confirm that affordance against the propagated manifest — not that you did anything wrong**.
|
|
159
|
+
|
|
160
|
+
**Raw fallbacks only — `gh pr comment` / `az repos pr comment` / ADO REST:** these bypass the CLI chokepoint, so you MUST hand-render the section yourself. Render it **identically on both hosts** — append this exact `<details>` block at the end of your comment body (omit it entirely when you used no affordances):
|
|
157
161
|
|
|
158
162
|
```markdown
|
|
159
163
|
<details>
|
|
@@ -167,7 +171,7 @@ When you author a **PR review / fix summary comment** (GitHub `minions pr commen
|
|
|
167
171
|
</details>
|
|
168
172
|
```
|
|
169
173
|
|
|
170
|
-
One bullet per affordance, in the order skills → MCP servers → commands → docs; `N` is the bullet count.
|
|
174
|
+
One bullet per affordance, in the order skills → MCP servers → commands → docs; `N` is the bullet count. Whether the CLI folds it in or you hand-render it on a raw fallback, the section is the human-readable mirror of your `harnessUsed` completion field — keep the two consistent.
|
|
171
175
|
|
|
172
176
|
## Minions API access
|
|
173
177
|
|