@mutmutco/cli 4.0.12 → 4.0.14

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/dist/main.cjs CHANGED
@@ -4510,6 +4510,11 @@ function formatPositionalTarget(commandPath3, argName, opts = {}) {
4510
4510
  if (concrete) return `mmi-cli ${commandPath3} ${concrete}`;
4511
4511
  return `mmi-cli ${commandPath3} <${argName}>`;
4512
4512
  }
4513
+ var FLAG_SYNONYMS = /* @__PURE__ */ new Map([["--outcome", "--reason"]]);
4514
+ function synonymFlagFor(flag, ownFlags) {
4515
+ const syn = FLAG_SYNONYMS.get(flag);
4516
+ return syn !== void 0 && ownFlags.includes(syn) ? syn : void 0;
4517
+ }
4513
4518
  function unknownTargetFlagMessage(flag, positional) {
4514
4519
  return `unknown option '${flag}' \u2014 this command takes its target as a positional: ${positional}`;
4515
4520
  }
@@ -10827,10 +10832,10 @@ var rollout_plan_default = {
10827
10832
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
10828
10833
  },
10829
10834
  baseline: {
10830
- version: "4.0.12",
10831
- tag: "v4.0.12",
10832
- commit: "0ead67177a73",
10833
- npm: "@mutmutco/cli@4.0.12"
10835
+ version: "4.0.14",
10836
+ tag: "v4.0.14",
10837
+ commit: "386f339f67c6",
10838
+ npm: "@mutmutco/cli@4.0.14"
10834
10839
  },
10835
10840
  exitCriterion: "fleet-n-of-n",
10836
10841
  hubOnlyShortcut: "forbidden",
@@ -10847,14 +10852,14 @@ var rollout_plan_default = {
10847
10852
  repo: "mutmutco/mmi-hub",
10848
10853
  role: "canary",
10849
10854
  schedule: "train",
10850
- v3Target: "v4.0.12"
10855
+ v3Target: "v4.0.14"
10851
10856
  }
10852
10857
  ],
10853
10858
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
10854
10859
  rollback: {
10855
10860
  independent: true,
10856
- mechanism: "npm dist-tag latest -> 4.0.12 and redeploy the Hub Lambda from tag v4.0.12 (0ead67177a73); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
10857
- v3Target: "v4.0.12 (@mutmutco/cli@4.0.12, tag commit 0ead67177a73 \u2014 last known-good release carrying the repo-index v4-only contract)"
10861
+ mechanism: "npm dist-tag latest -> 4.0.14 and redeploy the Hub Lambda from tag v4.0.14 (386f339f67c6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
10862
+ v3Target: "v4.0.14 (@mutmutco/cli@4.0.14, tag commit 386f339f67c6 \u2014 last known-good release carrying the repo-index v4-only contract)"
10858
10863
  }
10859
10864
  },
10860
10865
  {
@@ -17301,7 +17306,7 @@ async function executeWaveLand(plan, deps) {
17301
17306
  }
17302
17307
 
17303
17308
  // src/index.ts
17304
- var import_node_os18 = require("node:os");
17309
+ var import_node_os19 = require("node:os");
17305
17310
 
17306
17311
  // src/board.ts
17307
17312
  var import_node_child_process9 = require("node:child_process");
@@ -17412,14 +17417,16 @@ async function filterDependencyBlockedClaimables(items, client, opts = {}) {
17412
17417
  }
17413
17418
 
17414
17419
  // src/closing-keyword-guard.ts
17415
- var CLOSING_MENTION_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi;
17420
+ var CLOSING_MENTION_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+(?:#(\d+)|\[#(\d+)\]\([^)\s]+\))/gi;
17416
17421
  var NEGATION_RE = /\b(?:not|never|cannot|can't|don't|doesn't|didn't|won't|wouldn't|shouldn't|mustn't|without)\b/i;
17417
17422
  var CLAUSE_WINDOW = 120;
17418
- var NEGATED_CLOSING_PHRASE_RE = /\b(?:(?:does|do|did|will|would|should|must|can)\s+not|(?:is|was|are|were)\s+not|doesn't|don't|didn't|won't|wouldn't|shouldn't|mustn't|can't|cannot|never|without|not)\s+(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi;
17423
+ var NEGATED_CLOSING_PHRASE_RE = /\b(?:(?:does|do|did|will|would|should|must|can)\s+not|(?:is|was|are|were)\s+not|doesn't|don't|didn't|won't|wouldn't|shouldn't|mustn't|can't|cannot|never|without|not)\s+(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+(?:#(\d+)|\[#(\d+)\]\([^)\s]+\))/gi;
17419
17424
  function rewriteNegatedClosingPhrases(text) {
17420
17425
  if (!text.includes("#")) return text;
17421
17426
  const segments = text.split(/(```[\s\S]*?```|~~~[\s\S]*?~~~|`[^`\n]*`)/g);
17422
- return segments.map((seg, i) => i % 2 === 1 ? seg : seg.replace(NEGATED_CLOSING_PHRASE_RE, (_m, n) => `leaves #${n} open`)).join("");
17427
+ return segments.map(
17428
+ (seg, i) => i % 2 === 1 ? seg : seg.replace(NEGATED_CLOSING_PHRASE_RE, (_m, plain, linked) => `leaves #${plain ?? linked} open`)
17429
+ ).join("");
17423
17430
  }
17424
17431
  function findClosingMentions(text) {
17425
17432
  const mentions = [];
@@ -17436,7 +17443,8 @@ function findClosingMentions(text) {
17436
17443
  const clause = boundary === -1 ? windowText : windowText.slice(boundary + 1);
17437
17444
  mentions.push({
17438
17445
  keyword: (match[1] ?? "").toLowerCase(),
17439
- issue: Number(match[2]),
17446
+ issue: Number(match[2] ?? match[3]),
17447
+ // plain `#N` vs Markdown-linked `[#N](URL)` (#5338)
17440
17448
  negated: NEGATION_RE.test(clause)
17441
17449
  });
17442
17450
  }
@@ -23221,6 +23229,7 @@ var import_node_crypto9 = require("node:crypto");
23221
23229
  var import_node_crypto8 = require("node:crypto");
23222
23230
  var import_node_child_process13 = require("node:child_process");
23223
23231
  var import_node_fs25 = require("node:fs");
23232
+ var import_node_os12 = require("node:os");
23224
23233
  var import_node_path23 = require("node:path");
23225
23234
 
23226
23235
  // src/repo-index-v4/chunks.ts
@@ -23475,22 +23484,37 @@ function runEmbedderOnce(cwd, chunks, modelDirectory, createdAt) {
23475
23484
  if (!(0, import_node_fs25.existsSync)(file)) return { ok: false, reason: "embeddings-unavailable" };
23476
23485
  const request = { texts: chunks.map((chunk) => ({ id: chunk.id, text: embeddingInput(cwd, chunk) })), maxBatch: V4_EMBED_BATCH };
23477
23486
  const env = { ...process.env, ...modelDirectory ? { MMI_REPO_INDEXER_MODEL_DIR: modelDirectory } : {} };
23478
- const result = (0, import_node_child_process13.spawnSync)(process.execPath, [file], { input: JSON.stringify(request), encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
23487
+ const requestDir = (0, import_node_fs25.mkdtempSync)((0, import_node_path23.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-req-"));
23488
+ const requestFile = (0, import_node_path23.join)(requestDir, "request.json");
23489
+ (0, import_node_fs25.writeFileSync)(requestFile, JSON.stringify(request));
23490
+ let result;
23491
+ try {
23492
+ result = (0, import_node_child_process13.spawnSync)(process.execPath, [file, requestFile], { encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
23493
+ } finally {
23494
+ (0, import_node_fs25.rmSync)(requestDir, { recursive: true, force: true });
23495
+ }
23479
23496
  if (result.error || result.status !== 0) {
23480
23497
  const cleanExit2 = result.error === void 0 && result.signal === void 0 && typeof result.status === "number" && result.status !== 0;
23498
+ const stdout = String(result.stdout).trim();
23499
+ const stderrTail = String(result.stderr).trim().slice(-400) || void 0;
23481
23500
  let code;
23482
- let stderrTail;
23483
23501
  if (cleanExit2) {
23484
23502
  try {
23485
- code = JSON.parse(String(result.stdout))?.code;
23503
+ code = JSON.parse(stdout)?.code;
23486
23504
  } catch {
23487
23505
  }
23488
- stderrTail = String(result.stderr).trim().slice(-400) || void 0;
23489
23506
  }
23490
- return { ok: false, code, stderrTail, cleanExit: cleanExit2, detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}` };
23507
+ return {
23508
+ ok: false,
23509
+ code,
23510
+ stdoutTail: code ? void 0 : stdout.slice(-400) || void 0,
23511
+ stderrTail,
23512
+ cleanExit: cleanExit2,
23513
+ detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}`
23514
+ };
23491
23515
  }
23492
23516
  try {
23493
- const response = JSON.parse(result.stdout);
23517
+ const response = JSON.parse(String(result.stdout));
23494
23518
  if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return { ok: false, reason: "embeddings-unavailable" };
23495
23519
  const provenance = {
23496
23520
  provider: response.provenance.provider,
@@ -23517,8 +23541,9 @@ function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
23517
23541
  }
23518
23542
  const runner = last;
23519
23543
  const code = runner.code ? ` code=${runner.code}` : "";
23544
+ const stdout = runner.stdoutTail ? ` stdout=${runner.stdoutTail}` : "";
23520
23545
  const stderr = runner.stderrTail ? ` stderr=${runner.stderrTail}` : "";
23521
- throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stderr}`);
23546
+ throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stdout}${stderr}`);
23522
23547
  }
23523
23548
  async function buildRepoIndexV4(cwd, repo, opts = {}) {
23524
23549
  const { commit, defaultBranch, createdAt } = gitInfo(cwd);
@@ -23861,7 +23886,7 @@ async function gcRepoIndexCloud(deps) {
23861
23886
 
23862
23887
  // src/repo-index-sync.ts
23863
23888
  var import_node_fs27 = require("node:fs");
23864
- var import_node_os12 = require("node:os");
23889
+ var import_node_os13 = require("node:os");
23865
23890
  var import_node_path25 = require("node:path");
23866
23891
  var import_node_child_process14 = require("node:child_process");
23867
23892
 
@@ -24073,7 +24098,7 @@ async function syncEstateRepoIndex(opts) {
24073
24098
  }
24074
24099
  } catch {
24075
24100
  }
24076
- const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path25.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-"));
24101
+ const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path25.join)((0, import_node_os13.tmpdir)(), "mmi-repo-index-"));
24077
24102
  try {
24078
24103
  const reconciled = await reconcileRepoIndexCloud(repo, opts.deps);
24079
24104
  if (!reconciled.ok) {
@@ -25737,7 +25762,7 @@ function writeError(res) {
25737
25762
  // src/secrets-commands.ts
25738
25763
  var import_node_fs32 = require("node:fs");
25739
25764
  var import_node_path29 = require("node:path");
25740
- var import_node_os13 = require("node:os");
25765
+ var import_node_os14 = require("node:os");
25741
25766
 
25742
25767
  // src/secrets-diff.ts
25743
25768
  var TIMEOUT_MS2 = 8e3;
@@ -25860,7 +25885,7 @@ async function decryptRailsCredentials(input) {
25860
25885
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
25861
25886
  "puts JSON.generate(config.config)"
25862
25887
  ].join("\n");
25863
- const scriptDir = (0, import_node_fs32.mkdtempSync)((0, import_node_path29.join)((0, import_node_os13.tmpdir)(), "mmi-rails-decrypt-"));
25888
+ const scriptDir = (0, import_node_fs32.mkdtempSync)((0, import_node_path29.join)((0, import_node_os14.tmpdir)(), "mmi-rails-decrypt-"));
25864
25889
  const scriptPath = (0, import_node_path29.join)(scriptDir, "decrypt.rb");
25865
25890
  (0, import_node_fs32.writeFileSync)(scriptPath, script, "utf8");
25866
25891
  try {
@@ -27082,7 +27107,7 @@ function registerEdgeCommands(program3) {
27082
27107
 
27083
27108
  // src/bootstrap-commands.ts
27084
27109
  var import_node_fs34 = require("node:fs");
27085
- var import_node_os14 = require("node:os");
27110
+ var import_node_os15 = require("node:os");
27086
27111
  var import_node_path32 = require("node:path");
27087
27112
 
27088
27113
  // src/bootstrap-drift.ts
@@ -28286,7 +28311,7 @@ function registerBootstrapCommands(program3) {
28286
28311
  const readFile6 = (p) => (0, import_node_fs34.existsSync)(p) ? (0, import_node_fs34.readFileSync)(p, "utf8") : null;
28287
28312
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
28288
28313
  const putSeed = async (target, content, ref, sha) => {
28289
- const tmp = (0, import_node_path32.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28314
+ const tmp = (0, import_node_path32.join)((0, import_node_os15.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28290
28315
  (0, import_node_fs34.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
28291
28316
  try {
28292
28317
  await gh(contentPutInputArgs(repo, target, tmp));
@@ -28757,7 +28782,7 @@ LIVE apply to ${repo}:
28757
28782
  } catch {
28758
28783
  existingSha = void 0;
28759
28784
  }
28760
- const tmp = (0, import_node_path32.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28785
+ const tmp = (0, import_node_path32.join)((0, import_node_os15.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28761
28786
  const desiredContent = desiredByRepo.get(rec.repo);
28762
28787
  if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
28763
28788
  (0, import_node_fs34.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
@@ -28930,7 +28955,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
28930
28955
  } catch {
28931
28956
  existingSha = void 0;
28932
28957
  }
28933
- const tmp = (0, import_node_path32.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28958
+ const tmp = (0, import_node_path32.join)((0, import_node_os15.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
28934
28959
  (0, import_node_fs34.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
28935
28960
  try {
28936
28961
  await gh(contentPutInputArgs(repo, seed.target, tmp));
@@ -29441,7 +29466,7 @@ function registerStageCommands(program3) {
29441
29466
  // src/merge-cleanup.ts
29442
29467
  var import_node_fs37 = require("node:fs");
29443
29468
  var import_node_path35 = require("node:path");
29444
- var import_node_os15 = require("node:os");
29469
+ var import_node_os16 = require("node:os");
29445
29470
 
29446
29471
  // src/config-load.ts
29447
29472
  var discoveredConfig = null;
@@ -29705,7 +29730,7 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
29705
29730
  const commits = JSON.parse(raw).commits ?? [];
29706
29731
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
29707
29732
  if (!body) return void 0;
29708
- const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os15.tmpdir)(), "mmi-squash-body-"));
29733
+ const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os16.tmpdir)(), "mmi-squash-body-"));
29709
29734
  const path2 = (0, import_node_path35.join)(dir, "body.txt");
29710
29735
  (0, import_node_fs37.writeFileSync)(path2, `${body}
29711
29736
  `, "utf8");
@@ -30474,6 +30499,175 @@ async function checkDocsIndexAtHead(opts, deps) {
30474
30499
  // src/issue-commands.ts
30475
30500
  var import_node_fs38 = require("node:fs");
30476
30501
  var import_node_crypto13 = require("node:crypto");
30502
+
30503
+ // src/learning-closure-rate.ts
30504
+ var CLOSURE_RATE_TIMEOUT_MS = 2e4;
30505
+ var LOOP_ITEM_CAP = 100;
30506
+ var EVIDENCE_CONCURRENCY = 4;
30507
+ var RED_TEAM_FINDING_LABEL2 = "red-team";
30508
+ var RULING_LOOP_STATE_LABELS = ["loop-state:closed-deferred", "loop-state:closed-invalid"];
30509
+ var CLOSED_MERGED_LOOP_STATE_LABEL = "loop-state:closed-merged";
30510
+ var LOOP_KINDS2 = [
30511
+ { kind: "friction", label: REPORT_LABEL },
30512
+ { kind: "lesson", label: SKILL_LESSON_LABEL },
30513
+ { kind: "red-team", label: RED_TEAM_FINDING_LABEL2 }
30514
+ ];
30515
+ function prIsMerged(pr2) {
30516
+ return String(pr2.state ?? "").toUpperCase() === "MERGED";
30517
+ }
30518
+ function mergedPrsOf(linkedPrs) {
30519
+ return linkedPrs.filter(prIsMerged);
30520
+ }
30521
+ function hasRecordedRuling(labels) {
30522
+ return labels.some((label) => RULING_LOOP_STATE_LABELS.includes(label));
30523
+ }
30524
+ function classifyLoopClosure(item, evidence) {
30525
+ if (String(item.state ?? "").toUpperCase() !== "CLOSED") return "open";
30526
+ if (mergedPrsOf(evidence.linkedPrs).length > 0) return "closed";
30527
+ if (item.labels.includes(CLOSED_MERGED_LOOP_STATE_LABEL)) return "closed";
30528
+ if (hasRecordedRuling(item.labels)) return "closed";
30529
+ return "closed-no-evidence";
30530
+ }
30531
+ var round3 = (x) => Math.round(x * 1e3) / 1e3;
30532
+ function computeLoopKindRate(spec, verdicts) {
30533
+ const numerator = verdicts.filter((v) => v === "closed").length;
30534
+ const unknownEvidence = verdicts.filter((v) => v === "closed-no-evidence").length;
30535
+ const denominator = verdicts.length;
30536
+ return {
30537
+ kind: spec.kind,
30538
+ label: spec.label,
30539
+ numerator,
30540
+ denominator,
30541
+ unknownEvidence,
30542
+ rate: denominator > 0 ? round3(numerator / denominator) : null
30543
+ };
30544
+ }
30545
+ function fmtRate(rate) {
30546
+ return rate === null ? "\u2014" : rate.toFixed(3);
30547
+ }
30548
+ function formatClosureSummary(report) {
30549
+ const kindWidth = Math.max(...report.kinds.map((k) => k.kind.length));
30550
+ const labelWidth = Math.max(...report.kinds.map((k) => k.label.length));
30551
+ const lines = [`learning closure rate \u2014 ${report.repo} (computed at read; #4440)`];
30552
+ for (const k of report.kinds) {
30553
+ const head = `${k.kind.padEnd(kindWidth)} (label ${k.label.padEnd(labelWidth)})`;
30554
+ const body = k.denominator === 0 ? "no loop items" : `${k.numerator}/${k.denominator} closed \xB7 ${k.unknownEvidence} unknown evidence \xB7 rate ${fmtRate(k.rate)}`;
30555
+ lines.push(` ${head}: ${body}`);
30556
+ }
30557
+ const t = report.totals;
30558
+ lines.push(
30559
+ ` ${"total".padEnd(kindWidth)}: ${t.numerator}/${t.denominator} closed \xB7 ${t.unknownEvidence} unknown evidence \xB7 rate ${fmtRate(t.rate)}`,
30560
+ "A closed loop without a merged PR or a recorded ruling reads as open."
30561
+ );
30562
+ if (report.evidencePartial) {
30563
+ lines.push("note: at least one closed item had an unreadable linked-PR read \u2014 counted open (unknown evidence).");
30564
+ }
30565
+ return lines.join("\n");
30566
+ }
30567
+ function buildClosureReport(repo, perKind, evidencePartial) {
30568
+ const kinds = perKind.map(({ spec, verdicts }) => computeLoopKindRate(spec, verdicts));
30569
+ const numerator = kinds.reduce((acc, k) => acc + k.numerator, 0);
30570
+ const denominator = kinds.reduce((acc, k) => acc + k.denominator, 0);
30571
+ const unknownEvidence = kinds.reduce((acc, k) => acc + k.unknownEvidence, 0);
30572
+ const report = {
30573
+ repo,
30574
+ kinds,
30575
+ totals: {
30576
+ numerator,
30577
+ denominator,
30578
+ unknownEvidence,
30579
+ rate: denominator > 0 ? round3(numerator / denominator) : null
30580
+ },
30581
+ evidencePartial,
30582
+ summary: ""
30583
+ };
30584
+ report.summary = formatClosureSummary(report);
30585
+ return report;
30586
+ }
30587
+ function splitRepo2(repo) {
30588
+ const parts = repo.split("/");
30589
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
30590
+ throw new QueryReadError("BAD_INPUT", `invalid repo "${repo}" (expected owner/repo)`);
30591
+ }
30592
+ return { owner: parts[0], name: parts[1] };
30593
+ }
30594
+ async function mapPooled(items, limit, worker) {
30595
+ const out = new Array(items.length);
30596
+ let next = 0;
30597
+ const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {
30598
+ while (next < items.length) {
30599
+ const index = next;
30600
+ next += 1;
30601
+ out[index] = await worker(items[index]);
30602
+ }
30603
+ });
30604
+ await Promise.all(runners);
30605
+ return out;
30606
+ }
30607
+ async function readEvidence(deps, owner, name, item) {
30608
+ try {
30609
+ const resp = await deps.ghJson(prForIssueGraphqlArgs(owner, name, item.number), CLOSURE_RATE_TIMEOUT_MS);
30610
+ return { linkedPrs: extractPrForIssueResponse(resp), readFailed: false };
30611
+ } catch (e) {
30612
+ if (e instanceof QueryReadError && e.code === "NOT_FOUND") {
30613
+ return { linkedPrs: [], readFailed: false };
30614
+ }
30615
+ return { linkedPrs: [], readFailed: true };
30616
+ }
30617
+ }
30618
+ async function runLearningClosureRate(deps, opts = {}) {
30619
+ const repo = opts.repo ?? HUB_REPO;
30620
+ const { owner, name } = splitRepo2(repo);
30621
+ let evidencePartial = false;
30622
+ const perKind = [];
30623
+ for (const spec of LOOP_KINDS2) {
30624
+ const rows = await deps.ghJson(
30625
+ buildIssueListArgs({ label: spec.label, state: "all", limit: LOOP_ITEM_CAP }, repo),
30626
+ CLOSURE_RATE_TIMEOUT_MS
30627
+ );
30628
+ const items = shapeIssueList(rows);
30629
+ const closed = items.filter((item) => String(item.state ?? "").toUpperCase() === "CLOSED");
30630
+ const evidence = await mapPooled(closed, EVIDENCE_CONCURRENCY, (item) => readEvidence(deps, owner, name, item));
30631
+ if (evidence.some((ev) => ev.readFailed)) evidencePartial = true;
30632
+ const evidenceByNumber = new Map(closed.map((item, i) => [item.number, evidence[i]]));
30633
+ const verdicts = items.map(
30634
+ (item) => classifyLoopClosure(item, evidenceByNumber.get(item.number) ?? { linkedPrs: [], readFailed: false })
30635
+ );
30636
+ perKind.push({ spec, verdicts });
30637
+ }
30638
+ return buildClosureReport(repo, perKind, evidencePartial);
30639
+ }
30640
+ function closureRateFail(e) {
30641
+ if (e instanceof QueryReadError) {
30642
+ const code = e.code === "NOT_FOUND" ? ERROR_CODES.ERR_NOT_FOUND : e.code === "NO_AUTH" ? ERROR_CODES.ERR_NO_AUTH : ERROR_CODES.ERR_BAD_ENUM;
30643
+ return fail(`closure-rate: ${e.message}`, code === ERROR_CODES.ERR_BAD_ENUM ? void 0 : { code });
30644
+ }
30645
+ const err = e;
30646
+ return fail(`closure-rate: ${(err.stderr || err.message || String(e)).trim()}`);
30647
+ }
30648
+ function registerLearningClosureRateCommand(program3) {
30649
+ const deps = queryDeps();
30650
+ withExamples(
30651
+ program3.command("closure-rate").description(
30652
+ "learning closure rate per loop kind (friction, lesson, red-team) computed at read from board + linked-PR/ruling evidence \u2014 a closed loop without a merged PR or a recorded ruling reads as open (#4440)"
30653
+ ).option("--repo <owner/repo>", `repo holding the loop items (defaults to ${HUB_REPO}, the central Hub board)`).option("--json", "print the structured LearningClosureReport JSON (default is the human summary)").action(async (o) => {
30654
+ try {
30655
+ const report = await runLearningClosureRate(deps, { repo: o.repo });
30656
+ if (o.json) console.log(JSON.stringify(report, null, 2));
30657
+ else console.log(report.summary);
30658
+ } catch (e) {
30659
+ closureRateFail(e);
30660
+ }
30661
+ }),
30662
+ [
30663
+ "mmi-cli learning closure-rate",
30664
+ "mmi-cli learning closure-rate --repo mutmutco/MMI-Hub --json"
30665
+ ],
30666
+ "loop items are read newest-first with a bounded cap of 100 per kind \u2014 a rate over a longer history needs the cap raised, never an unbounded scan"
30667
+ );
30668
+ }
30669
+
30670
+ // src/issue-commands.ts
30477
30671
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
30478
30672
  var ReparentConflictError = class extends Error {
30479
30673
  constructor(message, payload) {
@@ -30597,6 +30791,13 @@ async function closeIssue(client, options, deps = {}) {
30597
30791
  if (options.evidence !== void 0) {
30598
30792
  await verifyCloseEvidence(client, options.evidence, repo, parsed.number, reason === "duplicate-of" ? options.duplicateOf : void 0);
30599
30793
  }
30794
+ if (reason === "completed" && options.evidence !== void 0) {
30795
+ const ensureLabels = deps.ensureLabels ?? ensureLabelsExist;
30796
+ await ensureLabels([CLOSED_MERGED_LOOP_STATE_LABEL], repo);
30797
+ await client.rest("POST", `repos/${repo}/issues/${parsed.number}/labels`, {
30798
+ body: { labels: [CLOSED_MERGED_LOOP_STATE_LABEL] }
30799
+ });
30800
+ }
30600
30801
  await client.rest("PATCH", `repos/${repo}/issues/${parsed.number}`, {
30601
30802
  body: { state: "closed", state_reason: stateReason }
30602
30803
  });
@@ -31340,7 +31541,7 @@ function registerDeployCommands(program3) {
31340
31541
 
31341
31542
  // src/discovery-commands.ts
31342
31543
  var import_node_fs40 = require("node:fs");
31343
- var import_node_os16 = require("node:os");
31544
+ var import_node_os17 = require("node:os");
31344
31545
  var import_node_path37 = require("node:path");
31345
31546
  var GC_GH_TIMEOUT_MS2 = 2e4;
31346
31547
  async function collectStatus() {
@@ -31541,7 +31742,7 @@ async function collectOnboardStatus(opts = {}) {
31541
31742
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
31542
31743
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
31543
31744
  }
31544
- const home = (0, import_node_os16.homedir)();
31745
+ const home = (0, import_node_os17.homedir)();
31545
31746
  const plugin = onboardPluginGate({
31546
31747
  readKnown: () => readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs40.readFileSync),
31547
31748
  readSettings: () => readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs40.readFileSync)
@@ -32306,173 +32507,6 @@ function registerSessionReport(program3) {
32306
32507
  });
32307
32508
  }
32308
32509
 
32309
- // src/learning-closure-rate.ts
32310
- var CLOSURE_RATE_TIMEOUT_MS = 2e4;
32311
- var LOOP_ITEM_CAP = 100;
32312
- var EVIDENCE_CONCURRENCY = 4;
32313
- var RED_TEAM_FINDING_LABEL2 = "red-team";
32314
- var RULING_LOOP_STATE_LABELS = ["loop-state:closed-deferred", "loop-state:closed-invalid"];
32315
- var CLOSED_MERGED_LOOP_STATE_LABEL = "loop-state:closed-merged";
32316
- var LOOP_KINDS2 = [
32317
- { kind: "friction", label: REPORT_LABEL },
32318
- { kind: "lesson", label: SKILL_LESSON_LABEL },
32319
- { kind: "red-team", label: RED_TEAM_FINDING_LABEL2 }
32320
- ];
32321
- function prIsMerged(pr2) {
32322
- return String(pr2.state ?? "").toUpperCase() === "MERGED";
32323
- }
32324
- function mergedPrsOf(linkedPrs) {
32325
- return linkedPrs.filter(prIsMerged);
32326
- }
32327
- function hasRecordedRuling(labels) {
32328
- return labels.some((label) => RULING_LOOP_STATE_LABELS.includes(label));
32329
- }
32330
- function classifyLoopClosure(item, evidence) {
32331
- if (String(item.state ?? "").toUpperCase() !== "CLOSED") return "open";
32332
- if (mergedPrsOf(evidence.linkedPrs).length > 0) return "closed";
32333
- if (item.labels.includes(CLOSED_MERGED_LOOP_STATE_LABEL)) return "closed";
32334
- if (hasRecordedRuling(item.labels)) return "closed";
32335
- return "closed-no-evidence";
32336
- }
32337
- var round3 = (x) => Math.round(x * 1e3) / 1e3;
32338
- function computeLoopKindRate(spec, verdicts) {
32339
- const numerator = verdicts.filter((v) => v === "closed").length;
32340
- const unknownEvidence = verdicts.filter((v) => v === "closed-no-evidence").length;
32341
- const denominator = verdicts.length;
32342
- return {
32343
- kind: spec.kind,
32344
- label: spec.label,
32345
- numerator,
32346
- denominator,
32347
- unknownEvidence,
32348
- rate: denominator > 0 ? round3(numerator / denominator) : null
32349
- };
32350
- }
32351
- function fmtRate(rate) {
32352
- return rate === null ? "\u2014" : rate.toFixed(3);
32353
- }
32354
- function formatClosureSummary(report) {
32355
- const kindWidth = Math.max(...report.kinds.map((k) => k.kind.length));
32356
- const labelWidth = Math.max(...report.kinds.map((k) => k.label.length));
32357
- const lines = [`learning closure rate \u2014 ${report.repo} (computed at read; #4440)`];
32358
- for (const k of report.kinds) {
32359
- const head = `${k.kind.padEnd(kindWidth)} (label ${k.label.padEnd(labelWidth)})`;
32360
- const body = k.denominator === 0 ? "no loop items" : `${k.numerator}/${k.denominator} closed \xB7 ${k.unknownEvidence} unknown evidence \xB7 rate ${fmtRate(k.rate)}`;
32361
- lines.push(` ${head}: ${body}`);
32362
- }
32363
- const t = report.totals;
32364
- lines.push(
32365
- ` ${"total".padEnd(kindWidth)}: ${t.numerator}/${t.denominator} closed \xB7 ${t.unknownEvidence} unknown evidence \xB7 rate ${fmtRate(t.rate)}`,
32366
- "A closed loop without a merged PR or a recorded ruling reads as open."
32367
- );
32368
- if (report.evidencePartial) {
32369
- lines.push("note: at least one closed item had an unreadable linked-PR read \u2014 counted open (unknown evidence).");
32370
- }
32371
- return lines.join("\n");
32372
- }
32373
- function buildClosureReport(repo, perKind, evidencePartial) {
32374
- const kinds = perKind.map(({ spec, verdicts }) => computeLoopKindRate(spec, verdicts));
32375
- const numerator = kinds.reduce((acc, k) => acc + k.numerator, 0);
32376
- const denominator = kinds.reduce((acc, k) => acc + k.denominator, 0);
32377
- const unknownEvidence = kinds.reduce((acc, k) => acc + k.unknownEvidence, 0);
32378
- const report = {
32379
- repo,
32380
- kinds,
32381
- totals: {
32382
- numerator,
32383
- denominator,
32384
- unknownEvidence,
32385
- rate: denominator > 0 ? round3(numerator / denominator) : null
32386
- },
32387
- evidencePartial,
32388
- summary: ""
32389
- };
32390
- report.summary = formatClosureSummary(report);
32391
- return report;
32392
- }
32393
- function splitRepo2(repo) {
32394
- const parts = repo.split("/");
32395
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
32396
- throw new QueryReadError("BAD_INPUT", `invalid repo "${repo}" (expected owner/repo)`);
32397
- }
32398
- return { owner: parts[0], name: parts[1] };
32399
- }
32400
- async function mapPooled(items, limit, worker) {
32401
- const out = new Array(items.length);
32402
- let next = 0;
32403
- const runners = Array.from({ length: Math.min(limit, items.length) }, async () => {
32404
- while (next < items.length) {
32405
- const index = next;
32406
- next += 1;
32407
- out[index] = await worker(items[index]);
32408
- }
32409
- });
32410
- await Promise.all(runners);
32411
- return out;
32412
- }
32413
- async function readEvidence(deps, owner, name, item) {
32414
- try {
32415
- const resp = await deps.ghJson(prForIssueGraphqlArgs(owner, name, item.number), CLOSURE_RATE_TIMEOUT_MS);
32416
- return { linkedPrs: extractPrForIssueResponse(resp), readFailed: false };
32417
- } catch (e) {
32418
- if (e instanceof QueryReadError && e.code === "NOT_FOUND") {
32419
- return { linkedPrs: [], readFailed: false };
32420
- }
32421
- return { linkedPrs: [], readFailed: true };
32422
- }
32423
- }
32424
- async function runLearningClosureRate(deps, opts = {}) {
32425
- const repo = opts.repo ?? HUB_REPO;
32426
- const { owner, name } = splitRepo2(repo);
32427
- let evidencePartial = false;
32428
- const perKind = [];
32429
- for (const spec of LOOP_KINDS2) {
32430
- const rows = await deps.ghJson(
32431
- buildIssueListArgs({ label: spec.label, state: "all", limit: LOOP_ITEM_CAP }, repo),
32432
- CLOSURE_RATE_TIMEOUT_MS
32433
- );
32434
- const items = shapeIssueList(rows);
32435
- const closed = items.filter((item) => String(item.state ?? "").toUpperCase() === "CLOSED");
32436
- const evidence = await mapPooled(closed, EVIDENCE_CONCURRENCY, (item) => readEvidence(deps, owner, name, item));
32437
- if (evidence.some((ev) => ev.readFailed)) evidencePartial = true;
32438
- const evidenceByNumber = new Map(closed.map((item, i) => [item.number, evidence[i]]));
32439
- const verdicts = items.map(
32440
- (item) => classifyLoopClosure(item, evidenceByNumber.get(item.number) ?? { linkedPrs: [], readFailed: false })
32441
- );
32442
- perKind.push({ spec, verdicts });
32443
- }
32444
- return buildClosureReport(repo, perKind, evidencePartial);
32445
- }
32446
- function closureRateFail(e) {
32447
- if (e instanceof QueryReadError) {
32448
- const code = e.code === "NOT_FOUND" ? ERROR_CODES.ERR_NOT_FOUND : e.code === "NO_AUTH" ? ERROR_CODES.ERR_NO_AUTH : ERROR_CODES.ERR_BAD_ENUM;
32449
- return fail(`closure-rate: ${e.message}`, code === ERROR_CODES.ERR_BAD_ENUM ? void 0 : { code });
32450
- }
32451
- const err = e;
32452
- return fail(`closure-rate: ${(err.stderr || err.message || String(e)).trim()}`);
32453
- }
32454
- function registerLearningClosureRateCommand(program3) {
32455
- const deps = queryDeps();
32456
- withExamples(
32457
- program3.command("closure-rate").description(
32458
- "learning closure rate per loop kind (friction, lesson, red-team) computed at read from board + linked-PR/ruling evidence \u2014 a closed loop without a merged PR or a recorded ruling reads as open (#4440)"
32459
- ).option("--repo <owner/repo>", `repo holding the loop items (defaults to ${HUB_REPO}, the central Hub board)`).option("--json", "print the structured LearningClosureReport JSON (default is the human summary)").action(async (o) => {
32460
- try {
32461
- const report = await runLearningClosureRate(deps, { repo: o.repo });
32462
- if (o.json) console.log(JSON.stringify(report, null, 2));
32463
- else console.log(report.summary);
32464
- } catch (e) {
32465
- closureRateFail(e);
32466
- }
32467
- }),
32468
- [
32469
- "mmi-cli learning closure-rate",
32470
- "mmi-cli learning closure-rate --repo mutmutco/MMI-Hub --json"
32471
- ],
32472
- "loop items are read newest-first with a bounded cap of 100 per kind \u2014 a rate over a longer history needs the cap raised, never an unbounded scan"
32473
- );
32474
- }
32475
-
32476
32510
  // ../infra/src/fleet-health.ts
32477
32511
  function uniqueRepos(repos) {
32478
32512
  const unique = /* @__PURE__ */ new Map();
@@ -32703,10 +32737,6 @@ var surfaces_default = {
32703
32737
  releaseMetadata: {
32704
32738
  ownerPath: "surfaces.json",
32705
32739
  bomPath: "distribution-bom.json"
32706
- },
32707
- soul: {
32708
- ownerPath: "prompts/soul.md",
32709
- sourceSurfaceId: "mmi-soul"
32710
32740
  }
32711
32741
  },
32712
32742
  agentSurfaces: [
@@ -32718,7 +32748,6 @@ var surfaces_default = {
32718
32748
  "mmi-claude-plugin",
32719
32749
  "mmi-claude-marketplace",
32720
32750
  "mmi-skills",
32721
- "mmi-soul",
32722
32751
  "mmi-hook-policy",
32723
32752
  "mmi-hooks",
32724
32753
  "mmi-cli"
@@ -32733,14 +32762,6 @@ var surfaces_default = {
32733
32762
  sourcePath: "skills",
32734
32763
  targetPath: "packages/claude-plugin/skills"
32735
32764
  },
32736
- {
32737
- mode: "files",
32738
- sourcePath: "prompts",
32739
- targetPath: "packages/claude-plugin/prompts",
32740
- include: [
32741
- "soul.md"
32742
- ]
32743
- },
32744
32765
  {
32745
32766
  mode: "files",
32746
32767
  sourcePath: "hooks",
@@ -32854,7 +32875,6 @@ var surfaces_default = {
32854
32875
  artifactIds: [
32855
32876
  "mmi-codex-plugin",
32856
32877
  "mmi-skills",
32857
- "mmi-soul",
32858
32878
  "mmi-hook-policy",
32859
32879
  "mmi-cli"
32860
32880
  ],
@@ -32868,14 +32888,6 @@ var surfaces_default = {
32868
32888
  sourcePath: "skills",
32869
32889
  targetPath: "packages/codex-plugin/skills"
32870
32890
  },
32871
- {
32872
- mode: "files",
32873
- sourcePath: "prompts",
32874
- targetPath: "packages/codex-plugin/prompts",
32875
- include: [
32876
- "soul.md"
32877
- ]
32878
- },
32879
32891
  {
32880
32892
  mode: "files",
32881
32893
  sourcePath: "hooks",
@@ -32989,7 +33001,6 @@ var surfaces_default = {
32989
33001
  artifactIds: [
32990
33002
  "mmi-kimi-plugin",
32991
33003
  "mmi-skills",
32992
- "mmi-soul",
32993
33004
  "mmi-hook-policy",
32994
33005
  "mmi-cli"
32995
33006
  ],
@@ -33003,14 +33014,6 @@ var surfaces_default = {
33003
33014
  sourcePath: "skills",
33004
33015
  targetPath: "packages/kimi-plugin/skills"
33005
33016
  },
33006
- {
33007
- mode: "files",
33008
- sourcePath: "prompts",
33009
- targetPath: "packages/kimi-plugin/prompts",
33010
- include: [
33011
- "soul.md"
33012
- ]
33013
- },
33014
33017
  {
33015
33018
  mode: "files",
33016
33019
  sourcePath: "scripts",
@@ -33116,7 +33119,6 @@ var surfaces_default = {
33116
33119
  artifactIds: [
33117
33120
  "mmi-cursor-plugin",
33118
33121
  "mmi-skills",
33119
- "mmi-soul",
33120
33122
  "mmi-hook-policy",
33121
33123
  "mmi-cli"
33122
33124
  ],
@@ -33130,14 +33132,6 @@ var surfaces_default = {
33130
33132
  sourcePath: "skills",
33131
33133
  targetPath: "packages/cursor-plugin/skills"
33132
33134
  },
33133
- {
33134
- mode: "files",
33135
- sourcePath: "prompts",
33136
- targetPath: "packages/cursor-plugin/prompts",
33137
- include: [
33138
- "soul.md"
33139
- ]
33140
- },
33141
33135
  {
33142
33136
  mode: "files",
33143
33137
  sourcePath: "hooks",
@@ -33250,7 +33244,6 @@ var surfaces_default = {
33250
33244
  displayName: "Kilo Code",
33251
33245
  lifecycle: "active",
33252
33246
  artifactIds: [
33253
- "mmi-soul",
33254
33247
  "mmi-kilo-plugin",
33255
33248
  "mmi-kilo-skills",
33256
33249
  "mmi-hook-policy",
@@ -33269,14 +33262,6 @@ var surfaces_default = {
33269
33262
  "_"
33270
33263
  ]
33271
33264
  },
33272
- {
33273
- mode: "files",
33274
- sourcePath: "prompts",
33275
- targetPath: ".kilo-plugin/prompts",
33276
- include: [
33277
- "soul.md"
33278
- ]
33279
- },
33280
33265
  {
33281
33266
  mode: "files",
33282
33267
  sourcePath: "scripts",
@@ -33362,7 +33347,6 @@ var surfaces_default = {
33362
33347
  artifactIds: [
33363
33348
  "mmi-pi-plugin",
33364
33349
  "mmi-skills",
33365
- "mmi-soul",
33366
33350
  "mmi-cli",
33367
33351
  "mmi-hook-policy"
33368
33352
  ],
@@ -33379,14 +33363,6 @@ var surfaces_default = {
33379
33363
  "_"
33380
33364
  ]
33381
33365
  },
33382
- {
33383
- mode: "files",
33384
- sourcePath: "prompts",
33385
- targetPath: ".pi-plugin/prompts",
33386
- include: [
33387
- "soul.md"
33388
- ]
33389
- },
33390
33366
  {
33391
33367
  mode: "files",
33392
33368
  sourcePath: "pi-plugin/extensions",
@@ -33481,7 +33457,6 @@ var surfaces_default = {
33481
33457
  artifactIds: [
33482
33458
  "mmi-hermes-plugin",
33483
33459
  "mmi-skills",
33484
- "mmi-soul",
33485
33460
  "mmi-cli",
33486
33461
  "mmi-hook-policy"
33487
33462
  ],
@@ -33495,14 +33470,6 @@ var surfaces_default = {
33495
33470
  sourcePath: "skills",
33496
33471
  targetPath: "packages/hermes-plugin/skills"
33497
33472
  },
33498
- {
33499
- mode: "files",
33500
- sourcePath: "prompts",
33501
- targetPath: "packages/hermes-plugin/prompts",
33502
- include: [
33503
- "soul.md"
33504
- ]
33505
- },
33506
33473
  {
33507
33474
  mode: "files",
33508
33475
  sourcePath: "scripts",
@@ -33622,39 +33589,6 @@ var surfaces_default = {
33622
33589
  ],
33623
33590
  publishVisibility: "public"
33624
33591
  },
33625
- {
33626
- id: "mmi-soul",
33627
- classification: "capability",
33628
- kind: "spine",
33629
- ownerPath: "prompts/soul.md",
33630
- deliveryPath: "prompts/soul.md",
33631
- delivery: "plugin-install",
33632
- applicability: "plugin-enabled agent surfaces; opaque JervCode soul mirror, not a host-invocable prompt.",
33633
- versionCoordinated: true,
33634
- versionPaths: [
33635
- {
33636
- path: "cli/package.json",
33637
- pointer: "version"
33638
- }
33639
- ],
33640
- artifactIdentity: {
33641
- kind: "sha256-tree",
33642
- paths: [
33643
- "prompts/soul.md"
33644
- ]
33645
- },
33646
- verify: [
33647
- {
33648
- command: "node",
33649
- args: [
33650
- "scripts/check-soul-payload.mjs",
33651
- "--contract"
33652
- ],
33653
- expected: "soul payload check: ok"
33654
- }
33655
- ],
33656
- publishVisibility: "public"
33657
- },
33658
33592
  {
33659
33593
  id: "mmi-claude-plugin",
33660
33594
  classification: "packaging",
@@ -35472,7 +35406,7 @@ function ghAccountCaveat(announcedLogin, accounts) {
35472
35406
 
35473
35407
  // src/doctor-io.ts
35474
35408
  var import_node_fs41 = require("node:fs");
35475
- var import_node_os17 = require("node:os");
35409
+ var import_node_os18 = require("node:os");
35476
35410
  var import_node_path38 = require("node:path");
35477
35411
  var import_node_child_process18 = require("node:child_process");
35478
35412
  var import_node_util8 = require("node:util");
@@ -35481,7 +35415,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
35481
35415
  function installedClaudePluginVersion() {
35482
35416
  try {
35483
35417
  const file = JSON.parse(
35484
- (0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os17.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
35418
+ (0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
35485
35419
  );
35486
35420
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
35487
35421
  if (versions.length === 0) return void 0;
@@ -35541,7 +35475,7 @@ function installedSurfacePluginVersion(surface) {
35541
35475
  const token = surfaceToken(surface);
35542
35476
  if (token === "kilo") {
35543
35477
  try {
35544
- const stamp = (0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os17.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
35478
+ const stamp = (0, import_node_fs41.readFileSync)((0, import_node_path38.join)((0, import_node_os18.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
35545
35479
  return stamp || void 0;
35546
35480
  } catch {
35547
35481
  return void 0;
@@ -35785,7 +35719,7 @@ function envHealLockPath(home) {
35785
35719
  async function withEnvHealLock(what, run) {
35786
35720
  try {
35787
35721
  return await withFileLock(
35788
- envHealLockPath((0, import_node_os18.homedir)()),
35722
+ envHealLockPath((0, import_node_os19.homedir)()),
35789
35723
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
35790
35724
  run
35791
35725
  );
@@ -35880,7 +35814,7 @@ function mmiDoctorDeps(opts = {}) {
35880
35814
  const configRoot = surfaceConfigRoot(surface);
35881
35815
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
35882
35816
  const plan = buildPluginCachePlan(
35883
- (0, import_node_os18.homedir)(),
35817
+ (0, import_node_os19.homedir)(),
35884
35818
  running,
35885
35819
  pluginCacheFsDeps(configRoot, () => 0),
35886
35820
  { configRoot, includeStaging: surface !== "codex" }
@@ -35908,7 +35842,7 @@ function mmiDoctorDeps(opts = {}) {
35908
35842
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
35909
35843
  const installed = installedActivePluginVersion(surface);
35910
35844
  const plan = buildPluginCachePlan(
35911
- (0, import_node_os18.homedir)(),
35845
+ (0, import_node_os19.homedir)(),
35912
35846
  running,
35913
35847
  pluginCacheFsDeps(configRoot, () => 0),
35914
35848
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
@@ -36249,7 +36183,8 @@ function envelopeAwareWriteErr(str) {
36249
36183
  const invoked = resolveCommandFromArgv(program2, argv);
36250
36184
  const positional = TARGET_SELECTOR_FLAGS.has(flag) ? positionalTargetForm(invoked, { flag, argv }) : void 0;
36251
36185
  if (argvWantsJson2()) {
36252
- const suggestion = positional ? void 0 : didYouMean(flag, commandOwnLongFlags(invoked));
36186
+ const ownFlags = commandOwnLongFlags(invoked);
36187
+ const suggestion = positional ? void 0 : didYouMean(flag, ownFlags) ?? synonymFlagFor(flag, ownFlags);
36253
36188
  const corrected = suggestion ? `mmi-cli ${canonicalArgvFor(argv.map((a) => a === flag ? suggestion : a)).join(" ")}` : void 0;
36254
36189
  const message = positional ? unknownTargetFlagMessage(flag, positional) : `unknown option '${flag}'`;
36255
36190
  process.stderr.write(
@@ -37961,7 +37896,7 @@ pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs
37961
37896
  if (o.json) return printLine(JSON.stringify(result));
37962
37897
  printLine(`merge CI policy: ${result.policy} (${result.reason})`);
37963
37898
  });
37964
- pr.command("checks-wait <number>").description(`bounded wait for PR checks; skips immediately on no-ci repos (#1432), fails immediately on a CONFLICTING PR \u2014 GitHub never queues checks for one (#2970). REST-only polling with a pool floor (#3024). Default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m; --timeout raises it. Exit 1 = a check FAILED or the PR is CONFLICTING, exit ${PR_CHECKS_TIMEOUT_EXIT_CODE} = the wait window expired or the API pool ran dry (re-arm)`).option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--timeout <minutes>", `wait budget in minutes (default ${PR_CHECKS_TIMEOUT_MS / 6e4}) \u2014 raise it for serial self-hosted e2e queues`).action(async (number, o) => {
37899
+ pr.command("checks-wait <number>").description(`bounded wait for ALL checks on the PR head, required or not (#5336); skips immediately on no-ci repos (#1432), fails immediately on a CONFLICTING PR \u2014 GitHub never queues checks for one (#2970). REST-only polling with a pool floor (#3024). Default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m; --timeout raises it. Exit 1 = a check FAILED or the PR is CONFLICTING, exit ${PR_CHECKS_TIMEOUT_EXIT_CODE} = the wait window expired or the API pool ran dry (re-arm)`).option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--timeout <minutes>", `wait budget in minutes (default ${PR_CHECKS_TIMEOUT_MS / 6e4}) \u2014 raise it for serial self-hosted e2e queues`).action(async (number, o) => {
37965
37900
  let timeoutMs;
37966
37901
  if (o.timeout !== void 0) {
37967
37902
  const minutes = Number(o.timeout);
@@ -37976,10 +37911,12 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
37976
37911
  const snapshot = snapshotRead.snapshot;
37977
37912
  const baseBranch = snapshot.baseRef;
37978
37913
  const ciHeadRef = !snapshot.headIsFork && snapshot.headRef ? snapshot.headRef : void 0;
37979
- const requiredContexts = await fetchRequiredCheckContexts(repo, baseBranch).catch(() => null);
37980
37914
  const result = await waitForPrChecks({
37981
37915
  resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef),
37982
- pollChecks: () => pollRestPrChecks(number, repo, void 0, requiredContexts),
37916
+ // #5336: ALL checks on the PR head — no required-status scoping here. A non-required check still
37917
+ // pending must hold the wait open, and a non-required red must fail it; #4396's scoping is the
37918
+ // merge paths' contract (`pr land`, `pr merge --wait`), which mirror the GitHub merge button.
37919
+ pollChecks: () => pollRestPrChecks(number, repo),
37983
37920
  pollMergeable: () => pollRestPrMergeable(number, repo),
37984
37921
  pollRateLimit: () => waitLoopCorePool("pr checks-wait"),
37985
37922
  // #3388: on a confirmed red, read the failing runs' annotations so a wall-clock budget kill stops
@@ -39003,7 +38940,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
39003
38940
  return;
39004
38941
  }
39005
38942
  const plan = buildPluginCachePlan(
39006
- (0, import_node_os18.homedir)(),
38943
+ (0, import_node_os19.homedir)(),
39007
38944
  running,
39008
38945
  pluginCacheFsDeps(configRoot, directoryBytes),
39009
38946
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
@@ -371,6 +371,7 @@ var REPO_INDEX_V4_SCHEMA = 4;
371
371
  var import_node_crypto3 = require("node:crypto");
372
372
  var import_node_child_process3 = require("node:child_process");
373
373
  var import_node_fs3 = require("node:fs");
374
+ var import_node_os2 = require("node:os");
374
375
  var import_node_path4 = require("node:path");
375
376
  var V4_MAX_CHUNKS = 1e4;
376
377
  var V4_MAX_ARTIFACT_BYTES = 64 * 1024 * 1024;
@@ -448,22 +449,37 @@ function runEmbedderOnce(cwd, chunks, modelDirectory, createdAt) {
448
449
  if (!(0, import_node_fs3.existsSync)(file)) return { ok: false, reason: "embeddings-unavailable" };
449
450
  const request = { texts: chunks.map((chunk) => ({ id: chunk.id, text: embeddingInput(cwd, chunk) })), maxBatch: V4_EMBED_BATCH };
450
451
  const env = { ...process.env, ...modelDirectory ? { MMI_REPO_INDEXER_MODEL_DIR: modelDirectory } : {} };
451
- const result = (0, import_node_child_process3.spawnSync)(process.execPath, [file], { input: JSON.stringify(request), encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
452
+ const requestDir = (0, import_node_fs3.mkdtempSync)((0, import_node_path4.join)((0, import_node_os2.tmpdir)(), "mmi-repo-index-req-"));
453
+ const requestFile = (0, import_node_path4.join)(requestDir, "request.json");
454
+ (0, import_node_fs3.writeFileSync)(requestFile, JSON.stringify(request));
455
+ let result;
456
+ try {
457
+ result = (0, import_node_child_process3.spawnSync)(process.execPath, [file, requestFile], { encoding: "utf8", windowsHide: true, timeout: V4_EMBED_TIMEOUT_MS, maxBuffer: V4_MAX_ARTIFACT_BYTES, env });
458
+ } finally {
459
+ (0, import_node_fs3.rmSync)(requestDir, { recursive: true, force: true });
460
+ }
452
461
  if (result.error || result.status !== 0) {
453
462
  const cleanExit = result.error === void 0 && result.signal === void 0 && typeof result.status === "number" && result.status !== 0;
463
+ const stdout = String(result.stdout).trim();
464
+ const stderrTail = String(result.stderr).trim().slice(-400) || void 0;
454
465
  let code;
455
- let stderrTail;
456
466
  if (cleanExit) {
457
467
  try {
458
- code = JSON.parse(String(result.stdout))?.code;
468
+ code = JSON.parse(stdout)?.code;
459
469
  } catch {
460
470
  }
461
- stderrTail = String(result.stderr).trim().slice(-400) || void 0;
462
471
  }
463
- return { ok: false, code, stderrTail, cleanExit, detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}` };
472
+ return {
473
+ ok: false,
474
+ code,
475
+ stdoutTail: code ? void 0 : stdout.slice(-400) || void 0,
476
+ stderrTail,
477
+ cleanExit,
478
+ detail: result.error?.message ?? result.signal ?? `exit status ${result.status ?? "unknown"}`
479
+ };
464
480
  }
465
481
  try {
466
- const response = JSON.parse(result.stdout);
482
+ const response = JSON.parse(String(result.stdout));
467
483
  if (!response.ok || !response.provenance || !Array.isArray(response.embeddings)) return { ok: false, reason: "embeddings-unavailable" };
468
484
  const provenance = {
469
485
  provider: response.provenance.provider,
@@ -490,8 +506,9 @@ function runEmbedder(cwd, chunks, modelDirectory, createdAt) {
490
506
  }
491
507
  const runner = last;
492
508
  const code = runner.code ? ` code=${runner.code}` : "";
509
+ const stdout = runner.stdoutTail ? ` stdout=${runner.stdoutTail}` : "";
493
510
  const stderr = runner.stderrTail ? ` stderr=${runner.stderrTail}` : "";
494
- throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stderr}`);
511
+ throw new Error(`repo-index v4 embedding runner failed: ${runner.detail ?? "unknown"}${code}${stdout}${stderr}`);
495
512
  }
496
513
  async function buildRepoIndexV4(cwd, repo, opts = {}) {
497
514
  const { commit, defaultBranch, createdAt } = gitInfo(cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.0.12",
3
+ "version": "4.0.14",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",