@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 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 has no
114
- engine-side comment poster (agents post via `az repos pr comment` / REST),
115
- so review/fix agents append the **byte-identical** section themselves per
116
- `playbooks/shared-rules.md` "Harness transparency / self-report". Both
117
- surfaces consume the one renderer, so there is no second formatter to drift.
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 (!currentTitle || currentTitle.includes('polling...') || pr.agent === 'human' || pr.description === undefined) {
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 (!latestTitle || latestTitle.includes('polling...') || /[{}"\[\]]/.test(latestTitle) || /^[0-9a-f-]{8,}$/i.test(latestTitle)) {
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 (!currentTitleForBackfill
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.2228",
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** (GitHub `minions pr comment` / `gh pr comment`, or Azure DevOps `az repos pr comment`), fold the same harness footprint into the body as a collapsible section so reviewers see which skills / MCPs / docs informed the change. 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):
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. This is the human-readable mirror of your `harnessUsed` completion field keep the two consistent. The engine renders the byte-identical section via `engine/comment-format.js#buildHarnessUsedSection` when it composes a comment, so agent-authored and engine-authored sections match.
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