@mutmutco/cli 3.80.0 → 3.82.0

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.
Files changed (2) hide show
  1. package/dist/main.cjs +409 -534
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3416,8 +3416,8 @@ var program = new Command();
3416
3416
 
3417
3417
  // src/index.ts
3418
3418
  var import_promises10 = require("node:fs/promises");
3419
- var import_node_fs34 = require("node:fs");
3420
- var import_node_child_process15 = require("node:child_process");
3419
+ var import_node_fs35 = require("node:fs");
3420
+ var import_node_child_process16 = require("node:child_process");
3421
3421
 
3422
3422
  // src/cli-shared.ts
3423
3423
  var import_node_child_process3 = require("node:child_process");
@@ -4483,6 +4483,7 @@ function trackedPathStatus(c, realPath, repoAnchor) {
4483
4483
  const gitPath = c.kind === "dir" ? `${rel.replace(/\/+$/, "")}/` : rel;
4484
4484
  try {
4485
4485
  (0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "rev-parse", "--is-inside-work-tree"], {
4486
+ windowsHide: true,
4486
4487
  stdio: ["ignore", "ignore", "ignore"],
4487
4488
  timeout: 1e4
4488
4489
  });
@@ -4491,6 +4492,7 @@ function trackedPathStatus(c, realPath, repoAnchor) {
4491
4492
  }
4492
4493
  try {
4493
4494
  const out = (0, import_node_child_process4.execFileSync)("git", ["-C", repoAnchor, "ls-files", "-z", "--", gitPath], {
4495
+ windowsHide: true,
4494
4496
  encoding: "utf8",
4495
4497
  stdio: ["ignore", "pipe", "ignore"],
4496
4498
  timeout: 1e4
@@ -5293,9 +5295,6 @@ function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
5293
5295
  const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName.toLowerCase());
5294
5296
  return repoDir ? (0, import_node_path7.join)(root, repoDir) : root;
5295
5297
  }
5296
- function strayWorktreeRootPaths(container, names, authoritativeRoot) {
5297
- return names.filter((name) => /worktrees/i.test(name)).map((name) => (0, import_node_path7.join)(container, name)).filter((path2) => !samePath(path2, authoritativeRoot));
5298
- }
5299
5298
  function classifySiblingWorktreeDir(entry) {
5300
5299
  if (!entry.ownedByCurrentRepo) {
5301
5300
  return { skip: { path: entry.path, reason: "unknown-git-state", detail: entry.detail ?? "not proven current-repo owned" } };
@@ -6259,6 +6258,8 @@ function readSettingsAutoUpdate(raw, name) {
6259
6258
  var AUTO_UPDATE_TOGGLE_STEPS = "`/plugin` \u2192 Marketplaces tab \u2192 select the marketplace \u2192 Enable auto-update";
6260
6259
  var CATALOG_CONTENT_REF = "main";
6261
6260
  var CATALOG_REF_PIN_STEPS = `add \`"ref": "${CATALOG_CONTENT_REF}"\` to this marketplace's \`source\` in ~/.claude/plugins/known_marketplaces.json, then restart Claude`;
6261
+ var PIN_HEAL_COMMAND = "run `mmi-cli doctor`";
6262
+ var ORG_MARKETPLACE_PINS = { autoUpdate: true, ref: CATALOG_CONTENT_REF };
6262
6263
  function resolveCatalogRef(probe) {
6263
6264
  const { name, registered, ref, autoUpdate } = probe;
6264
6265
  if (!registered) return void 0;
@@ -6281,7 +6282,7 @@ function resolveCatalogRef(probe) {
6281
6282
  reason: `no ref pinned \u2014 the catalog is served from the repo's default branch while plugin content is pinned to ${CATALOG_CONTENT_REF}`
6282
6283
  };
6283
6284
  }
6284
- function checkMarketplaceAutoUpdate(probe) {
6285
+ function checkMarketplaceAutoUpdate(probe, healable = false) {
6285
6286
  if (!probe) return null;
6286
6287
  const evidence = [`${probe.name}: auto-update ${probe.effective ? "on" : "off"} \u2014 ${probe.reason}`];
6287
6288
  if (probe.effective) {
@@ -6292,11 +6293,11 @@ function checkMarketplaceAutoUpdate(probe) {
6292
6293
  ok: true,
6293
6294
  warn: true,
6294
6295
  label: `marketplace auto-update (${probe.name})`,
6295
- detail: `off \u2014 this machine will not pick up a new plugin release on its own; turn it on with ${AUTO_UPDATE_TOGGLE_STEPS}`,
6296
+ detail: `off \u2014 this machine will not pick up a new plugin release on its own; ${healable ? `${PIN_HEAL_COMMAND} to turn it on` : `turn it on with ${AUTO_UPDATE_TOGGLE_STEPS}`}`,
6296
6297
  verbose: evidence
6297
6298
  };
6298
6299
  }
6299
- function checkMarketplaceCatalogRef(probe) {
6300
+ function checkMarketplaceCatalogRef(probe, healable = false) {
6300
6301
  if (!probe) return null;
6301
6302
  const label = `marketplace catalog ref (${probe.name})`;
6302
6303
  const evidence = [`${probe.name}: ${probe.reason}`, `auto-update: ${probe.autoUpdate ? "on" : "off"}`];
@@ -6313,7 +6314,7 @@ function checkMarketplaceCatalogRef(probe) {
6313
6314
  // ✗ branch carried it in `fix` only because `renderCheckLine` discarded `detail` on failure — one
6314
6315
  // row, two conventions, decided by which field would actually print.
6315
6316
  detail: `${drift} while auto-update is ON`,
6316
- fix: `this machine installs plugin releases advertised by a branch nobody released; ${CATALOG_REF_PIN_STEPS}`,
6317
+ fix: `this machine installs plugin releases advertised by a branch nobody released; ${healable ? `${PIN_HEAL_COMMAND} to pin it` : CATALOG_REF_PIN_STEPS}`,
6317
6318
  verbose: evidence
6318
6319
  };
6319
6320
  }
@@ -6322,11 +6323,11 @@ function checkMarketplaceCatalogRef(probe) {
6322
6323
  ok: true,
6323
6324
  warn: true,
6324
6325
  label,
6325
- detail: `${drift} \u2014 harmless while auto-update is off, and the thing to fix before turning it on; ${CATALOG_REF_PIN_STEPS}`,
6326
+ detail: `${drift} \u2014 harmless while auto-update is off, and the thing to fix before turning it on; ${healable ? `${PIN_HEAL_COMMAND} to pin it` : CATALOG_REF_PIN_STEPS}`,
6326
6327
  verbose: evidence
6327
6328
  };
6328
6329
  }
6329
- function marketplaceRows(name, known, settings) {
6330
+ function marketplaceRows(name, known, settings, healable = false) {
6330
6331
  const { registered, declared, ref } = readKnownMarketplace(known, name);
6331
6332
  const autoUpdate = resolveAutoUpdate({
6332
6333
  name,
@@ -6334,8 +6335,9 @@ function marketplaceRows(name, known, settings) {
6334
6335
  declared,
6335
6336
  settingsDeclared: readSettingsAutoUpdate(settings, name)
6336
6337
  });
6337
- const rows = [checkMarketplaceAutoUpdate(autoUpdate), checkMarketplaceCatalogRef(
6338
- resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective })
6338
+ const rows = [checkMarketplaceAutoUpdate(autoUpdate, healable), checkMarketplaceCatalogRef(
6339
+ resolveCatalogRef({ name, registered, ref, autoUpdate: autoUpdate.effective }),
6340
+ healable
6339
6341
  )];
6340
6342
  return rows.filter((r) => r !== null);
6341
6343
  }
@@ -6347,32 +6349,6 @@ var DEFAULT_SURFACE = "claude";
6347
6349
  function activityLogPath(cwd) {
6348
6350
  return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
6349
6351
  }
6350
- var REDACTOR_WINDOW_MS = 48 * 60 * 60 * 1e3;
6351
- var REDACTOR_SCAN_BYTES = 512 * 1024;
6352
- function redactorLivenessProbe(cwd, now = /* @__PURE__ */ new Date()) {
6353
- try {
6354
- const raw = (0, import_node_fs10.readFileSync)(activityLogPath(cwd), "utf8");
6355
- const tail = raw.length > REDACTOR_SCAN_BYTES ? raw.slice(raw.length - REDACTOR_SCAN_BYTES) : raw;
6356
- const floor = now.getTime() - REDACTOR_WINDOW_MS;
6357
- let failed = 0;
6358
- let lastTs;
6359
- for (const line of tail.split("\n")) {
6360
- if (!line.includes('"secret-redact"') || !line.includes('"failed"')) continue;
6361
- try {
6362
- const row = JSON.parse(line);
6363
- if (row.script !== "secret-redact" || row.outcome !== "failed") continue;
6364
- const ts = row.ts ? Date.parse(row.ts) : Number.NaN;
6365
- if (Number.isNaN(ts) || ts < floor) continue;
6366
- failed += 1;
6367
- if (!lastTs || row.ts > lastTs) lastTs = row.ts;
6368
- } catch {
6369
- }
6370
- }
6371
- return { failed, ...lastTs ? { lastTs } : {} };
6372
- } catch {
6373
- return void 0;
6374
- }
6375
- }
6376
6352
  function appendHookActivity(cwd, entry) {
6377
6353
  try {
6378
6354
  const path2 = activityLogPath(cwd);
@@ -6619,7 +6595,7 @@ function commandLadderHint() {
6619
6595
  }
6620
6596
 
6621
6597
  // src/index.ts
6622
- var import_node_path33 = require("node:path");
6598
+ var import_node_path34 = require("node:path");
6623
6599
 
6624
6600
  // src/merge-ci-policy.ts
6625
6601
  function resolveMergeCiPolicy(input) {
@@ -13440,6 +13416,7 @@ function spawnExitCode(status, signal) {
13440
13416
  function defaultSpawn(command, args, env) {
13441
13417
  const target = resolveSpawnTarget(command, args);
13442
13418
  const r = (0, import_node_child_process6.spawnSync)(target.cmd, target.args, {
13419
+ windowsHide: true,
13443
13420
  stdio: "inherit",
13444
13421
  env,
13445
13422
  windowsVerbatimArguments: target.windowsVerbatimArguments
@@ -13937,62 +13914,6 @@ async function branchesMergedIntoBase(remote) {
13937
13914
  }
13938
13915
  return [...out];
13939
13916
  }
13940
- var STRAY_ROOT_WALK_BUDGET_MS = 4e3;
13941
- function measureWorktreeRoot(root, deadline) {
13942
- let dirs = 0;
13943
- let bytes = 0;
13944
- let partial = false;
13945
- const stack = [root];
13946
- let depth0 = true;
13947
- while (stack.length) {
13948
- if (Date.now() > deadline) {
13949
- partial = true;
13950
- break;
13951
- }
13952
- const current = stack.pop();
13953
- let entries;
13954
- try {
13955
- entries = (0, import_node_fs17.readdirSync)(current, { withFileTypes: true });
13956
- } catch {
13957
- continue;
13958
- }
13959
- for (const ent of entries) {
13960
- const child2 = (0, import_node_path15.join)(current, ent.name);
13961
- if (ent.isDirectory()) {
13962
- if (depth0) dirs++;
13963
- let isLink = false;
13964
- try {
13965
- (0, import_node_fs17.readlinkSync)(child2);
13966
- isLink = true;
13967
- } catch {
13968
- }
13969
- if (!isLink) stack.push(child2);
13970
- } else if (ent.isFile()) {
13971
- try {
13972
- bytes += (0, import_node_fs17.statSync)(child2).size;
13973
- } catch {
13974
- }
13975
- }
13976
- }
13977
- depth0 = false;
13978
- }
13979
- return partial ? { dirs, bytes, partial } : { dirs, bytes };
13980
- }
13981
- function worktreeRootsProbe(repoRoot2) {
13982
- const authoritative = siblingMmiWorktreesRoot(repoRoot2);
13983
- const container = (0, import_node_path15.dirname)(authoritative);
13984
- let names;
13985
- try {
13986
- names = (0, import_node_fs17.readdirSync)(container, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => ent.name);
13987
- } catch {
13988
- return { authoritative, stray: [] };
13989
- }
13990
- const deadline = Date.now() + STRAY_ROOT_WALK_BUDGET_MS;
13991
- return {
13992
- authoritative,
13993
- stray: strayWorktreeRootPaths(container, names, authoritative).map((path2) => ({ path: path2, ...measureWorktreeRoot(path2, deadline) }))
13994
- };
13995
- }
13996
13917
 
13997
13918
  // src/repo-resolve.ts
13998
13919
  function slugOf(repoOrSlug) {
@@ -14892,7 +14813,7 @@ function readKnownMarketplacesFile(path2) {
14892
14813
  return void 0;
14893
14814
  }
14894
14815
  }
14895
- function restoreMarketplacePinsOnDisk(path2, pins) {
14816
+ function writeMarketplacePinsOnDisk(path2, pins, succeeded, failedVerb) {
14896
14817
  if (pins.size === 0) return void 0;
14897
14818
  const after = readKnownMarketplacesFile(path2);
14898
14819
  const next = restoreMarketplacePins(after, pins);
@@ -14900,14 +14821,35 @@ function restoreMarketplacePinsOnDisk(path2, pins) {
14900
14821
  try {
14901
14822
  (0, import_node_fs18.writeFileSync)(path2, next, "utf8");
14902
14823
  } catch {
14903
- return `could NOT restore ${[...pins.keys()].join(", ")} \u2014 re-pin by hand`;
14824
+ return `could NOT ${failedVerb} ${[...pins.keys()].join(", ")} \u2014 set it by hand`;
14904
14825
  }
14905
14826
  const verify = readKnownMarketplacesFile(path2);
14906
14827
  const failed = [...pins].filter(([name, want]) => {
14907
14828
  const got = readKnownMarketplace(verify, name);
14908
14829
  return typeof want.autoUpdate === "boolean" && got.declared !== want.autoUpdate || want.ref !== void 0 && got.ref !== want.ref;
14909
14830
  });
14910
- return failed.length ? `restore did NOT take for ${failed.map(([n]) => n).join(", ")} \u2014 re-pin by hand` : `re-pinned ${[...pins.keys()].join(", ")} (auto-update + catalog ref survive the reinstall)`;
14831
+ return failed.length ? `${failedVerb} did NOT take for ${failed.map(([n]) => n).join(", ")} \u2014 set it by hand` : succeeded(pins);
14832
+ }
14833
+ function restoreMarketplacePinsOnDisk(path2, pins) {
14834
+ return writeMarketplacePinsOnDisk(path2, pins, restoredPinsLine, "restore");
14835
+ }
14836
+ function applyOrgMarketplacePins(path2, names) {
14837
+ return writeMarketplacePinsOnDisk(
14838
+ path2,
14839
+ new Map(names.map((name) => [name, ORG_MARKETPLACE_PINS])),
14840
+ (pins) => `pinned ${[...pins.keys()].join(", ")} to ${ORG_MARKETPLACE_PINS.ref} with auto-update on`,
14841
+ "pin"
14842
+ );
14843
+ }
14844
+ function restoredPinsLine(pins) {
14845
+ const named = [...pins].map(([name, want]) => {
14846
+ const fields = [
14847
+ ...typeof want.autoUpdate === "boolean" ? ["auto-update"] : [],
14848
+ ...want.ref ? ["catalog ref"] : []
14849
+ ];
14850
+ return `${name} (${fields.join(" + ")})`;
14851
+ });
14852
+ return `re-pinned ${named.join(", ")} \u2014 restored across the reinstall`;
14911
14853
  }
14912
14854
  async function runGuard(readOrigin) {
14913
14855
  const surface = detectSurface(process.env);
@@ -16562,12 +16504,13 @@ var PRIMARY_GROUPS = [
16562
16504
  ["Plan and work", ["board", "issue", "worktree", "stage"]],
16563
16505
  ["Review and ship", ["pr", "ci", "rcand", "release", "hotfix", "train"]],
16564
16506
  // `tests` sits beside `docs` deliberately: both are deterministic, repo-local gates a workflow
16565
- // step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605).
16566
- ["Setup and support", ["bootstrap", "secrets", "docs", "tests"]],
16507
+ // step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605). `spawn policy`
16508
+ // joins them on the same footing (#3979).
16509
+ ["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn"]],
16567
16510
  ["Coordinate and improve", ["wave", "report", "skill-lesson"]]
16568
16511
  ];
16569
16512
  var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
16570
- var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "wave", "report", "skill-lesson"]);
16513
+ var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "wave", "report", "skill-lesson"]);
16571
16514
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
16572
16515
  var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
16573
16516
  var topLevelPosition = 0;
@@ -16607,6 +16550,7 @@ var COMMAND_OWNERSHIP = {
16607
16550
  secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
16608
16551
  docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
16609
16552
  tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
16553
+ spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
16610
16554
  wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
16611
16555
  report: { module_owner: "cli/src/report.ts", consumer: "campaign-orchestrator" },
16612
16556
  "skill-lesson": { module_owner: "cli/src/skill-lesson.ts", consumer: "campaign-orchestrator" },
@@ -18898,10 +18842,115 @@ function runDocRefs(root, deps = {}) {
18898
18842
  };
18899
18843
  }
18900
18844
 
18901
- // src/test-policy-core.ts
18845
+ // src/spawn-policy-core.ts
18902
18846
  var import_node_child_process11 = require("node:child_process");
18903
18847
  var import_node_fs21 = require("node:fs");
18904
18848
  var import_node_path19 = require("node:path");
18849
+ var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
18850
+ var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
18851
+ var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
18852
+ var EXCLUDED = [
18853
+ /(^|\/)node_modules\//,
18854
+ /(^|\/)(dist|build|coverage)\//,
18855
+ /(^|\/)(test|tests|__tests__|fixtures|__fixtures__)\//,
18856
+ /\.(test|spec)\.[cm]?[jt]s$/,
18857
+ /\.d\.[cm]?ts$/
18858
+ ];
18859
+ function stripComments(src) {
18860
+ return src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/[^\n]*/g, (m, p) => p + " ".repeat(m.length - p.length));
18861
+ }
18862
+ function callArgs(src, open2) {
18863
+ let depth = 0;
18864
+ for (let i = open2; i < src.length; i++) {
18865
+ const c = src[i];
18866
+ if (c === "(") depth++;
18867
+ else if (c === ")") {
18868
+ depth--;
18869
+ if (depth === 0) return src.slice(open2, i + 1);
18870
+ }
18871
+ }
18872
+ return "";
18873
+ }
18874
+ function findViolationsInSource(raw) {
18875
+ if (!/from\s+["']node:child_process["']/.test(raw)) return [];
18876
+ const importBlock = /import\s*(?:type\s*)?\{([^}]*)\}\s*from\s+["']node:child_process["']/.exec(raw);
18877
+ const imported = new Set(
18878
+ (importBlock?.[1] ?? "").split(",").map((s) => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean)
18879
+ );
18880
+ if (imported.size === 0) return [];
18881
+ const rawLines = raw.split("\n");
18882
+ const src = stripComments(raw);
18883
+ const call = new RegExp(CALL_SOURCE, "g");
18884
+ const found = [];
18885
+ let m;
18886
+ while (m = call.exec(src)) {
18887
+ const callee = m[2];
18888
+ if (!imported.has(callee)) continue;
18889
+ const open2 = m.index + m[0].length - 1;
18890
+ const args = callArgs(src, open2);
18891
+ if (!args) continue;
18892
+ if (args.includes("windowsHide")) continue;
18893
+ const line = src.slice(0, m.index + m[1].length).split("\n").length;
18894
+ if (/windows-hide-exempt:/.test(rawLines[line - 2] ?? "")) continue;
18895
+ if (/\.{3}\s*\w+/.test(args)) continue;
18896
+ if (/\b(opts|options|spawnOptions)\b/.test(args)) continue;
18897
+ const identifiers = new Set(args.match(/\b[A-Za-z_$][\w$]*\b/g) ?? []);
18898
+ let satisfied = false;
18899
+ for (const id of identifiers) {
18900
+ if (SPAWNERS.includes(id)) continue;
18901
+ const decl = new RegExp(String.raw`\b(?:const|let|var)\s+${id}\b[^;]*`, "s").exec(src);
18902
+ if (decl && decl[0].includes("windowsHide")) {
18903
+ satisfied = true;
18904
+ break;
18905
+ }
18906
+ if (new RegExp(String.raw`\b${id}\s*:\s*\{[^}]*windowsHide`, "s").test(src)) {
18907
+ satisfied = true;
18908
+ break;
18909
+ }
18910
+ }
18911
+ if (satisfied) continue;
18912
+ found.push({ line, callee });
18913
+ }
18914
+ return found;
18915
+ }
18916
+ function policedFiles(root) {
18917
+ const r = (0, import_node_child_process11.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
18918
+ cwd: root,
18919
+ encoding: "utf8",
18920
+ windowsHide: true,
18921
+ maxBuffer: 32 * 1024 * 1024
18922
+ });
18923
+ if (r.status !== 0) {
18924
+ throw new Error(`spawn policy: git ls-files failed: ${(r.stderr || "").trim() || `exit ${r.status}`}`);
18925
+ }
18926
+ return (r.stdout ?? "").split("\0").filter(Boolean).filter((f) => SOURCE_EXT.test(f)).filter((f) => !EXCLUDED.some((re) => re.test(f)));
18927
+ }
18928
+ function runSpawnPolicy(root) {
18929
+ const files = policedFiles(root);
18930
+ const findings = [];
18931
+ for (const file of files) {
18932
+ let raw;
18933
+ try {
18934
+ raw = (0, import_node_fs21.readFileSync)((0, import_node_path19.join)(root, file), "utf8");
18935
+ } catch {
18936
+ continue;
18937
+ }
18938
+ for (const v of findViolationsInSource(raw)) {
18939
+ findings.push({
18940
+ file,
18941
+ line: v.line,
18942
+ callee: v.callee,
18943
+ detail: `${file}:${v.line} ${v.callee}(...) has no windowsHide`
18944
+ });
18945
+ }
18946
+ }
18947
+ return { ok: findings.length === 0, scannedCount: files.length, findings };
18948
+ }
18949
+
18950
+ // src/test-policy-core.ts
18951
+ var import_node_child_process12 = require("node:child_process");
18952
+ var import_node_fs22 = require("node:fs");
18953
+ var import_node_path20 = require("node:path");
18905
18954
  var POLICY_FILE = "test-policy.json";
18906
18955
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
18907
18956
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -18954,7 +19003,7 @@ function isTestPath(path2) {
18954
19003
  return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
18955
19004
  }
18956
19005
  function loadPolicy(root, readFile9 = readFileOrNull2) {
18957
- const raw = readFile9((0, import_node_path19.join)(root, POLICY_FILE));
19006
+ const raw = readFile9((0, import_node_path20.join)(root, POLICY_FILE));
18958
19007
  if (raw == null) return { mandatory: [], declared: false };
18959
19008
  try {
18960
19009
  return { ...JSON.parse(raw), declared: true };
@@ -18964,7 +19013,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
18964
19013
  }
18965
19014
  function readFileOrNull2(path2) {
18966
19015
  try {
18967
- return (0, import_node_fs21.readFileSync)(path2, "utf8");
19016
+ return (0, import_node_fs22.readFileSync)(path2, "utf8");
18968
19017
  } catch {
18969
19018
  return null;
18970
19019
  }
@@ -18991,12 +19040,12 @@ function classify(changed, policy, present = () => false) {
18991
19040
  const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
18992
19041
  return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
18993
19042
  }
18994
- function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs21.existsSync)(path2)) {
18995
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path19.join)(root, p)));
19043
+ function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
19044
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path20.join)(root, p)));
18996
19045
  }
18997
- function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs21.existsSync)(path2)) {
19046
+ function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
18998
19047
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
18999
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path19.join)(root, p)));
19048
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path20.join)(root, p)));
19000
19049
  }
19001
19050
  function evaluate(changed, policy, present = () => false) {
19002
19051
  const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -19029,14 +19078,15 @@ function evaluate(changed, policy, present = () => false) {
19029
19078
  return findings;
19030
19079
  }
19031
19080
  function git(args, cwd) {
19032
- return (0, import_node_child_process11.execFileSync)("git", args, { cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
19081
+ return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
19033
19082
  }
19034
19083
  var COAUTHOR_KEY = "Co-authored-by";
19035
19084
  var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
19036
19085
  var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
19037
19086
  function parseTrailers(message, cwd) {
19038
19087
  try {
19039
- return (0, import_node_child_process11.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
19088
+ return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
19089
+ windowsHide: true,
19040
19090
  cwd,
19041
19091
  input: message,
19042
19092
  encoding: "utf8",
@@ -19177,13 +19227,13 @@ function changedFilesSince(base, cwd) {
19177
19227
  }
19178
19228
  function runTestPolicy(root, deps = {}) {
19179
19229
  const policy = deps.policy ?? loadPolicy(root);
19180
- const exists = deps.exists ?? ((path2) => (0, import_node_fs21.existsSync)(path2));
19230
+ const exists = deps.exists ?? ((path2) => (0, import_node_fs22.existsSync)(path2));
19181
19231
  const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
19182
19232
  const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
19183
19233
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
19184
19234
  const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
19185
19235
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
19186
- const present = (path2) => exists((0, import_node_path19.join)(root, path2));
19236
+ const present = (path2) => exists((0, import_node_path20.join)(root, path2));
19187
19237
  const removedByThisDiff = removedPaths(changed);
19188
19238
  const staleFindings = [];
19189
19239
  const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
@@ -19339,8 +19389,8 @@ function docsAuditStatus(fetch2, opts) {
19339
19389
  }
19340
19390
 
19341
19391
  // src/project-info-sync.ts
19342
- var import_node_fs22 = require("node:fs");
19343
- var import_node_path20 = require("node:path");
19392
+ var import_node_fs23 = require("node:fs");
19393
+ var import_node_path21 = require("node:path");
19344
19394
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
19345
19395
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
19346
19396
  projectV2 { id }
@@ -19385,14 +19435,14 @@ function sharedName(entries, fallback) {
19385
19435
  }
19386
19436
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19387
19437
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
19388
- const readmePath = (0, import_node_path20.join)(repoRoot2, "README.md");
19389
- if (!(0, import_node_fs22.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
19438
+ const readmePath = (0, import_node_path21.join)(repoRoot2, "README.md");
19439
+ if (!(0, import_node_fs23.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
19390
19440
  const entries = entriesFor(project2, projects);
19391
19441
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
19392
19442
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
19393
19443
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
19394
19444
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
19395
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs22.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
19445
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs23.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
19396
19446
  const lines = [
19397
19447
  `# ${projectName}`,
19398
19448
  "",
@@ -19411,8 +19461,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19411
19461
  const targetBase = `https://github.com/${targetRepo2}`;
19412
19462
  const targetBranch = branchFor(targetRepo2, projects);
19413
19463
  const orgDocs = [
19414
- (0, import_node_fs22.existsSync)((0, import_node_path20.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
19415
- (0, import_node_fs22.existsSync)((0, import_node_path20.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
19464
+ (0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
19465
+ (0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
19416
19466
  ].filter(Boolean);
19417
19467
  if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
19418
19468
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
@@ -20271,8 +20321,8 @@ function writeError(res) {
20271
20321
  }
20272
20322
 
20273
20323
  // src/secrets-commands.ts
20274
- var import_node_fs23 = require("node:fs");
20275
- var import_node_path21 = require("node:path");
20324
+ var import_node_fs24 = require("node:fs");
20325
+ var import_node_path22 = require("node:path");
20276
20326
  var import_node_os8 = require("node:os");
20277
20327
 
20278
20328
  // src/project-runtime.ts
@@ -20396,18 +20446,18 @@ function collectMap(value, previous = []) {
20396
20446
  return [...previous, value];
20397
20447
  }
20398
20448
  async function decryptRailsCredentials(input) {
20399
- const appDir = (0, import_node_path21.resolve)(input.appDir ?? process.cwd());
20449
+ const appDir = (0, import_node_path22.resolve)(input.appDir ?? process.cwd());
20400
20450
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
20401
20451
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
20402
- const credentialsPath = (0, import_node_path21.resolve)(appDir, credentialsFile);
20403
- const masterKeyPath = (0, import_node_path21.resolve)(appDir, masterKeyFile);
20452
+ const credentialsPath = (0, import_node_path22.resolve)(appDir, credentialsFile);
20453
+ const masterKeyPath = (0, import_node_path22.resolve)(appDir, masterKeyFile);
20404
20454
  const env = {
20405
20455
  ...process.env,
20406
20456
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
20407
20457
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
20408
20458
  };
20409
- if ((0, import_node_fs23.existsSync)(masterKeyPath)) {
20410
- env.RAILS_MASTER_KEY = (0, import_node_fs23.readFileSync)(masterKeyPath, "utf8").trim();
20459
+ if ((0, import_node_fs24.existsSync)(masterKeyPath)) {
20460
+ env.RAILS_MASTER_KEY = (0, import_node_fs24.readFileSync)(masterKeyPath, "utf8").trim();
20411
20461
  }
20412
20462
  const script = [
20413
20463
  'require "json"',
@@ -20417,9 +20467,9 @@ async function decryptRailsCredentials(input) {
20417
20467
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
20418
20468
  "puts JSON.generate(config.config)"
20419
20469
  ].join("\n");
20420
- const scriptDir = (0, import_node_fs23.mkdtempSync)((0, import_node_path21.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
20421
- const scriptPath = (0, import_node_path21.join)(scriptDir, "decrypt.rb");
20422
- (0, import_node_fs23.writeFileSync)(scriptPath, script, "utf8");
20470
+ const scriptDir = (0, import_node_fs24.mkdtempSync)((0, import_node_path22.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
20471
+ const scriptPath = (0, import_node_path22.join)(scriptDir, "decrypt.rb");
20472
+ (0, import_node_fs24.writeFileSync)(scriptPath, script, "utf8");
20423
20473
  try {
20424
20474
  const args = ["exec", "ruby", scriptPath];
20425
20475
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -20431,7 +20481,7 @@ async function decryptRailsCredentials(input) {
20431
20481
  });
20432
20482
  return JSON.parse(stdout);
20433
20483
  } finally {
20434
- (0, import_node_fs23.rmSync)(scriptDir, { recursive: true, force: true });
20484
+ (0, import_node_fs24.rmSync)(scriptDir, { recursive: true, force: true });
20435
20485
  }
20436
20486
  }
20437
20487
  async function readSecretStdin() {
@@ -20521,7 +20571,7 @@ function registerSecretsCommands(program3) {
20521
20571
  let body;
20522
20572
  if (o.file) {
20523
20573
  try {
20524
- body = (0, import_node_fs23.readFileSync)((0, import_node_path21.resolve)(o.file), "utf8");
20574
+ body = (0, import_node_fs24.readFileSync)((0, import_node_path22.resolve)(o.file), "utf8");
20525
20575
  } catch (e) {
20526
20576
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
20527
20577
  }
@@ -20626,7 +20676,7 @@ function registerSecretsCommands(program3) {
20626
20676
  {
20627
20677
  ...d,
20628
20678
  decryptRailsCredentials,
20629
- removeFile: (path2) => (0, import_node_fs23.unlinkSync)((0, import_node_path21.resolve)(o.appDir ?? process.cwd(), path2))
20679
+ removeFile: (path2) => (0, import_node_fs24.unlinkSync)((0, import_node_path22.resolve)(o.appDir ?? process.cwd(), path2))
20630
20680
  },
20631
20681
  {
20632
20682
  repo: o.repo,
@@ -20766,6 +20816,13 @@ async function mintInstallationToken(deps) {
20766
20816
  }
20767
20817
  return { token: body.token, expiresAt: body.expires_at };
20768
20818
  }
20819
+ async function pollTokenOrUndefined(deps) {
20820
+ try {
20821
+ return (await mintInstallationToken(deps)).token;
20822
+ } catch {
20823
+ return void 0;
20824
+ }
20825
+ }
20769
20826
  async function activateAppActor(commandPath3, env, mint) {
20770
20827
  const requested = (env[APP_ACTOR_ENV] ?? "").trim();
20771
20828
  if (!requested) return "personal";
@@ -20781,46 +20838,9 @@ async function activateAppActor(commandPath3, env, mint) {
20781
20838
  env.GH_TOKEN = minted.token;
20782
20839
  return "app";
20783
20840
  }
20784
- async function probeRatePools(fetchLike, token) {
20785
- if (!token) return null;
20786
- try {
20787
- const res = await fetchLike("https://api.github.com/rate_limit", {
20788
- method: "GET",
20789
- headers: {
20790
- accept: "application/vnd.github+json",
20791
- "user-agent": "mmi-cli-app-actor",
20792
- authorization: `Bearer ${token}`
20793
- }
20794
- });
20795
- if (!res.ok) return null;
20796
- const body = await res.json();
20797
- const pool = (p) => typeof p?.remaining === "number" && typeof p?.limit === "number" && typeof p?.reset === "number" ? { remaining: p.remaining, limit: p.limit, reset: p.reset } : null;
20798
- const core = pool(body.resources?.core);
20799
- const graphql = pool(body.resources?.graphql);
20800
- if (!core || !graphql) return null;
20801
- return { core, graphql };
20802
- } catch {
20803
- return null;
20804
- }
20805
- }
20806
- function renderPoolDetail(probe) {
20807
- const fmt = (p) => `${p.remaining}/${p.limit} (resets ${new Date(p.reset * 1e3).toISOString().slice(11, 16)}Z)`;
20808
- return `core ${fmt(probe.core)}, graphql ${fmt(probe.graphql)}`;
20809
- }
20810
- function checkGithubPools(probe) {
20811
- if (!probe) return [];
20812
- const lines = [];
20813
- lines.push(probe.personal ? { id: "github-pools-personal", ok: true, label: "github pools (personal)", detail: renderPoolDetail(probe.personal) } : { id: "github-pools-personal", ok: false, label: "github pools (personal)", detail: "rate_limit unreadable", fix: "check `gh auth status`" });
20814
- if (typeof probe.app === "string") {
20815
- lines.push({ id: "github-pools-app", ok: false, label: "github pools (app actor)", fix: probe.app });
20816
- } else if (probe.app) {
20817
- lines.push({ id: "github-pools-app", ok: true, label: "github pools (app actor)", detail: renderPoolDetail(probe.app) });
20818
- }
20819
- return lines;
20820
- }
20821
20841
 
20822
20842
  // src/box-commands.ts
20823
- var import_node_fs24 = require("node:fs");
20843
+ var import_node_fs25 = require("node:fs");
20824
20844
 
20825
20845
  // src/box.ts
20826
20846
  var BOX_KEYS = {
@@ -21023,7 +21043,7 @@ function registerBoxCommands(program3) {
21023
21043
  }
21024
21044
  if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
21025
21045
  else if (o.ssh && o.script) {
21026
- (0, import_node_fs24.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
21046
+ (0, import_node_fs25.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
21027
21047
  console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
21028
21048
  } else if (o.ssh) console.log(`${formatSshRecipe(found)}
21029
21049
  ${SSH_RECIPE_AGENT_NOTE}`);
@@ -21038,7 +21058,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
21038
21058
 
21039
21059
  // src/schedules-commands.ts
21040
21060
  var import_promises3 = require("node:fs/promises");
21041
- var import_node_child_process12 = require("node:child_process");
21061
+ var import_node_child_process13 = require("node:child_process");
21042
21062
  var import_node_util7 = require("node:util");
21043
21063
 
21044
21064
  // src/schedules.ts
@@ -21326,12 +21346,6 @@ function spliceDoc(docText, generatedSection) {
21326
21346
  }
21327
21347
  return docText.slice(0, start) + generatedSection + docText.slice(end + DOC_END_MARKER.length);
21328
21348
  }
21329
- function driftClearableFrom(drift, repoName) {
21330
- if (drift.class !== "file-vs-registry-stale") return false;
21331
- const slash = drift.name.indexOf("/");
21332
- if (slash <= 0) return false;
21333
- return drift.name.slice(0, slash).toLowerCase() === repoName.toLowerCase();
21334
- }
21335
21349
  var HARBOUR_LLM_LAUNCHERS = /* @__PURE__ */ new Set(["cursor-agent"]);
21336
21350
  function isHarbourLlmLauncher(executor) {
21337
21351
  return HARBOUR_LLM_LAUNCHERS.has(executor);
@@ -21478,7 +21492,7 @@ function assembleReconciliation(githubEntries2, readRepos, registry2, activeWork
21478
21492
  }
21479
21493
 
21480
21494
  // src/schedules-commands.ts
21481
- var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process12.execFile);
21495
+ var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process13.execFile);
21482
21496
  var AWS_REGION = "eu-central-1";
21483
21497
  var AWS_TIMEOUT_MS = 3e4;
21484
21498
  var AWS_RETRY_DELAY_MS = 1500;
@@ -21715,7 +21729,7 @@ function registerSchedulesCommands(program3) {
21715
21729
 
21716
21730
  // src/file-lock.ts
21717
21731
  var import_promises4 = require("node:fs/promises");
21718
- var import_node_path22 = require("node:path");
21732
+ var import_node_path23 = require("node:path");
21719
21733
  var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
21720
21734
  var IMMEDIATE_RETRY_BUDGET = 3;
21721
21735
  var FileLockBusyError = class extends Error {
@@ -21800,7 +21814,7 @@ async function releaseFileLock(lockPath, guard) {
21800
21814
  }
21801
21815
  async function withFileLock(lockPath, opts, fn) {
21802
21816
  const resolved = resolveFileLockOpts(opts);
21803
- await (0, import_promises4.mkdir)((0, import_node_path22.dirname)(lockPath), { recursive: true }).catch(() => void 0);
21817
+ await (0, import_promises4.mkdir)((0, import_node_path23.dirname)(lockPath), { recursive: true }).catch(() => void 0);
21804
21818
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
21805
21819
  try {
21806
21820
  return await fn();
@@ -21811,7 +21825,7 @@ async function withFileLock(lockPath, opts, fn) {
21811
21825
 
21812
21826
  // src/schedules-lift-command.ts
21813
21827
  var import_promises5 = require("node:fs/promises");
21814
- var import_node_path23 = require("node:path");
21828
+ var import_node_path24 = require("node:path");
21815
21829
 
21816
21830
  // src/schedules-lift.ts
21817
21831
  var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
@@ -21917,7 +21931,7 @@ async function readWorkflowFiles(dir) {
21917
21931
  const files = [];
21918
21932
  for (const name of names.sort()) {
21919
21933
  if (!/\.ya?ml$/.test(name)) continue;
21920
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path23.join)(dir, name), "utf8") });
21934
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path24.join)(dir, name), "utf8") });
21921
21935
  }
21922
21936
  return files;
21923
21937
  }
@@ -22459,9 +22473,9 @@ function registerQueryCommands(program3) {
22459
22473
  }
22460
22474
 
22461
22475
  // src/bootstrap-commands.ts
22462
- var import_node_fs25 = require("node:fs");
22476
+ var import_node_fs26 = require("node:fs");
22463
22477
  var import_node_os9 = require("node:os");
22464
- var import_node_path24 = require("node:path");
22478
+ var import_node_path25 = require("node:path");
22465
22479
 
22466
22480
  // src/bootstrap-drift.ts
22467
22481
  function byteComparableSeeds(manifest, cls) {
@@ -23075,13 +23089,13 @@ function registerBootstrapCommands(program3) {
23075
23089
  client: defaultGitHubClient(),
23076
23090
  projectMeta: meta,
23077
23091
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
23078
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
23092
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
23079
23093
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
23080
23094
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
23081
23095
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
23082
23096
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
23083
23097
  // sanction, which is the pre-#3664 behaviour.
23084
- sanctionedAdmins: (0, import_node_fs25.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
23098
+ sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
23085
23099
  requiredGcpApis: (() => {
23086
23100
  const v = meta?.requiredGcpApis;
23087
23101
  if (Array.isArray(v)) return v;
@@ -23119,12 +23133,12 @@ function registerBootstrapCommands(program3) {
23119
23133
  bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
23120
23134
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
23121
23135
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23122
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
23123
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
23136
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
23137
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
23124
23138
  const hubContents = /* @__PURE__ */ new Map();
23125
23139
  for (const s of manifest.seeds) {
23126
23140
  if (s.ownership !== "org" || s.source !== "self") continue;
23127
- hubContents.set(s.target, (0, import_node_fs25.existsSync)(s.target) ? (0, import_node_fs25.readFileSync)(s.target, "utf8") : null);
23141
+ hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
23128
23142
  }
23129
23143
  let targets;
23130
23144
  let classOf = (_repo) => "deployable";
@@ -23201,8 +23215,8 @@ function registerBootstrapCommands(program3) {
23201
23215
  return fail(`bootstrap apply: ${e.message}`);
23202
23216
  }
23203
23217
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23204
- if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
23205
- const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
23218
+ if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
23219
+ const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
23206
23220
  const baseBranch = o.class === "content" ? "main" : "development";
23207
23221
  const slug = parsedRepo.slug;
23208
23222
  const onlyTarget = o.only.trim();
@@ -23213,16 +23227,16 @@ function registerBootstrapCommands(program3) {
23213
23227
  ${known}`);
23214
23228
  }
23215
23229
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
23216
- const readFile9 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
23230
+ const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
23217
23231
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
23218
23232
  const putSeed = async (target, content, ref, sha) => {
23219
- const tmp = (0, import_node_path24.join)((0, import_node_os9.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
23220
- (0, import_node_fs25.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
23233
+ const tmp = (0, import_node_path25.join)((0, import_node_os9.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
23234
+ (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
23221
23235
  try {
23222
23236
  await gh(contentPutInputArgs(repo, target, tmp));
23223
23237
  } finally {
23224
23238
  try {
23225
- (0, import_node_fs25.unlinkSync)(tmp);
23239
+ (0, import_node_fs26.unlinkSync)(tmp);
23226
23240
  } catch {
23227
23241
  }
23228
23242
  }
@@ -23474,12 +23488,12 @@ LIVE apply to ${repo}:
23474
23488
  }
23475
23489
 
23476
23490
  // src/stage-commands.ts
23477
- var import_node_fs27 = require("node:fs");
23478
- var import_node_path26 = require("node:path");
23491
+ var import_node_fs28 = require("node:fs");
23492
+ var import_node_path27 = require("node:path");
23479
23493
 
23480
23494
  // src/port-registry.ts
23481
- var import_node_fs26 = require("node:fs");
23482
- var import_node_path25 = require("node:path");
23495
+ var import_node_fs27 = require("node:fs");
23496
+ var import_node_path26 = require("node:path");
23483
23497
 
23484
23498
  // ../infra/port-geometry.mjs
23485
23499
  var PORT_BLOCK = 100;
@@ -23493,8 +23507,8 @@ function nextPortBlock(registry2) {
23493
23507
  return [base, base + PORT_SPAN];
23494
23508
  }
23495
23509
  function loadPortRegistry(path2) {
23496
- if (!(0, import_node_fs26.existsSync)(path2)) return {};
23497
- const raw = JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8"));
23510
+ if (!(0, import_node_fs27.existsSync)(path2)) return {};
23511
+ const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
23498
23512
  const out = {};
23499
23513
  for (const [key, value] of Object.entries(raw)) {
23500
23514
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -23508,9 +23522,9 @@ function ensurePortRange(repo, path2) {
23508
23522
  const existing = registry2[repo];
23509
23523
  if (existing) return existing;
23510
23524
  const range = nextPortBlock(registry2);
23511
- const raw = (0, import_node_fs26.existsSync)(path2) ? JSON.parse((0, import_node_fs26.readFileSync)(path2, "utf8")) : {};
23525
+ const raw = (0, import_node_fs27.existsSync)(path2) ? JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8")) : {};
23512
23526
  raw[repo] = range;
23513
- (0, import_node_fs26.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
23527
+ (0, import_node_fs27.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
23514
23528
  return range;
23515
23529
  }
23516
23530
  function portCursorSeed(registry2) {
@@ -23532,22 +23546,22 @@ function existingPortRange(repo, registry2) {
23532
23546
  return registry2[repo] ?? null;
23533
23547
  }
23534
23548
  function portRangeInfraAt(root, source) {
23535
- const registryPath = (0, import_node_path25.join)(root, "infra", "port-ranges.json");
23536
- const ddbScriptPath = (0, import_node_path25.join)(root, "infra", "port-ddb.mjs");
23537
- if (!(0, import_node_fs26.existsSync)(registryPath) || !(0, import_node_fs26.existsSync)(ddbScriptPath)) return null;
23549
+ const registryPath = (0, import_node_path26.join)(root, "infra", "port-ranges.json");
23550
+ const ddbScriptPath = (0, import_node_path26.join)(root, "infra", "port-ddb.mjs");
23551
+ if (!(0, import_node_fs27.existsSync)(registryPath) || !(0, import_node_fs27.existsSync)(ddbScriptPath)) return null;
23538
23552
  return { root, source, registryPath, ddbScriptPath };
23539
23553
  }
23540
23554
  function resolvePortRangeInfra(cwd, packageDir) {
23541
23555
  const direct = portRangeInfraAt(cwd, "cwd");
23542
23556
  if (direct) return direct;
23543
- for (let dir = cwd; ; dir = (0, import_node_path25.dirname)(dir)) {
23544
- const sibling = portRangeInfraAt((0, import_node_path25.join)(dir, "MMI-Hub"), "sibling-hub");
23557
+ for (let dir = cwd; ; dir = (0, import_node_path26.dirname)(dir)) {
23558
+ const sibling = portRangeInfraAt((0, import_node_path26.join)(dir, "MMI-Hub"), "sibling-hub");
23545
23559
  if (sibling) return sibling;
23546
- const parent = (0, import_node_path25.dirname)(dir);
23560
+ const parent = (0, import_node_path26.dirname)(dir);
23547
23561
  if (parent === dir) break;
23548
23562
  }
23549
23563
  if (packageDir) {
23550
- const pkgRoot = (0, import_node_path25.join)(packageDir, "..", "..");
23564
+ const pkgRoot = (0, import_node_path26.join)(packageDir, "..", "..");
23551
23565
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
23552
23566
  if (pkgFrom) return pkgFrom;
23553
23567
  }
@@ -23723,8 +23737,8 @@ function registerStageCommands(program3) {
23723
23737
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
23724
23738
  return decideStage({
23725
23739
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
23726
- hasCompose: (0, import_node_fs27.existsSync)((0, import_node_path26.join)(process.cwd(), "docker-compose.yml")),
23727
- hasEnvExample: (0, import_node_fs27.existsSync)((0, import_node_path26.join)(process.cwd(), ".env.example"))
23740
+ hasCompose: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), "docker-compose.yml")),
23741
+ hasEnvExample: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), ".env.example"))
23728
23742
  });
23729
23743
  }
23730
23744
  async function fetchStageVaultEnvMerge() {
@@ -24162,11 +24176,11 @@ function registerBoardCommands(program3) {
24162
24176
  }
24163
24177
 
24164
24178
  // src/merge-cleanup.ts
24165
- var import_node_fs28 = require("node:fs");
24179
+ var import_node_fs29 = require("node:fs");
24166
24180
  var import_promises7 = require("node:fs/promises");
24167
- var import_node_path28 = require("node:path");
24181
+ var import_node_path29 = require("node:path");
24168
24182
  var import_node_os10 = require("node:os");
24169
- var import_node_child_process13 = require("node:child_process");
24183
+ var import_node_child_process14 = require("node:child_process");
24170
24184
 
24171
24185
  // src/board-advance.ts
24172
24186
  function repoOf2(ref) {
@@ -24252,7 +24266,7 @@ function boardAdvanceFailureMessage(result) {
24252
24266
 
24253
24267
  // src/deferred-registry-store.ts
24254
24268
  var import_promises6 = require("node:fs/promises");
24255
- var import_node_path27 = require("node:path");
24269
+ var import_node_path28 = require("node:path");
24256
24270
  var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
24257
24271
  async function atomicWrite(target, contents) {
24258
24272
  const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
@@ -24303,12 +24317,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
24303
24317
  },
24304
24318
  // Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
24305
24319
  write: async (entries) => {
24306
- await (0, import_promises6.mkdir)((0, import_node_path27.dirname)(registryPath), { recursive: true });
24320
+ await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
24307
24321
  await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
24308
24322
  },
24309
24323
  // Serialized read-modify-write under the repo-wide lock (#2846).
24310
24324
  update: async (mutate) => {
24311
- await (0, import_promises6.mkdir)((0, import_node_path27.dirname)(registryPath), { recursive: true });
24325
+ await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
24312
24326
  const deadline = Date.now() + opts.maxWaitMs;
24313
24327
  for (; ; ) {
24314
24328
  const guard = await acquireLock(lockPath, opts, deadline);
@@ -24469,7 +24483,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24469
24483
  );
24470
24484
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
24471
24485
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
24472
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path28.dirname)((0, import_node_path28.dirname)(worktreeGitRoot)) : repoRoot2;
24486
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
24473
24487
  const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
24474
24488
  const owners = readWorktreeOwners(primaryRepoRoot);
24475
24489
  const removalNow = Date.now();
@@ -24500,7 +24514,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24500
24514
  const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
24501
24515
  beforeWorktrees,
24502
24516
  startingPath: branch.worktreePath,
24503
- pathExists: (p) => (0, import_node_fs28.existsSync)(p),
24517
+ pathExists: (p) => (0, import_node_fs29.existsSync)(p),
24504
24518
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
24505
24519
  teardownWorktreeStage,
24506
24520
  deferredStore,
@@ -24528,7 +24542,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24528
24542
  for (const wt of worktreeDirsToRemove) {
24529
24543
  try {
24530
24544
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
24531
- realpath: (path2) => (0, import_node_fs28.realpathSync)(path2)
24545
+ realpath: (path2) => (0, import_node_fs29.realpathSync)(path2)
24532
24546
  });
24533
24547
  if (!cleanupTarget.ok) {
24534
24548
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -24593,13 +24607,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
24593
24607
  const commits = JSON.parse(raw).commits ?? [];
24594
24608
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
24595
24609
  if (!body) return void 0;
24596
- const dir = (0, import_node_fs28.mkdtempSync)((0, import_node_path28.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
24597
- const path2 = (0, import_node_path28.join)(dir, "body.txt");
24598
- (0, import_node_fs28.writeFileSync)(path2, `${body}
24610
+ const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path29.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
24611
+ const path2 = (0, import_node_path29.join)(dir, "body.txt");
24612
+ (0, import_node_fs29.writeFileSync)(path2, `${body}
24599
24613
  `, "utf8");
24600
24614
  return { path: path2, cleanup: () => {
24601
24615
  try {
24602
- (0, import_node_fs28.rmSync)(dir, { recursive: true, force: true });
24616
+ (0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
24603
24617
  } catch {
24604
24618
  }
24605
24619
  } };
@@ -24707,7 +24721,7 @@ async function remoteBranchExists2(branch, options = {}) {
24707
24721
  }
24708
24722
  var COMPOSE_TIMEOUT_MS = 12e4;
24709
24723
  function spawnDeferredGcSweep() {
24710
- spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
24724
+ spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
24711
24725
  }
24712
24726
  async function createDeferredWorktreeStore() {
24713
24727
  try {
@@ -24721,13 +24735,13 @@ var realWorktreeDirRemover = {
24721
24735
  probe: (p) => {
24722
24736
  let st;
24723
24737
  try {
24724
- st = (0, import_node_fs28.lstatSync)(p);
24738
+ st = (0, import_node_fs29.lstatSync)(p);
24725
24739
  } catch {
24726
24740
  return null;
24727
24741
  }
24728
24742
  if (st.isSymbolicLink()) return "link";
24729
24743
  try {
24730
- (0, import_node_fs28.readlinkSync)(p);
24744
+ (0, import_node_fs29.readlinkSync)(p);
24731
24745
  return "link";
24732
24746
  } catch {
24733
24747
  }
@@ -24735,7 +24749,7 @@ var realWorktreeDirRemover = {
24735
24749
  },
24736
24750
  readdir: (p) => {
24737
24751
  try {
24738
- return (0, import_node_fs28.readdirSync)(p);
24752
+ return (0, import_node_fs29.readdirSync)(p);
24739
24753
  } catch {
24740
24754
  return [];
24741
24755
  }
@@ -24744,9 +24758,9 @@ var realWorktreeDirRemover = {
24744
24758
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
24745
24759
  detachLink: (p) => {
24746
24760
  try {
24747
- (0, import_node_fs28.rmdirSync)(p);
24761
+ (0, import_node_fs29.rmdirSync)(p);
24748
24762
  } catch {
24749
- (0, import_node_fs28.unlinkSync)(p);
24763
+ (0, import_node_fs29.unlinkSync)(p);
24750
24764
  }
24751
24765
  },
24752
24766
  removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
@@ -24779,9 +24793,9 @@ async function worktreeHasStageState(worktreePath) {
24779
24793
  }
24780
24794
  }
24781
24795
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
24782
- if (!(0, import_node_fs28.existsSync)(statePath)) return false;
24796
+ if (!(0, import_node_fs29.existsSync)(statePath)) return false;
24783
24797
  try {
24784
- const state = JSON.parse((0, import_node_fs28.readFileSync)(statePath, "utf8"));
24798
+ const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
24785
24799
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
24786
24800
  return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
24787
24801
  } catch {
@@ -24804,8 +24818,25 @@ function teardownWorktreeStage(worktreePath) {
24804
24818
 
24805
24819
  // src/pr-checks-rest.ts
24806
24820
  var REST_GH_TIMEOUT_MS = 2e4;
24821
+ var pollTokenProvider;
24822
+ var pollTokenOnce;
24823
+ function setPollTokenProvider(provider) {
24824
+ pollTokenProvider = provider;
24825
+ pollTokenOnce = void 0;
24826
+ }
24827
+ async function pollToken() {
24828
+ if (!pollTokenProvider) return void 0;
24829
+ pollTokenOnce ??= pollTokenProvider().catch(() => void 0);
24830
+ return pollTokenOnce;
24831
+ }
24807
24832
  async function defaultGhApi(args) {
24808
- const { stdout } = await execFileP2("gh", ["api", ...args], { timeout: REST_GH_TIMEOUT_MS });
24833
+ const token = await pollToken();
24834
+ const { stdout } = await execFileP2("gh", ["api", ...args], {
24835
+ timeout: REST_GH_TIMEOUT_MS,
24836
+ // Only when a token was actually minted: passing `env` at all would otherwise replace the
24837
+ // inherited environment wholesale, and `gh` needs the rest of it (PATH, GH_HOST, config home).
24838
+ ...token ? { env: { ...process.env, GH_TOKEN: token } } : {}
24839
+ });
24809
24840
  return stdout;
24810
24841
  }
24811
24842
  function classifyCheckRun(run) {
@@ -24988,9 +25019,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
24988
25019
  }
24989
25020
 
24990
25021
  // src/worktree-lifecycle-commands.ts
24991
- var import_node_fs29 = require("node:fs");
25022
+ var import_node_fs30 = require("node:fs");
24992
25023
  var import_promises8 = require("node:fs/promises");
24993
- var import_node_path29 = require("node:path");
25024
+ var import_node_path30 = require("node:path");
24994
25025
  var GH_TIMEOUT_MS = 2e4;
24995
25026
  var DEFAULT_BASE = "origin/development";
24996
25027
  var DEFAULT_REMOTE = "origin";
@@ -25126,7 +25157,7 @@ function classifyStaleLeaks(input) {
25126
25157
  var defaultOrphanDirScanDeps = {
25127
25158
  listDirs: (root) => {
25128
25159
  try {
25129
- return (0, import_node_fs29.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path29.join)(root, e.name));
25160
+ return (0, import_node_fs30.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path30.join)(root, e.name));
25130
25161
  } catch {
25131
25162
  return [];
25132
25163
  }
@@ -25273,13 +25304,13 @@ function registerWorktreeCommands(program3) {
25273
25304
  const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
25274
25305
  const branch = headBorn ? (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim() : (await execFileP2("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
25275
25306
  if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
25276
- const gitFile = (0, import_node_path29.join)(wtPath, ".git");
25277
- const isLinked = (0, import_node_fs29.existsSync)(gitFile) && (0, import_node_fs29.statSync)(gitFile).isFile();
25307
+ const gitFile = (0, import_node_path30.join)(wtPath, ".git");
25308
+ const isLinked = (0, import_node_fs30.existsSync)(gitFile) && (0, import_node_fs30.statSync)(gitFile).isFile();
25278
25309
  if (apply && !isLinked) {
25279
25310
  return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
25280
25311
  }
25281
25312
  const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
25282
- const primaryCheckout = commonDir ? (0, import_node_path29.dirname)(commonDir) : wtPath;
25313
+ const primaryCheckout = commonDir ? (0, import_node_path30.dirname)(commonDir) : wtPath;
25283
25314
  const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
25284
25315
  const orphan = classifyOrphanedWorktree({
25285
25316
  branch,
@@ -25466,10 +25497,10 @@ async function gatherWorktreeContext() {
25466
25497
  if (s) stages.push({ path: wt.path, port: s.port });
25467
25498
  }
25468
25499
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
25469
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
25500
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
25470
25501
  const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
25471
25502
  let orphanDirs = [];
25472
- if ((0, import_node_fs29.existsSync)(wtRoot)) {
25503
+ if ((0, import_node_fs30.existsSync)(wtRoot)) {
25473
25504
  orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
25474
25505
  ...defaultOrphanDirScanDeps,
25475
25506
  listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
@@ -25492,7 +25523,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
25492
25523
  }
25493
25524
 
25494
25525
  // src/issue-commands.ts
25495
- var import_node_fs30 = require("node:fs");
25526
+ var import_node_fs31 = require("node:fs");
25496
25527
  var import_node_crypto5 = require("node:crypto");
25497
25528
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
25498
25529
  var ReparentConflictError = class extends Error {
@@ -25510,7 +25541,7 @@ async function editIssue(client, options, deps = {}) {
25510
25541
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
25511
25542
  const patch = {};
25512
25543
  let bodyChanged = false;
25513
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs30.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
25544
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
25514
25545
  if (options.titleFile !== void 0) {
25515
25546
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
25516
25547
  } else if (options.title !== void 0) {
@@ -26073,7 +26104,7 @@ function extendCreateCommand(issue2, batchAttach) {
26073
26104
  if (opts.batch) {
26074
26105
  let specs;
26075
26106
  try {
26076
- const raw = (0, import_node_fs30.readFileSync)(opts.batch, "utf8");
26107
+ const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
26077
26108
  specs = JSON.parse(raw);
26078
26109
  if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
26079
26110
  } catch (e) {
@@ -26147,8 +26178,8 @@ ${lines}`, {
26147
26178
  }
26148
26179
 
26149
26180
  // src/train-commands.ts
26150
- var import_node_fs31 = require("node:fs");
26151
- var import_node_path30 = require("node:path");
26181
+ var import_node_fs32 = require("node:fs");
26182
+ var import_node_path31 = require("node:path");
26152
26183
 
26153
26184
  // src/train-status.ts
26154
26185
  function buildTrainStatusReport(input) {
@@ -26188,7 +26219,7 @@ function formatTrainStatus(r) {
26188
26219
  // src/train-commands.ts
26189
26220
  function readRepoVersion() {
26190
26221
  try {
26191
- return JSON.parse((0, import_node_fs31.readFileSync)((0, import_node_path30.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
26222
+ return JSON.parse((0, import_node_fs32.readFileSync)((0, import_node_path31.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
26192
26223
  } catch {
26193
26224
  return void 0;
26194
26225
  }
@@ -26334,9 +26365,9 @@ function registerDeployCommands(program3) {
26334
26365
  }
26335
26366
 
26336
26367
  // src/discovery-commands.ts
26337
- var import_node_fs32 = require("node:fs");
26368
+ var import_node_fs33 = require("node:fs");
26338
26369
  var import_node_os11 = require("node:os");
26339
- var import_node_path31 = require("node:path");
26370
+ var import_node_path32 = require("node:path");
26340
26371
  var GC_GH_TIMEOUT_MS3 = 2e4;
26341
26372
  async function collectStatus() {
26342
26373
  let branch = "";
@@ -26513,8 +26544,8 @@ async function collectOnboardStatus() {
26513
26544
  }
26514
26545
  const home = (0, import_node_os11.homedir)();
26515
26546
  const plugin = onboardPluginGate({
26516
- readKnown: () => readFileSyncSafe((0, import_node_path31.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs32.readFileSync),
26517
- readSettings: () => readFileSyncSafe((0, import_node_path31.join)(home, ".claude", "settings.json"), import_node_fs32.readFileSync)
26547
+ readKnown: () => readFileSyncSafe((0, import_node_path32.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs33.readFileSync),
26548
+ readSettings: () => readFileSyncSafe((0, import_node_path32.join)(home, ".claude", "settings.json"), import_node_fs33.readFileSync)
26518
26549
  });
26519
26550
  return { track, board, registry: registry2, secrets, plugin, nextCommand };
26520
26551
  }
@@ -28100,7 +28131,7 @@ function planSurfaceRepair(diagnosis) {
28100
28131
  owner: "mmi-cli",
28101
28132
  command: "mmi-cli plugin heal",
28102
28133
  changes: ["replace the active MMI plugin installation", `reload via ${descriptor.reload}`],
28103
- instruction: `run \`mmi-cli doctor --apply\` (or \`mmi-cli plugin heal\`), then ${reloadInstruction(descriptor)}`
28134
+ instruction: `run \`mmi-cli doctor\` (or \`mmi-cli plugin heal\`), then ${reloadInstruction(descriptor)}`
28104
28135
  };
28105
28136
  }
28106
28137
  function buildSurfaceDoctorCheck(diagnosis) {
@@ -28206,34 +28237,6 @@ function checkRepoWorktrees(probe) {
28206
28237
  ]
28207
28238
  };
28208
28239
  }
28209
- function formatBytes(bytes, partial) {
28210
- const prefix = partial ? "\u2265" : "";
28211
- return bytes >= 1e9 ? `${prefix}${(bytes / 1e9).toFixed(2)} GB` : `${prefix}${(bytes / 1e6).toFixed(1)} MB`;
28212
- }
28213
- function checkWorktreeRoots(probe) {
28214
- if (!probe) return null;
28215
- const evidence = [
28216
- `scanned by worktree gc/list: ${probe.authoritative}`,
28217
- ...probe.stray.length ? probe.stray.map((s) => `unreachable by any gc: ${s.path} \u2014 ${s.dirs} dir(s), ${formatBytes(s.bytes, s.partial)}`) : ["no other *worktrees* directory beside this repo"]
28218
- ];
28219
- if (!probe.stray.length) return { ok: true, id: "worktree-roots", label: "worktree roots", verbose: evidence };
28220
- const named = probe.stray.map((s) => `${s.path} (${s.dirs} dir(s), ${formatBytes(s.bytes, s.partial)})`).join("; ");
28221
- const cutShort = probe.stray.some((s) => s.partial) ? " (a \u2265 figure is a floor \u2014 the size walk hit its time budget, so the real total is larger)" : "";
28222
- return {
28223
- ok: false,
28224
- id: "worktree-roots",
28225
- label: "worktree roots",
28226
- // #3485: report-only in EVERY mode means report-only in the exit code too. `worktree gc --root` does
28227
- // exist and is the eventual fix, but this row is deliberately not a gate: the remedy is a human
28228
- // review of directories that may hold unlanded work, and a size-based auto-sweep once nearly deleted
28229
- // an unlanded correctness fix. Counting it made `mmi-cli doctor` exit 1 until someone swept by hand.
28230
- reportOnly: true,
28231
- // The fix is a review, never a delete: `gc --root` still refuses anything it cannot classify as dead,
28232
- // and doctor itself — with or without --apply — never touches these directories.
28233
- fix: `${probe.stray.length} worktrees root(s) outside ${probe.authoritative} \u2014 ${named}; no gc scans them. Review, then sweep deliberately from the owning repo with \`mmi-cli worktree gc --root <path> --apply\` (report-only here: doctor never deletes these)${cutShort}`,
28234
- verbose: evidence
28235
- };
28236
- }
28237
28240
  function checkCodexHookTrust(probe, displayName = "Codex") {
28238
28241
  if (!probe?.applicable) return null;
28239
28242
  const evidence = [
@@ -28344,81 +28347,6 @@ function checkTrainSync(result) {
28344
28347
  verbose: evidence
28345
28348
  };
28346
28349
  }
28347
- function checkSchedules(probe) {
28348
- if (!probe) return null;
28349
- const evidence = [
28350
- `${probe.armed} armed (${probe.live} live, ${probe.declared} declared)`,
28351
- ...probe.incomplete.map((i) => `incomplete: ${i}`),
28352
- ...probe.drift.map((d) => `drift: ${d}`)
28353
- ];
28354
- const clearableHere = probe.clearableHere ?? 0;
28355
- if (probe.incomplete.length) {
28356
- return {
28357
- ok: false,
28358
- id: "schedules",
28359
- label: "schedules",
28360
- // #3485: ruled report-only — an unreadable source is a read failure somewhere in the org, and
28361
- // nothing in this checkout clears it.
28362
- reportOnly: true,
28363
- detail: `${probe.incomplete.length} source(s) unreadable`,
28364
- fix: "run `mmi-cli org schedules` for the full report \u2014 the notebook may be missing armed entries",
28365
- verbose: evidence
28366
- };
28367
- }
28368
- if (probe.drift.length) {
28369
- return {
28370
- ok: false,
28371
- id: "schedules",
28372
- label: "schedules",
28373
- // #3485 ruled this row report-only because the drift classes are owned by other repos. #3492 makes
28374
- // that conditional rather than blanket: when a finding IS clearable from this checkout — a
28375
- // `file-vs-registry-stale` row for this repo, one `org schedules register` away — the row gates
28376
- // like any other actionable red. Exempting a fault the operator can fix in one command is the
28377
- // tolerated-red failure the tier exists to prevent, not an instance of it.
28378
- ...clearableHere > 0 ? {} : { reportOnly: true },
28379
- detail: `${probe.drift.length} drift finding(s)`,
28380
- fix: clearableHere > 0 ? `${clearableHere} of ${probe.drift.length} clearable from this repo \u2014 from an up-to-date checkout run \`mmi-cli org schedules register\` here, then \`mmi-cli org schedules\` for the rest (each line names its own per-class remedy, applied in the owning repo)` : "run `mmi-cli org schedules` \u2014 each drift line names its per-class remedy (file/registry/live mismatch or harbour enforcement), applied in the owning repo",
28381
- verbose: evidence
28382
- };
28383
- }
28384
- return { ok: true, id: "schedules", label: "schedules", detail: `${probe.armed} armed`, verbose: evidence };
28385
- }
28386
- function checkDocsAudit(probe) {
28387
- if (!probe) return null;
28388
- if (!probe.armed) {
28389
- return { ok: true, id: "docs-audit", label: "docs-audit", detail: "janitor not armed", verbose: [probe.detail] };
28390
- }
28391
- if (!probe.ok) {
28392
- return {
28393
- ok: false,
28394
- id: "docs-audit",
28395
- label: "docs-audit",
28396
- // #3485: ruled report-only — the remedy is re-running or re-arming the janitor in the repo that
28397
- // owns it. `mmi-cli docs audit record` can write a verdict from here, but hand-writing one to clear
28398
- // a dead-man check defeats the check (#3067 pillar 4: silence is an alarm, never a success).
28399
- reportOnly: true,
28400
- fix: `${probe.detail} \u2014 re-run the janitor in the owning repo or re-arm its schedule (the remedy never lives in this doctor)`,
28401
- verbose: [probe.detail]
28402
- };
28403
- }
28404
- return { ok: true, id: "docs-audit", label: "docs-audit", detail: probe.detail, verbose: [probe.detail] };
28405
- }
28406
- function checkRedactorLiveness(probe) {
28407
- if (!probe) return null;
28408
- const evidence = [`secret-redact failed rows in the last 48h: ${probe.failed}${probe.lastTs ? ` (last ${probe.lastTs})` : ""}`];
28409
- if (probe.failed === 0) {
28410
- return { ok: true, id: "redactor-liveness", label: "secret-redact liveness", verbose: evidence };
28411
- }
28412
- return {
28413
- ok: false,
28414
- id: "redactor-liveness",
28415
- label: "secret-redact liveness",
28416
- reportOnly: true,
28417
- detail: `${probe.failed} crash marker${probe.failed === 1 ? "" : "s"} in 48h`,
28418
- fix: "the redactor is crashing during scans \u2014 read `.git/mmi-runtime/hooks/activity.jsonl` for the error and fix it; every crash is a scan that never ran",
28419
- verbose: evidence
28420
- };
28421
- }
28422
28350
  function checkSessionPayload(probe) {
28423
28351
  if (!probe) return null;
28424
28352
  const { chars } = probe;
@@ -28473,13 +28401,14 @@ function gcReapable(plan) {
28473
28401
  return plan.branches.length + plan.trackingRefs.length + plan.worktreeDirs.length;
28474
28402
  }
28475
28403
  async function runDoctorClean(opts, io, deps) {
28476
- const applyEnv = Boolean(opts.apply) || Boolean(opts.preflight);
28477
- const applyRepo = Boolean(opts.apply) && opts.repoWrites !== false;
28404
+ const full = !opts.fast && !opts.banner && !opts.preflight;
28405
+ const applyEnv = full || Boolean(opts.preflight);
28406
+ const applyRepo = full && opts.repoWrites !== false;
28478
28407
  const lane = {
28479
28408
  banner: Boolean(opts.banner),
28480
28409
  fast: Boolean(opts.fast),
28481
28410
  preflight: Boolean(opts.preflight),
28482
- full: !opts.fast && !opts.banner && !opts.preflight
28411
+ full
28483
28412
  };
28484
28413
  const probeReleased = !opts.fast || Boolean(opts.self);
28485
28414
  const probeAws = !opts.fast && !opts.banner;
@@ -28577,9 +28506,6 @@ async function runDoctorClean(opts, io, deps) {
28577
28506
  async function runGithubAuthRow() {
28578
28507
  emitNow(checkGithubAuth({ login, ghInstalled: ghInstalled2, reach }));
28579
28508
  }
28580
- async function runGithubPoolsRows() {
28581
- for (const pool of checkGithubPools(await deps.githubPools())) emitNow(pool);
28582
- }
28583
28509
  async function runAwsRow() {
28584
28510
  const aws = checkAwsIdentity({ isOrgRepo, probed: probeAws, callerArn });
28585
28511
  if (aws) emitNow(aws);
@@ -28600,7 +28526,7 @@ async function runDoctorClean(opts, io, deps) {
28600
28526
  emitNow({ ok: true, id: "gitignore-block", label: "gitignore block", detail: "rewrote managed block", verbose: giEvidence });
28601
28527
  restartPending = true;
28602
28528
  } else {
28603
- emitNow({ ok: false, id: "gitignore-block", label: "gitignore block", fix: "run `mmi-cli doctor --apply` to write the org-managed .gitignore block", verbose: giEvidence });
28529
+ emitNow({ ok: false, id: "gitignore-block", label: "gitignore block", fix: "run `mmi-cli doctor` (without --no-repo-writes) to write the org-managed .gitignore block", verbose: giEvidence });
28604
28530
  }
28605
28531
  }
28606
28532
  async function runPluginCacheRow() {
@@ -28611,37 +28537,15 @@ async function runDoctorClean(opts, io, deps) {
28611
28537
  if (payload) emitNow(payload);
28612
28538
  }
28613
28539
  async function runMarketplaceRows() {
28540
+ if (applyEnv) {
28541
+ const healed = deps.healMarketplacePins();
28542
+ if (healed) {
28543
+ healIntent(`marketplace pins \u2014 ${healed}`);
28544
+ restartPending = true;
28545
+ }
28546
+ }
28614
28547
  for (const row of deps.marketplaceRows()) emitNow(row);
28615
28548
  }
28616
- async function runSchedulesRow() {
28617
- const probe = await deps.schedulesNotebook().catch((e) => ({
28618
- armed: 0,
28619
- live: 0,
28620
- declared: 0,
28621
- incomplete: [`schedules probe failed \u2014 ${e.message}`],
28622
- drift: []
28623
- }));
28624
- const sched = checkSchedules(probe);
28625
- if (sched) emitNow(sched);
28626
- }
28627
- async function runRedactorRow() {
28628
- const redactor = checkRedactorLiveness(deps.redactorLiveness());
28629
- if (redactor) emitNow(redactor);
28630
- }
28631
- async function runDocsAuditRow() {
28632
- const probe = await deps.docsAudit().catch((e) => ({
28633
- armed: true,
28634
- ok: false,
28635
- detail: `docs-audit probe failed \u2014 ${e.message}`
28636
- }));
28637
- const docsAudit2 = checkDocsAudit(probe);
28638
- if (docsAudit2) emitNow(docsAudit2);
28639
- }
28640
- async function runWorktreeRootsRow() {
28641
- const probe = await deps.worktreeRoots().catch(() => void 0);
28642
- const roots = checkWorktreeRoots(probe);
28643
- if (roots) emitNow(roots);
28644
- }
28645
28549
  async function runTrainSyncRow() {
28646
28550
  try {
28647
28551
  emitNow(checkTrainSync(await deps.syncTrain()));
@@ -28697,7 +28601,7 @@ async function runDoctorClean(opts, io, deps) {
28697
28601
  // #3574: `${n} stale` was prepended to `fix` because the ✗ branch would not render `detail` —
28698
28602
  // the same fact this row already puts in `detail` on its ✓ path four lines up. One convention now.
28699
28603
  detail: `${n} stale`,
28700
- fix: "run `mmi-cli doctor --apply` to reap merged branches, stale refs, and dead worktrees",
28604
+ fix: "run `mmi-cli doctor` (without --no-repo-writes) to reap merged branches, stale refs, and dead worktrees",
28701
28605
  verbose: gcEvidence
28702
28606
  });
28703
28607
  }
@@ -28711,7 +28615,7 @@ async function runDoctorClean(opts, io, deps) {
28711
28615
  emitNow({ id: "scratch", ok: true, label: "scratch", detail: pruned ? `removed ${pruned} aged item(s)` : "nothing stale", verbose: scratchEvidence });
28712
28616
  if (pruned) restartPending = true;
28713
28617
  } else {
28714
- emitNow(scratch.plan.safeAuto.length === 0 ? { id: "scratch", ok: true, label: "scratch", verbose: scratchEvidence } : { id: "scratch", ok: false, label: "scratch", detail: `${scratch.plan.safeAuto.length} aged item(s)`, fix: "run `mmi-cli doctor --apply`", verbose: scratchEvidence });
28618
+ emitNow(scratch.plan.safeAuto.length === 0 ? { id: "scratch", ok: true, label: "scratch", verbose: scratchEvidence } : { id: "scratch", ok: false, label: "scratch", detail: `${scratch.plan.safeAuto.length} aged item(s)`, fix: "run `mmi-cli doctor` (without --no-repo-writes)", verbose: scratchEvidence });
28715
28619
  }
28716
28620
  }
28717
28621
  const table = [
@@ -28720,17 +28624,12 @@ async function runDoctorClean(opts, io, deps) {
28720
28624
  { id: "plugin", when: true, run: runPluginRow },
28721
28625
  { id: "cli-version", when: true, run: runCliRow },
28722
28626
  { id: "github-auth", when: true, run: runGithubAuthRow },
28723
- { id: "github-pools", when: lane.full, run: runGithubPoolsRows },
28724
28627
  { id: "aws-identity", when: true, run: runAwsRow },
28725
28628
  { id: "repo-worktrees", when: true, run: runRepoWorktreesRow },
28726
28629
  { id: "gitignore-block", when: isOrgRepo, run: runGitignoreRow },
28727
28630
  { id: "plugin-cache", when: true, run: runPluginCacheRow },
28728
28631
  { id: "sessionstart-payload", when: true, run: runSessionPayloadRow },
28729
28632
  { id: "marketplace", when: true, run: runMarketplaceRows },
28730
- { id: "schedules", when: lane.full, run: runSchedulesRow },
28731
- { id: "redactor-liveness", when: lane.full, run: runRedactorRow },
28732
- { id: "docs-audit", when: lane.full, run: runDocsAuditRow },
28733
- { id: "worktree-roots", when: lane.full, run: runWorktreeRootsRow },
28734
28633
  // The two most expensive things in this file — a real `git fetch` plus train-branch fast-forward,
28735
28634
  // and a `gh`-backed gc sweep with a 20s timeout — so org repos on the full lane only (#3485).
28736
28635
  { id: "train-branches", when: isOrgRepo && lane.full, run: runTrainSyncRow },
@@ -28789,17 +28688,17 @@ function parseOriginRepo(remoteUrl) {
28789
28688
  }
28790
28689
  function ghHostsConfigPath(env, platform2) {
28791
28690
  const sep2 = platform2 === "win32" ? "\\" : "/";
28792
- const join28 = (...parts) => parts.join(sep2);
28691
+ const join29 = (...parts) => parts.join(sep2);
28793
28692
  const explicit = env.GH_CONFIG_DIR?.trim();
28794
- if (explicit) return join28(explicit, "hosts.yml");
28693
+ if (explicit) return join29(explicit, "hosts.yml");
28795
28694
  if (platform2 === "win32") {
28796
28695
  const appData = (env.AppData ?? env.APPDATA)?.trim();
28797
- return appData ? join28(appData, "GitHub CLI", "hosts.yml") : void 0;
28696
+ return appData ? join29(appData, "GitHub CLI", "hosts.yml") : void 0;
28798
28697
  }
28799
28698
  const xdg = env.XDG_CONFIG_HOME?.trim();
28800
- if (xdg) return join28(xdg, "gh", "hosts.yml");
28699
+ if (xdg) return join29(xdg, "gh", "hosts.yml");
28801
28700
  const home = env.HOME?.trim();
28802
- return home ? join28(home, ".config", "gh", "hosts.yml") : void 0;
28701
+ return home ? join29(home, ".config", "gh", "hosts.yml") : void 0;
28803
28702
  }
28804
28703
  function parseGhHostsAccounts(yaml, host = "github.com") {
28805
28704
  let hostIndent = null;
@@ -28849,17 +28748,17 @@ function ghAccountCaveat(announcedLogin, accounts) {
28849
28748
  }
28850
28749
 
28851
28750
  // src/doctor-io.ts
28852
- var import_node_fs33 = require("node:fs");
28751
+ var import_node_fs34 = require("node:fs");
28853
28752
  var import_node_os12 = require("node:os");
28854
- var import_node_path32 = require("node:path");
28855
- var import_node_child_process14 = require("node:child_process");
28753
+ var import_node_path33 = require("node:path");
28754
+ var import_node_child_process15 = require("node:child_process");
28856
28755
  var import_node_util8 = require("node:util");
28857
- var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process14.execFile);
28756
+ var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
28858
28757
  var MMI_PLUGIN_ID2 = "mmi@mutmutco";
28859
28758
  function installedClaudePluginVersion() {
28860
28759
  try {
28861
28760
  const file = JSON.parse(
28862
- (0, import_node_fs33.readFileSync)((0, import_node_path32.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
28761
+ (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
28863
28762
  );
28864
28763
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
28865
28764
  if (versions.length === 0) return void 0;
@@ -28870,7 +28769,7 @@ function installedClaudePluginVersion() {
28870
28769
  }
28871
28770
  function manifestVersion(path2) {
28872
28771
  try {
28873
- const manifest = JSON.parse((0, import_node_fs33.readFileSync)(path2, "utf8"));
28772
+ const manifest = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
28874
28773
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
28875
28774
  } catch {
28876
28775
  return void 0;
@@ -28880,27 +28779,27 @@ function installedSurfacePluginVersion(surface) {
28880
28779
  const token = surfaceToken(surface);
28881
28780
  if (token === "kilo") {
28882
28781
  try {
28883
- const stamp = (0, import_node_fs33.readFileSync)((0, import_node_path32.join)((0, import_node_os12.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
28782
+ const stamp = (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
28884
28783
  return stamp || void 0;
28885
28784
  } catch {
28886
28785
  return void 0;
28887
28786
  }
28888
28787
  }
28889
28788
  if (token === "cursor") {
28890
- return manifestVersion((0, import_node_path32.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
28789
+ return manifestVersion((0, import_node_path33.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
28891
28790
  }
28892
28791
  if (token === "kimi") {
28893
- return manifestVersion((0, import_node_path32.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
28792
+ return manifestVersion((0, import_node_path33.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
28894
28793
  }
28895
28794
  if (token === "claude") return installedClaudePluginVersion();
28896
28795
  if (token !== "codex") return void 0;
28897
28796
  try {
28898
- const raw = process.platform === "win32" ? (0, import_node_child_process14.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
28797
+ const raw = process.platform === "win32" ? (0, import_node_child_process15.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
28899
28798
  encoding: "utf8",
28900
28799
  stdio: ["ignore", "pipe", "ignore"],
28901
28800
  timeout: 15e3,
28902
28801
  windowsHide: true
28903
- }) : (0, import_node_child_process14.execFileSync)("codex", ["plugin", "list", "--json"], {
28802
+ }) : (0, import_node_child_process15.execFileSync)("codex", ["plugin", "list", "--json"], {
28904
28803
  encoding: "utf8",
28905
28804
  stdio: ["ignore", "pipe", "ignore"],
28906
28805
  timeout: 15e3,
@@ -28918,7 +28817,7 @@ function installedActivePluginVersion(surface = detectSurface(process.env)) {
28918
28817
  }
28919
28818
  function worktreeRootSync() {
28920
28819
  try {
28921
- const out = (0, import_node_child_process14.execFileSync)("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
28820
+ const out = (0, import_node_child_process15.execFileSync)("git", ["rev-parse", "--show-toplevel"], { windowsHide: true, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
28922
28821
  let root = out.endsWith("\n") ? out.slice(0, -1) : out;
28923
28822
  if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
28924
28823
  return root || null;
@@ -28928,13 +28827,13 @@ function worktreeRootSync() {
28928
28827
  }
28929
28828
  var gitignorePath = () => {
28930
28829
  const root = worktreeRootSync();
28931
- return root === null ? null : (0, import_node_path32.join)(root, ".gitignore");
28830
+ return root === null ? null : (0, import_node_path33.join)(root, ".gitignore");
28932
28831
  };
28933
28832
  function readGitignore() {
28934
28833
  const path2 = gitignorePath();
28935
28834
  if (path2 === null) return null;
28936
28835
  try {
28937
- return (0, import_node_fs33.readFileSync)(path2, "utf8");
28836
+ return (0, import_node_fs34.readFileSync)(path2, "utf8");
28938
28837
  } catch {
28939
28838
  return null;
28940
28839
  }
@@ -28943,7 +28842,7 @@ function writeGitignore(content) {
28943
28842
  const path2 = gitignorePath();
28944
28843
  if (path2 === null) return false;
28945
28844
  try {
28946
- (0, import_node_fs33.writeFileSync)(path2, content, "utf8");
28845
+ (0, import_node_fs34.writeFileSync)(path2, content, "utf8");
28947
28846
  return true;
28948
28847
  } catch {
28949
28848
  return false;
@@ -28967,7 +28866,7 @@ async function repoRoot() {
28967
28866
  }
28968
28867
  function hasRepoLocalWorktrees() {
28969
28868
  const root = worktreeRootSync();
28970
- return root !== null && (0, import_node_fs33.existsSync)((0, import_node_path32.join)(root, ".worktrees"));
28869
+ return root !== null && (0, import_node_fs34.existsSync)((0, import_node_path33.join)(root, ".worktrees"));
28971
28870
  }
28972
28871
 
28973
28872
  // src/index.ts
@@ -29003,8 +28902,8 @@ ${r.stderr ?? ""}`).catch(() => "");
29003
28902
  function ghMultiAccountCaveat(announcedLogin) {
29004
28903
  try {
29005
28904
  const hostsPath = ghHostsConfigPath(process.env, process.platform);
29006
- if (!hostsPath || !(0, import_node_fs34.existsSync)(hostsPath)) return void 0;
29007
- return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs34.readFileSync)(hostsPath, "utf8")));
28905
+ if (!hostsPath || !(0, import_node_fs35.existsSync)(hostsPath)) return void 0;
28906
+ return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs35.readFileSync)(hostsPath, "utf8")));
29008
28907
  } catch {
29009
28908
  return void 0;
29010
28909
  }
@@ -29012,7 +28911,7 @@ function ghMultiAccountCaveat(announcedLogin) {
29012
28911
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
29013
28912
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
29014
28913
  function envHealLockPath(home) {
29015
- return (0, import_node_path33.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
28914
+ return (0, import_node_path34.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
29016
28915
  }
29017
28916
  async function withEnvHealLock(what, run) {
29018
28917
  try {
@@ -29028,9 +28927,6 @@ async function withEnvHealLock(what, run) {
29028
28927
  return { ok: false, detail: `${what} could not take the env-heal lock \u2014 ${e.message}` };
29029
28928
  }
29030
28929
  }
29031
- function docsJanitorScheduleId(repo) {
29032
- return `${repo.split("/").pop()}/docs-janitor`;
29033
- }
29034
28930
  function throttledReleasedVersion() {
29035
28931
  let note;
29036
28932
  return {
@@ -29051,8 +28947,6 @@ function throttledReleasedVersion() {
29051
28947
  }
29052
28948
  function mmiDoctorDeps(opts = {}) {
29053
28949
  const throttled = opts.throttleReleasedRead ? throttledReleasedVersion() : void 0;
29054
- let notebook;
29055
- const notebookOnce = () => notebook ??= fetchNotebook();
29056
28950
  let surfaceEvidence;
29057
28951
  let surfaceEvidenceRead = false;
29058
28952
  const surfaceEvidenceOnce = (isOrgRepo) => {
@@ -29076,11 +28970,6 @@ function mmiDoctorDeps(opts = {}) {
29076
28970
  };
29077
28971
  return surfaceEvidence;
29078
28972
  };
29079
- const docsJanitorArmedAt = async (repo) => {
29080
- const wanted = docsJanitorScheduleId(repo);
29081
- const { entries } = await notebookOnce();
29082
- return entries.find((e) => e.scheduleId === wanted)?.armedAt;
29083
- };
29084
28973
  return {
29085
28974
  githubLogin,
29086
28975
  ghInstalled,
@@ -29119,24 +29008,6 @@ function mmiDoctorDeps(opts = {}) {
29119
29008
  // writes to the harness-owned cache itself. Same plan builder the verb uses, so the two never disagree.
29120
29009
  // No `withBytes` here: doctor runs on EVERY SessionStart, and sizing means recursively stat'ing every
29121
29010
  // stale version tree. The count comes from one cheap readdir; `plugin-prune` reports the MB.
29122
- // #3025: both identities' rate pools. Personal probes with the ambient token; the App line only exists
29123
- // when this machine can mint — a vault-denied read means "not an automation host", not a failure.
29124
- githubPools: async () => {
29125
- const deps = appActorDeps();
29126
- const personal = await probeRatePools(deps.fetch, await githubToken());
29127
- let app = null;
29128
- try {
29129
- const minted = await mintInstallationToken(deps);
29130
- app = await probeRatePools(deps.fetch, minted.token) ?? "token minted but rate_limit probe failed \u2014 check network/App permissions";
29131
- } catch (e) {
29132
- if (e instanceof AppActorError) {
29133
- if (e.code !== "vault-denied") app = e.message;
29134
- } else {
29135
- app = "app actor probe failed unexpectedly \u2014 run `MMI_ACTOR=app mmi-cli pr checks-wait --help` to reproduce";
29136
- }
29137
- }
29138
- return { personal, app };
29139
- },
29140
29011
  pluginCache: () => {
29141
29012
  const surface = detectSurface(process.env);
29142
29013
  const configRoot = surfaceConfigRoot(surface);
@@ -29155,41 +29026,9 @@ function mmiDoctorDeps(opts = {}) {
29155
29026
  stagingBytes: plan.stagingBytes
29156
29027
  };
29157
29028
  },
29158
- // #3008: the schedules-notebook drift probe, compacted for the check. Full doctor only — the gather in
29159
- // runDoctorClean skips this dep entirely on fast/banner/preflight runs.
29160
- schedulesNotebook: async () => {
29161
- const { entries, incomplete, drift, reconciliation } = await notebookOnce();
29162
- const repoName = await repoSlug().catch(() => "");
29163
- const clearableHere = repoName ? reconciliation.filter((d) => driftClearableFrom(d, repoName)).length : 0;
29164
- return {
29165
- armed: entries.length,
29166
- live: entries.filter((e) => e.resolved === "live").length,
29167
- declared: entries.filter((e) => e.resolved === "declared").length,
29168
- incomplete,
29169
- drift,
29170
- clearableHere
29171
- };
29172
- },
29173
- // #3075: the docs-audit dead-man verdict probe, compacted for the check. Full doctor only, exactly like
29174
- // schedulesNotebook — a registry read the SessionStart banner / --preflight / --fast lanes never pay for.
29175
- // The 404/absent route maps to `armed:false` (an informational "not armed" line), never a false RED.
29176
- docsAudit: async () => {
29177
- const repo = await currentRepoFullName();
29178
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
29179
- const armedAt = await docsJanitorArmedAt(repo).catch(() => void 0);
29180
- const status = docsAuditStatus(await readDocsAuditFetch(repo), { repo, today, armedAt });
29181
- return { armed: status.state !== "not-armed", ok: status.ok, detail: status.line };
29182
- },
29183
- // #3471: the competing-worktrees-roots probe. Full doctor only (the gather skips it on
29184
- // fast/banner/preflight), and READ-ONLY by construction — it stats, it never removes. Nothing here is
29185
- // wired to a reaper, in either mode.
29186
- worktreeRoots: async () => worktreeRootsProbe(await repoRoot()),
29187
29029
  // #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
29188
29030
  // A local record read — cheap enough for every lane, including the banner.
29189
29031
  sessionPayload: () => readSessionPayload(process.cwd()),
29190
- // #3630: recent secret-redact crash markers from the shared hook-activity trace — with the Stop
29191
- // hook retired, the full/scheduled doctor is the trace's only reader. Local tail-scan, fail-soft.
29192
- redactorLiveness: () => redactorLivenessProbe(process.cwd()),
29193
29032
  // #3485 items 9 and 8: why the MMI plugin has never printed an "updated — please restart" notice, and
29194
29033
  // which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
29195
29034
  marketplaceRows: () => {
@@ -29198,12 +29037,25 @@ function mmiDoctorDeps(opts = {}) {
29198
29037
  const home = (0, import_node_os13.homedir)();
29199
29038
  return marketplaceRows(
29200
29039
  MMI_MARKETPLACE_NAME,
29201
- readFileSyncSafe((0, import_node_path33.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs34.readFileSync),
29202
- readFileSyncSafe((0, import_node_path33.join)(home, ".claude", "settings.json"), import_node_fs34.readFileSync)
29040
+ readFileSyncSafe((0, import_node_path34.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs35.readFileSync),
29041
+ readFileSyncSafe((0, import_node_path34.join)(home, ".claude", "settings.json"), import_node_fs35.readFileSync),
29042
+ // #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
29043
+ // edit. Unconditional — it is a fact about mmi-cli, not about the lane this run is on.
29044
+ true
29203
29045
  );
29204
29046
  } catch {
29205
29047
  return [];
29206
29048
  }
29049
+ },
29050
+ // #3974: the heal behind those rows, under --apply only. Same Codex carve-out as the rows above —
29051
+ // Codex keeps its registration in config.toml, so there is no ~/.claude registration to pin.
29052
+ healMarketplacePins: () => {
29053
+ try {
29054
+ if (detectSurface(process.env) === "codex") return void 0;
29055
+ return applyOrgMarketplacePins((0, import_node_path34.join)((0, import_node_os13.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE), [MMI_MARKETPLACE_NAME]);
29056
+ } catch {
29057
+ return void 0;
29058
+ }
29207
29059
  }
29208
29060
  };
29209
29061
  }
@@ -29381,6 +29233,7 @@ function appActorDeps() {
29381
29233
  fetch: (url, init) => fetch(url, init)
29382
29234
  };
29383
29235
  }
29236
+ setPollTokenProvider(() => pollTokenOrUndefined(appActorDeps()));
29384
29237
  function shouldMarkWorktreeActivity(commandPath3) {
29385
29238
  return commandPath3.split(" ")[0] !== "worktree";
29386
29239
  }
@@ -29405,19 +29258,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
29405
29258
  });
29406
29259
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
29407
29260
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
29408
- const path2 = (0, import_node_path33.join)(process.cwd(), ".gitignore");
29409
- const current = (0, import_node_fs34.existsSync)(path2) ? (0, import_node_fs34.readFileSync)(path2, "utf8") : null;
29261
+ const path2 = (0, import_node_path34.join)(process.cwd(), ".gitignore");
29262
+ const current = (0, import_node_fs35.existsSync)(path2) ? (0, import_node_fs35.readFileSync)(path2, "utf8") : null;
29410
29263
  const plan = planManagedGitignore(current);
29411
29264
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
29412
29265
  if (opts.json) {
29413
- if (opts.write && plan.changed) (0, import_node_fs34.writeFileSync)(path2, plan.content, "utf8");
29266
+ if (opts.write && plan.changed) (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
29414
29267
  console.log(JSON.stringify(plan, null, 2));
29415
29268
  if (!opts.write && plan.changed) process.exitCode = 1;
29416
29269
  return;
29417
29270
  }
29418
29271
  if (opts.write) {
29419
29272
  if (plan.changed) {
29420
- (0, import_node_fs34.writeFileSync)(path2, plan.content, "utf8");
29273
+ (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
29421
29274
  console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
29422
29275
  } else {
29423
29276
  console.log("mmi-cli org rules gitignore: up to date");
@@ -29572,8 +29425,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
29572
29425
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
29573
29426
  let root;
29574
29427
  if (o.root !== void 0) {
29575
- root = (0, import_node_path33.resolve)(o.root);
29576
- if (!(0, import_node_fs34.existsSync)(root) || !(0, import_node_fs34.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
29428
+ root = (0, import_node_path34.resolve)(o.root);
29429
+ if (!(0, import_node_fs35.existsSync)(root) || !(0, import_node_fs35.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
29577
29430
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
29578
29431
  if (isPathUnderDirectory(gcRepoRoot, root)) {
29579
29432
  return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
@@ -29615,7 +29468,7 @@ function runWorktreeInstall(command, cwd, quiet) {
29615
29468
  const file = isWin2 ? "cmd.exe" : bin;
29616
29469
  const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
29617
29470
  return new Promise((resolve5, reject) => {
29618
- const child2 = (0, import_node_child_process15.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
29471
+ const child2 = (0, import_node_child_process16.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
29619
29472
  const timer = setTimeout(() => {
29620
29473
  try {
29621
29474
  child2.kill();
@@ -29638,7 +29491,7 @@ async function primaryCheckoutRoot(from) {
29638
29491
  return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
29639
29492
  }
29640
29493
  async function unprovenWorktreeReason(wtPath, repoRoot2) {
29641
- if (!(0, import_node_fs34.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
29494
+ if (!(0, import_node_fs35.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
29642
29495
  const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
29643
29496
  const registered = parseWorktreePorcelainEntries(porcelain);
29644
29497
  if (!registered.length) {
@@ -29659,26 +29512,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
29659
29512
  function acquireWorktreeSetupLock(worktreeRoot) {
29660
29513
  const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
29661
29514
  const take = () => {
29662
- const fd = (0, import_node_fs34.openSync)(lockPath, "wx");
29515
+ const fd = (0, import_node_fs35.openSync)(lockPath, "wx");
29663
29516
  try {
29664
- (0, import_node_fs34.writeSync)(fd, String(Date.now()));
29517
+ (0, import_node_fs35.writeSync)(fd, String(Date.now()));
29665
29518
  } finally {
29666
- (0, import_node_fs34.closeSync)(fd);
29519
+ (0, import_node_fs35.closeSync)(fd);
29667
29520
  }
29668
29521
  return () => {
29669
29522
  try {
29670
- (0, import_node_fs34.rmSync)(lockPath, { force: true });
29523
+ (0, import_node_fs35.rmSync)(lockPath, { force: true });
29671
29524
  } catch {
29672
29525
  }
29673
29526
  };
29674
29527
  };
29675
29528
  try {
29676
- (0, import_node_fs34.mkdirSync)((0, import_node_path33.dirname)(lockPath), { recursive: true });
29529
+ (0, import_node_fs35.mkdirSync)((0, import_node_path34.dirname)(lockPath), { recursive: true });
29677
29530
  return take();
29678
29531
  } catch {
29679
29532
  try {
29680
- if (Date.now() - (0, import_node_fs34.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
29681
- (0, import_node_fs34.rmSync)(lockPath, { force: true });
29533
+ if (Date.now() - (0, import_node_fs35.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
29534
+ (0, import_node_fs35.rmSync)(lockPath, { force: true });
29682
29535
  return take();
29683
29536
  }
29684
29537
  } catch {
@@ -29901,7 +29754,7 @@ function scheduleRelatedDiscovery(o) {
29901
29754
  try {
29902
29755
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
29903
29756
  if (o.repo) args.push("--repo", o.repo);
29904
- spawnDetachedSelf(args, { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
29757
+ spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
29905
29758
  } catch {
29906
29759
  }
29907
29760
  }
@@ -29951,6 +29804,29 @@ docs.command("refs").description("deterministic doc reference gate: every backti
29951
29804
  await failGraceful(e.message);
29952
29805
  }
29953
29806
  });
29807
+ var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract \u2014 every child process must be unable to pop a console window");
29808
+ spawnCmd.command("policy").description("enforce the windowsHide contract across this repo's tracked source: a child_process call must set windowsHide, or take its options from a named constant, a type annotation, or a forwarded caller bag that does. Waive a call the scan cannot classify with a `// windows-hide-exempt: <reason>` comment on the line above it (#3979)").option("--json", "machine-readable result: { ok, scannedCount, findings[] }").action(async (o) => {
29809
+ try {
29810
+ const root = await repoRoot();
29811
+ const result = runSpawnPolicy(root);
29812
+ if (o.json) {
29813
+ consoleIo.log(JSON.stringify(result, null, 2));
29814
+ if (!result.ok) process.exitCode = 1;
29815
+ return;
29816
+ }
29817
+ if (result.ok) {
29818
+ console.log(`spawn policy: OK (${result.scannedCount} source file(s) scanned).`);
29819
+ return;
29820
+ }
29821
+ for (const f of result.findings) console.error(`spawn policy: ${f.detail}`);
29822
+ console.error(
29823
+ "\nA process spawned without windowsHide allocates a console when it has none to inherit.\nWhere Windows Terminal is the default terminal, that console becomes a desktop window the\nhost never reaps \u2014 it outlives the process as an empty frame. Set windowsHide: true, or\nroute the call through a helper that does."
29824
+ );
29825
+ process.exitCode = 1;
29826
+ } catch (e) {
29827
+ await failGraceful(e.message);
29828
+ }
29829
+ });
29954
29830
  var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
29955
29831
  tests.command("policy").description("enforce this repo's test-policy.json against the diff: a mandatory-zone change must carry a test, an unrequested new test file is refused, a `protected` test file may not be deleted or renamed away, and a `protected` entry naming a missing file is refused. Override any of them with a `Test-Policy-Override: <reason>` commit trailer (#3605)").option("--json", "machine-readable result: { ok, base, changedCount, findings[] }").option("--base <ref>", "comparison base (default: TEST_POLICY_BASE, then origin/development, then origin/main)").action(async (o) => {
29956
29832
  try {
@@ -30257,7 +30133,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
30257
30133
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
30258
30134
  if (o.secretsFile) {
30259
30135
  try {
30260
- vars.push(`secrets=${(0, import_node_fs34.readFileSync)(o.secretsFile, "utf8")}`);
30136
+ vars.push(`secrets=${(0, import_node_fs35.readFileSync)(o.secretsFile, "utf8")}`);
30261
30137
  } catch (e) {
30262
30138
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
30263
30139
  }
@@ -31005,11 +30881,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
31005
30881
  }
31006
30882
  });
31007
30883
  async function listCiWorkflowPaths(cwd = process.cwd()) {
31008
- const wfDir = (0, import_node_path33.join)(cwd, ".github", "workflows");
31009
- if (!(0, import_node_fs34.existsSync)(wfDir)) return [];
31010
- return (0, import_node_fs34.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
30884
+ const wfDir = (0, import_node_path34.join)(cwd, ".github", "workflows");
30885
+ if (!(0, import_node_fs35.existsSync)(wfDir)) return [];
30886
+ return (0, import_node_fs35.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
31011
30887
  try {
31012
- return workflowReportsPrChecks((0, import_node_fs34.readFileSync)((0, import_node_path33.join)(wfDir, name), "utf8"));
30888
+ return workflowReportsPrChecks((0, import_node_fs35.readFileSync)((0, import_node_path34.join)(wfDir, name), "utf8"));
31013
30889
  } catch {
31014
30890
  return true;
31015
30891
  }
@@ -31041,16 +30917,16 @@ function ciAuditDeps() {
31041
30917
  // gate re-seed step is skipped gracefully rather than failing mid-run.
31042
30918
  readSeedFile: (path2) => {
31043
30919
  if (!root) return null;
31044
- const fullPath = (0, import_node_path33.join)(root, path2);
31045
- return (0, import_node_fs34.existsSync)(fullPath) ? (0, import_node_fs34.readFileSync)(fullPath, "utf8") : null;
30920
+ const fullPath = (0, import_node_path34.join)(root, path2);
30921
+ return (0, import_node_fs35.existsSync)(fullPath) ? (0, import_node_fs35.readFileSync)(fullPath, "utf8") : null;
31046
30922
  }
31047
30923
  };
31048
30924
  }
31049
30925
  function hubRoot() {
31050
- const fromPkg = (0, import_node_path33.join)(__dirname, "..", "..");
30926
+ const fromPkg = (0, import_node_path34.join)(__dirname, "..", "..");
31051
30927
  const marker = "skills/bootstrap/seeds/manifest.json";
31052
- if ((0, import_node_fs34.existsSync)((0, import_node_path33.join)(fromPkg, marker))) return fromPkg;
31053
- if ((0, import_node_fs34.existsSync)((0, import_node_path33.join)(process.cwd(), marker))) return process.cwd();
30928
+ if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(fromPkg, marker))) return fromPkg;
30929
+ if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(process.cwd(), marker))) return process.cwd();
31054
30930
  return null;
31055
30931
  }
31056
30932
  pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
@@ -31353,7 +31229,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
31353
31229
  localCleanup = await cleanupPrMergeLocalBranch(headRef, {
31354
31230
  beforeWorktrees,
31355
31231
  startingPath,
31356
- pathExists: (p) => (0, import_node_fs34.existsSync)(p),
31232
+ pathExists: (p) => (0, import_node_fs35.existsSync)(p),
31357
31233
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
31358
31234
  teardownWorktreeStage,
31359
31235
  deferredStore,
@@ -31828,26 +31704,25 @@ access.command("audit").description("audit collaborator roles + train-branch pus
31828
31704
  targets = resolution.targets;
31829
31705
  }
31830
31706
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
31831
- const fileMatrix = (0, import_node_fs34.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs34.readFileSync)("access-matrix.json", "utf8")) : {};
31707
+ const fileMatrix = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
31832
31708
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
31833
31709
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
31834
- const fileContracts = (0, import_node_fs34.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs34.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
31710
+ const fileContracts = (0, import_node_fs35.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs35.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
31835
31711
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
31836
- const sanctioned = (0, import_node_fs34.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs34.readFileSync)("access-matrix.json", "utf8")) : {};
31712
+ const sanctioned = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
31837
31713
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
31838
31714
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
31839
31715
  if (!report.ok) process.exitCode = 1;
31840
31716
  });
31841
31717
  access.command("capabilities").description("enumerate your effective vault reach \u2014 every credential NAME + tier + scope you can read/use across project + org/master tiers (names only, no values) (#1615)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets((d) => secretsCapabilities(d, o)));
31842
31718
  var isWin2 = process.platform === "win32";
31843
- program2.command("doctor").description("check onboarding gates and auto-heal CLI/plugin wiring; use --verbose for the full audit checklist").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; skips slow checks and network probes").option("--preflight", "eager version/plugin-heal (env repairs only \u2014 never the repo working tree) with upfront notice when stale; silent when healthy (#1871)").option("--verbose", "print the evidence behind every check \u2014 probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output (read-only inspection \u2014 performs no repairs)").option("--apply", "perform the same auto-repairs as the interactive run (combine with --json for a machine-readable repair run)").option("--no-repo-writes", "env/plugin repairs only \u2014 never mutate the repo working tree; report pending managed .gitignore repairs with the follow-up command (for train preflights)").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nThree rows (worktree roots, schedules, docs-audit) are report-only: they still print \u2717 with their fix,\nbut never move the exit code, because their remedy is a deliberate human review or lives in the repo\nthat owns the fault. Every other red row gates, including ones only you can clear by hand (#3485).\n\n--banner keeps the legacy SessionStart contract and returns 0 unless the process crashes.\n").action(async (opts) => {
31719
+ program2.command("doctor").description("heal CLI/plugin wiring and clean up repo cruft \u2014 repairs run by default (#3975); use --verbose for the full audit checklist").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; read-only, skips slow checks and network probes").option("--preflight", "eager version/plugin-heal (env repairs only \u2014 never the repo working tree) with upfront notice when stale; silent when healthy (#1871)").option("--verbose", "print the evidence behind every check \u2014 probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output (an output format \u2014 repairs still run by lane)").option("--apply", "deprecated no-op: repairs run by default now (#3975); kept so older instructions still parse").option("--no-repo-writes", "env/plugin repairs only \u2014 never mutate the repo working tree; report pending managed .gitignore repairs with the follow-up command (for train preflights)").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nA plain run heals env drift (CLI, plugin, marketplace pins) and cleans repo cruft (gitignore block,\nmerged branches, dead worktrees, aged scratch) automatically (#3975). --no-repo-writes keeps the\nworking tree untouched for train preflights; --banner/--fast/--self are read-only lanes.\n\n--banner keeps the legacy SessionStart contract and returns 0 unless the process crashes.\n").action(async (opts) => {
31844
31720
  if (opts.guide) {
31845
31721
  consoleIo.log("MMI Agentic Onboarding: docs/Architecture/agentic-dev-environment.md");
31846
31722
  return;
31847
31723
  }
31848
31724
  process.exitCode = await runDoctorClean(
31849
31725
  {
31850
- apply: Boolean(opts.apply),
31851
31726
  repoWrites: opts.repoWrites,
31852
31727
  banner: opts.banner,
31853
31728
  preflight: opts.preflight,
@@ -31866,16 +31741,16 @@ function directoryBytes(path2) {
31866
31741
  let total = 0;
31867
31742
  let entries;
31868
31743
  try {
31869
- entries = (0, import_node_fs34.readdirSync)(path2, { withFileTypes: true });
31744
+ entries = (0, import_node_fs35.readdirSync)(path2, { withFileTypes: true });
31870
31745
  } catch {
31871
31746
  return 0;
31872
31747
  }
31873
31748
  for (const entry of entries) {
31874
- const child2 = (0, import_node_path33.join)(path2, entry.name);
31749
+ const child2 = (0, import_node_path34.join)(path2, entry.name);
31875
31750
  if (entry.isDirectory()) total += directoryBytes(child2);
31876
31751
  else {
31877
31752
  try {
31878
- total += (0, import_node_fs34.statSync)(child2).size;
31753
+ total += (0, import_node_fs35.statSync)(child2).size;
31879
31754
  } catch {
31880
31755
  }
31881
31756
  }
@@ -31883,25 +31758,25 @@ function directoryBytes(path2) {
31883
31758
  return total;
31884
31759
  }
31885
31760
  function listDirEntries(dir) {
31886
- return (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
31761
+ return (0, import_node_fs35.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
31887
31762
  }
31888
31763
  function readInstalledPluginRefs(configRoot) {
31889
31764
  const p = installedPluginsPathForConfig(configRoot);
31890
- if (!(0, import_node_fs34.existsSync)(p)) return [];
31765
+ if (!(0, import_node_fs35.existsSync)(p)) return [];
31891
31766
  try {
31892
- return installedPluginPaths((0, import_node_fs34.readFileSync)(p, "utf8"));
31767
+ return installedPluginPaths((0, import_node_fs35.readFileSync)(p, "utf8"));
31893
31768
  } catch {
31894
31769
  return null;
31895
31770
  }
31896
31771
  }
31897
31772
  function pluginCacheFsDeps(configRoot, dirBytes) {
31898
31773
  return {
31899
- exists: (p) => (0, import_node_fs34.existsSync)(p),
31900
- listVersionDirs: (root) => (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
31774
+ exists: (p) => (0, import_node_fs35.existsSync)(p),
31775
+ listVersionDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
31901
31776
  dirBytes,
31902
- listStagingDirs: (root) => (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
31777
+ listStagingDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
31903
31778
  try {
31904
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path33.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs34.statSync)(p).mtimeMs) };
31779
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path34.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs35.statSync)(p).mtimeMs) };
31905
31780
  } catch {
31906
31781
  return { name: d.name, mtimeMs: Date.now() };
31907
31782
  }
@@ -31915,10 +31790,10 @@ function stagingApplyFsGuard(configRoot) {
31915
31790
  return {
31916
31791
  referencedPaths: () => readInstalledPluginRefs(configRoot),
31917
31792
  mtimeMs: (name) => {
31918
- const p = (0, import_node_path33.join)(stagingRoot, name);
31919
- if (!(0, import_node_fs34.existsSync)(p)) return null;
31793
+ const p = (0, import_node_path34.join)(stagingRoot, name);
31794
+ if (!(0, import_node_fs35.existsSync)(p)) return null;
31920
31795
  try {
31921
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs34.statSync)(q).mtimeMs);
31796
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs35.statSync)(q).mtimeMs);
31922
31797
  } catch {
31923
31798
  return null;
31924
31799
  }
@@ -31944,7 +31819,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
31944
31819
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
31945
31820
  );
31946
31821
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
31947
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs34.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
31822
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs35.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
31948
31823
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
31949
31824
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
31950
31825
  else console.log(renderPluginCachePlan(plan, result));
@@ -32007,7 +31882,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
32007
31882
  for (const line of scratchGcLines(process.cwd())) bannerIo.log(line);
32008
31883
  const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
32009
31884
  if (worktreeBanner) {
32010
- spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process15.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
31885
+ spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
32011
31886
  bannerIo.log(worktreeBanner);
32012
31887
  }
32013
31888
  if (isLinkedWorktree(process.cwd())) {