@mutmutco/cli 4.5.41 → 4.5.43

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 +413 -189
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -5496,8 +5496,8 @@ function resolveVerbAliasShim(argv) {
5496
5496
  }
5497
5497
 
5498
5498
  // src/command-composition.ts
5499
- var import_node_os22 = require("node:os");
5500
- var import_node_path51 = require("node:path");
5499
+ var import_node_os23 = require("node:os");
5500
+ var import_node_path52 = require("node:path");
5501
5501
 
5502
5502
  // src/board-read.ts
5503
5503
  var import_node_fs13 = require("node:fs");
@@ -16871,10 +16871,10 @@ var rollout_plan_default = {
16871
16871
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
16872
16872
  },
16873
16873
  baseline: {
16874
- version: "4.5.41",
16875
- tag: "v4.5.41",
16876
- commit: "bdff9815ff09",
16877
- npm: "@mutmutco/cli@4.5.41"
16874
+ version: "4.5.43",
16875
+ tag: "v4.5.43",
16876
+ commit: "daaa2fa607cd",
16877
+ npm: "@mutmutco/cli@4.5.43"
16878
16878
  },
16879
16879
  exitCriterion: "fleet-n-of-n",
16880
16880
  hubOnlyShortcut: "forbidden",
@@ -16891,14 +16891,14 @@ var rollout_plan_default = {
16891
16891
  repo: "mutmutco/mmi-hub",
16892
16892
  role: "canary",
16893
16893
  schedule: "train",
16894
- v3Target: "v4.5.41"
16894
+ v3Target: "v4.5.43"
16895
16895
  }
16896
16896
  ],
16897
16897
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
16898
16898
  rollback: {
16899
16899
  independent: true,
16900
- mechanism: "npm dist-tag latest -> 4.5.41 and redeploy the Hub Lambda from tag v4.5.41 (bdff9815ff09); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16901
- v3Target: "v4.5.41 (@mutmutco/cli@4.5.41, tag commit bdff9815ff09 \u2014 last known-good release carrying the repo-index v4-only contract)"
16900
+ mechanism: "npm dist-tag latest -> 4.5.43 and redeploy the Hub Lambda from tag v4.5.43 (daaa2fa607cd); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16901
+ v3Target: "v4.5.43 (@mutmutco/cli@4.5.43, tag commit daaa2fa607cd \u2014 last known-good release carrying the repo-index v4-only contract)"
16902
16902
  }
16903
16903
  },
16904
16904
  {
@@ -29577,8 +29577,8 @@ function registerBoardCommands(program3) {
29577
29577
 
29578
29578
  // src/bootstrap-commands.ts
29579
29579
  var import_node_fs28 = require("node:fs");
29580
- var import_node_os16 = require("node:os");
29581
- var import_node_path27 = require("node:path");
29580
+ var import_node_os17 = require("node:os");
29581
+ var import_node_path28 = require("node:path");
29582
29582
  init_cli_shared();
29583
29583
  init_clean_exit();
29584
29584
 
@@ -29890,6 +29890,122 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
29890
29890
 
29891
29891
  // src/box-commands.ts
29892
29892
  var import_node_fs27 = require("node:fs");
29893
+ var import_node_child_process10 = require("node:child_process");
29894
+ var import_node_os16 = require("node:os");
29895
+ var import_node_path27 = require("node:path");
29896
+
29897
+ // src/box-control.ts
29898
+ var import_node_crypto8 = require("node:crypto");
29899
+ var MANIFEST_PATH = "infra/box-control/manifest.json";
29900
+ var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
29901
+ function parseManifest(text) {
29902
+ const raw = JSON.parse(text);
29903
+ if (!Array.isArray(raw.files)) throw new Error('box control manifest: "files" must be an array');
29904
+ const seen = /* @__PURE__ */ new Set();
29905
+ return raw.files.map((f, i) => {
29906
+ const where = `box control manifest: files[${i}]`;
29907
+ if (typeof f.src !== "string" || !f.src || f.src.startsWith("/") || f.src.includes("..")) throw new Error(`${where}: src must be a repo-relative path`);
29908
+ if (f.mode !== "0644" && f.mode !== "0755") throw new Error(`${where} (${f.src}): mode must be "0644" or "0755"`);
29909
+ if (f.post !== void 0 && (typeof f.post !== "string" || !ALLOWED_POST.has(f.post))) {
29910
+ throw new Error(`${where} (${f.src}): post must be one of ${[...ALLOWED_POST].join(", ")}`);
29911
+ }
29912
+ const targets = [];
29913
+ if (Array.isArray(f.targets)) targets.push(...f.targets);
29914
+ if (f.lanes !== void 0) targets.push(...expandLanes(f.lanes, where));
29915
+ if (!targets.length) throw new Error(`${where} (${f.src}): needs targets or lanes`);
29916
+ for (const t of targets) {
29917
+ if (typeof t.box !== "string" || !/^[a-z0-9-]+$/.test(t.box)) throw new Error(`${where} (${f.src}): bad box name`);
29918
+ if (typeof t.dest !== "string" || !/^\/[A-Za-z0-9._\/-]+$/.test(t.dest) || t.dest.includes("..")) {
29919
+ throw new Error(`${where} (${f.src}): dest must be an absolute path of plain characters`);
29920
+ }
29921
+ if (t.dest.startsWith("/opt/mmi-control/secrets") || t.dest.endsWith(".env")) {
29922
+ throw new Error(`${where} (${f.src}): ${t.dest} is vault-owned and cannot be a manifest destination`);
29923
+ }
29924
+ const key = `${t.box}:${t.dest}`;
29925
+ if (seen.has(key)) throw new Error(`${where} (${f.src}): duplicate destination ${key}`);
29926
+ seen.add(key);
29927
+ }
29928
+ return { src: f.src, mode: f.mode, ...f.post ? { post: f.post } : {}, targets };
29929
+ });
29930
+ }
29931
+ function expandLanes(l, where) {
29932
+ if (!l || typeof l.box !== "string" || !Number.isInteger(l.from) || !Number.isInteger(l.to) || l.from < 1 || l.to < l.from || typeof l.dest !== "string" || !l.dest.includes("{N}")) {
29933
+ throw new Error(`${where}: lanes needs box, integer from <= to, and a dest containing {N}`);
29934
+ }
29935
+ const out = [];
29936
+ for (let n = l.from; n <= l.to; n += 1) out.push({ box: l.box, dest: l.dest.replace("{N}", String(n)) });
29937
+ return out;
29938
+ }
29939
+ function filesForBox(files, box) {
29940
+ return files.flatMap((file) => file.targets.filter((t) => t.box === box).map((t) => ({ file, dest: t.dest })));
29941
+ }
29942
+ function md5(content) {
29943
+ return (0, import_node_crypto8.createHash)("md5").update(content).digest("hex");
29944
+ }
29945
+ function planRemoteScript(dests) {
29946
+ const lines2 = ["set -u"];
29947
+ for (const d of dests) {
29948
+ lines2.push(`if [ -f '${d}' ]; then printf '%s %s %s\\n' "$(md5sum < '${d}' | cut -d' ' -f1)" "$(stat -c %a '${d}')" '${d}'; else printf 'MISSING - %s\\n' '${d}'; fi`);
29949
+ }
29950
+ return `${lines2.join("\n")}
29951
+ `;
29952
+ }
29953
+ function parsePlanOutput(output, rows) {
29954
+ const seen = /* @__PURE__ */ new Map();
29955
+ for (const line of output.split("\n")) {
29956
+ const m = /^([0-9a-f]{32}|MISSING) (\d{3,4}|-) (\/\S+)$/.exec(line.trim());
29957
+ if (m) seen.set(m[3], { md5: m[1], mode: m[2] });
29958
+ }
29959
+ return rows.map(({ file, dest, expectedMd5 }) => {
29960
+ const expectedMode = file.mode.replace(/^0/, "");
29961
+ const got = seen.get(dest);
29962
+ const base = { src: file.src, dest, expectedMd5, expectedMode: file.mode };
29963
+ if (!got) return { ...base, status: "unread", installedMd5: null, installedMode: null };
29964
+ if (got.md5 === "MISSING") return { ...base, status: "missing", installedMd5: null, installedMode: null };
29965
+ const status = got.md5 !== expectedMd5 ? "drift" : got.mode !== expectedMode ? "mode-drift" : "in-sync";
29966
+ return { ...base, status, installedMd5: got.md5, installedMode: `0${got.mode}` };
29967
+ });
29968
+ }
29969
+ function selectApplyRows(rows, sel) {
29970
+ const unknown = sel.only.filter((s) => !rows.some((r) => r.file.src === s));
29971
+ if (unknown.length) return `not a manifest src for this box: ${unknown.join(", ")}`;
29972
+ const chosen = rows.filter((r) => sel.only.includes(r.file.src));
29973
+ const strayDest = sel.dest.filter((d) => !chosen.some((r) => r.dest === d));
29974
+ if (strayDest.length) return `--dest is not a destination of the chosen --only source(s): ${strayDest.join(", ")}`;
29975
+ const out = [];
29976
+ for (const src of sel.only) {
29977
+ const targets = chosen.filter((r) => r.file.src === src);
29978
+ if (targets.length === 1 || sel.allTargets) {
29979
+ out.push(...targets);
29980
+ continue;
29981
+ }
29982
+ const named = targets.filter((r) => sel.dest.includes(r.dest));
29983
+ if (!named.length) {
29984
+ return `${src} has ${targets.length} destinations on this box; name each with --dest <path>, or pass --all-targets`;
29985
+ }
29986
+ out.push(...named);
29987
+ }
29988
+ return out;
29989
+ }
29990
+ function backupPath(dest, date) {
29991
+ if (dest.startsWith("/etc/cron.d/")) return `/root/${dest.slice("/etc/cron.d/".length)}.cron.bak-${date}`;
29992
+ return `${dest}.bak-${date}`;
29993
+ }
29994
+ function applyRemoteScript(items, date) {
29995
+ const lines2 = ["set -eu", "tmp=$(mktemp)", `trap 'rm -f "$tmp"' EXIT`];
29996
+ for (const it of items) {
29997
+ const b64 = Buffer.from(it.content).toString("base64");
29998
+ lines2.push(`if [ -f '${it.dest}' ]; then cp -p '${it.dest}' '${backupPath(it.dest, date)}'; fi`);
29999
+ lines2.push(`printf '%s' '${b64}' | base64 -d > "$tmp"`);
30000
+ lines2.push(`install -D -m ${it.mode} "$tmp" '${it.dest}'`);
30001
+ }
30002
+ for (const post of [...new Set(items.map((i) => i.post).filter((p) => !!p))]) lines2.push(post);
30003
+ lines2.push(planRemoteScript(items.map((i) => i.dest)).replace(/^set -u\n/, ""));
30004
+ return `${lines2.join("\n")}
30005
+ `;
30006
+ }
30007
+
30008
+ // src/box-commands.ts
29893
30009
  init_clean_exit();
29894
30010
 
29895
30011
  // src/secrets-commands.ts
@@ -31479,6 +31595,80 @@ ${SSH_RECIPE_AGENT_NOTE}`);
31479
31595
  await failGraceful(e.message);
31480
31596
  }
31481
31597
  });
31598
+ box.command("control <name>").description("box-side control files (#7402): --plan (default, read-only) reports drift from the repo; --apply --only <src> installs with backups").option("--plan", "read-only drift report (the default)").option("--apply", "install the files named by --only on this box, backing up each previous copy").option("--only <src>", "with --apply: a manifest src to install (repeatable; approval is file by file)", (v, acc) => [...acc, v], []).option("--dest <path>", "with --apply: a destination of a multi-target --only source to install (repeatable, #7409)", (v, acc) => [...acc, v], []).option("--all-targets", "with --apply: install a multi-target --only source to every one of its destinations on this box").option("--ref <ref>", "git ref the repo content is read from", "origin/development").option("--json", "machine-readable output").action(async (name, o) => {
31599
+ try {
31600
+ if (o.apply && o.plan) throw new UsageError("box control: --plan and --apply are exclusive");
31601
+ if (o.apply && !o.only.length) throw new UsageError("box control: --apply needs --only <src> for each file to install; approval is file by file");
31602
+ if (!o.apply && (o.only.length || o.dest.length || o.allTargets)) throw new UsageError("box control: --only, --dest and --all-targets apply to --apply");
31603
+ const show = hubGitShow(o.ref);
31604
+ const files = parseManifest(show(MANIFEST_PATH).toString("utf8"));
31605
+ const { boxes, incomplete } = await fetchInventory();
31606
+ const found = lookupBox(name, boxes, incomplete);
31607
+ if (found.kind !== "found") {
31608
+ const why = found.kind === "unknown" ? `the inventory is incomplete (${found.reasons.join("; ")})` : `no box named '${name}'`;
31609
+ throw new UnreadableError(`box control: ${why}`);
31610
+ }
31611
+ const rows = filesForBox(files, found.box.name).map(({ file, dest }) => {
31612
+ const content = show(file.src);
31613
+ return { file, dest, content, expectedMd5: md5(content) };
31614
+ });
31615
+ if (!rows.length) throw new UsageError(`box control: ${MANIFEST_PATH} lists no files for ${found.box.name}`);
31616
+ let probeRows = rows;
31617
+ if (o.apply) {
31618
+ const picked = selectApplyRows(rows, { only: o.only, dest: o.dest, allTargets: !!o.allTargets });
31619
+ if (typeof picked === "string") throw new UsageError(`box control: ${picked}`);
31620
+ probeRows = picked;
31621
+ }
31622
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
31623
+ const script = o.apply ? applyRemoteScript(probeRows.map((r) => ({ dest: r.dest, mode: r.file.mode, content: r.content, post: r.file.post })), date) : planRemoteScript(probeRows.map((r) => r.dest));
31624
+ const output = runOnBox(found.box, script);
31625
+ const results = parsePlanOutput(output, probeRows);
31626
+ const drift = results.filter((r) => r.status !== "in-sync").length;
31627
+ const unread = results.some((r) => r.status === "unread");
31628
+ const payload = { box: found.box.name, ref: o.ref, mode: o.apply ? "apply" : "plan", files: results, drift, ok: drift === 0 };
31629
+ if (o.json) console.log(JSON.stringify(payload, null, 2));
31630
+ else console.log(formatControl(payload.mode, found.box.name, results));
31631
+ process.exitCode = unread ? 2 : drift ? 1 : 0;
31632
+ } catch (e) {
31633
+ if (o.json) console.log(JSON.stringify({ box: name, ref: o.ref, ok: false, error: e.message }, null, 2));
31634
+ else console.error(e.message);
31635
+ process.exitCode = 2;
31636
+ }
31637
+ });
31638
+ }
31639
+ var UsageError = class extends Error {
31640
+ };
31641
+ var UnreadableError = class extends Error {
31642
+ };
31643
+ function hubGitShow(ref) {
31644
+ const git3 = (args) => (0, import_node_child_process10.execFileSync)("git", args, { maxBuffer: 64 * 1024 * 1024, stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
31645
+ try {
31646
+ git3(["cat-file", "-e", `HEAD:${MANIFEST_PATH}`]);
31647
+ } catch {
31648
+ throw new UsageError(`box control: run this from an MMI-Hub checkout (no ${MANIFEST_PATH} here)`);
31649
+ }
31650
+ if (ref.startsWith("origin/")) git3(["fetch", "--quiet", "origin"]);
31651
+ return (path2) => git3(["show", `${ref}:${path2}`]);
31652
+ }
31653
+ function runOnBox(found, script) {
31654
+ const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path27.join)((0, import_node_os16.tmpdir)(), "mmi-box-control-"));
31655
+ try {
31656
+ const recipe = (0, import_node_path27.join)(dir, "recipe.sh");
31657
+ (0, import_node_fs27.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
31658
+ const r = (0, import_node_child_process10.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
31659
+ if (r.status !== 0) {
31660
+ throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
31661
+ }
31662
+ return r.stdout;
31663
+ } finally {
31664
+ (0, import_node_fs27.rmSync)(dir, { recursive: true, force: true });
31665
+ }
31666
+ }
31667
+ function formatControl(mode, box, rows) {
31668
+ const lines2 = rows.map((r) => `${r.status.padEnd(10)} ${r.dest} <- ${r.src}`);
31669
+ const bad = rows.filter((r) => r.status !== "in-sync").length;
31670
+ lines2.push(`${box}: ${mode} \u2014 ${rows.length} file(s), ${bad} not in sync`);
31671
+ return lines2.join("\n");
31482
31672
  }
31483
31673
 
31484
31674
  // src/bootstrap-box-ports.ts
@@ -31599,7 +31789,7 @@ function renderDeployPortDoctor(report) {
31599
31789
  }
31600
31790
 
31601
31791
  // src/bootstrap-drift.ts
31602
- var import_node_crypto8 = require("node:crypto");
31792
+ var import_node_crypto9 = require("node:crypto");
31603
31793
  function byteComparableSeeds(manifest, cls) {
31604
31794
  return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
31605
31795
  }
@@ -31619,7 +31809,7 @@ function compareSeedBytes(hubContent, repoContent) {
31619
31809
  return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
31620
31810
  }
31621
31811
  function seedContentHash(content) {
31622
- return (0, import_node_crypto8.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
31812
+ return (0, import_node_crypto9.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
31623
31813
  }
31624
31814
  function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
31625
31815
  const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
@@ -31781,7 +31971,7 @@ function renderPropagationReport(plan) {
31781
31971
  }
31782
31972
 
31783
31973
  // src/bootstrap-propagation-identity.ts
31784
- var import_node_crypto9 = require("node:crypto");
31974
+ var import_node_crypto10 = require("node:crypto");
31785
31975
  var PROPAGATION_BRANCH_PREFIX = "seed-propagate-";
31786
31976
  var TARGET_MARKER_NAME = "mmi-bootstrap-propagation-target";
31787
31977
  function safeBranchPart(value, maxLength, fallback) {
@@ -31794,7 +31984,7 @@ function repoSlug2(repo) {
31794
31984
  function propagationBranch(repo, target) {
31795
31985
  const repoPart = safeBranchPart(repoSlug2(repo), 32, "repo");
31796
31986
  const targetPart = safeBranchPart(target, 48, "target");
31797
- const hash = (0, import_node_crypto9.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
31987
+ const hash = (0, import_node_crypto10.createHash)("sha256").update(repo.trim().toLowerCase()).update("\0").update(target).digest("hex").slice(0, 12);
31798
31988
  return `${PROPAGATION_BRANCH_PREFIX}${repoPart}-${targetPart}-${hash}`;
31799
31989
  }
31800
31990
  function legacyPropagationBranch(repo) {
@@ -32950,7 +33140,7 @@ function renderBootstrapVerifyReport(report) {
32950
33140
  }
32951
33141
 
32952
33142
  // src/bootstrap-seed-delivery.ts
32953
- var import_node_crypto10 = require("node:crypto");
33143
+ var import_node_crypto11 = require("node:crypto");
32954
33144
  init_github_client();
32955
33145
  var SHA = /^[a-f0-9]{40}$/;
32956
33146
  async function readSeedFile(repo, target, ref, client) {
@@ -32983,7 +33173,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
32983
33173
  }
32984
33174
  const baseSha = base?.object?.sha;
32985
33175
  if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
32986
- const identity = (0, import_node_crypto10.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
33176
+ const identity = (0, import_node_crypto11.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
32987
33177
  return { ...planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`), baseSha, baseBranch };
32988
33178
  }
32989
33179
  async function verifyTargetedSeedBranch(repo, plan, target, client, allowMissing = false) {
@@ -33378,7 +33568,7 @@ function registerBootstrapCommands(program3) {
33378
33568
  const readFile9 = (p) => (0, import_node_fs28.existsSync)(p) ? (0, import_node_fs28.readFileSync)(p, "utf8") : null;
33379
33569
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
33380
33570
  const putSeed = async (target, content, ref, sha) => {
33381
- const tmp = (0, import_node_path27.join)((0, import_node_os16.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
33571
+ const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
33382
33572
  (0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
33383
33573
  try {
33384
33574
  await gh2(contentPutInputArgs(repo, target, tmp));
@@ -34125,7 +34315,7 @@ LIVE apply to ${repo}:
34125
34315
  } catch {
34126
34316
  existingSha = void 0;
34127
34317
  }
34128
- const tmp = (0, import_node_path27.join)((0, import_node_os16.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
34318
+ const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
34129
34319
  const desiredContent = desiredByRepo.get(rec.repo);
34130
34320
  if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
34131
34321
  (0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha)), "utf8");
@@ -34300,7 +34490,7 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
34300
34490
  } catch {
34301
34491
  existingSha = void 0;
34302
34492
  }
34303
- const tmp = (0, import_node_path27.join)((0, import_node_os16.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
34493
+ const tmp = (0, import_node_path28.join)((0, import_node_os17.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
34304
34494
  (0, import_node_fs28.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
34305
34495
  try {
34306
34496
  await gh2(contentPutInputArgs(repo, seed.target, tmp));
@@ -34586,8 +34776,8 @@ function formatSuiteStatus(report) {
34586
34776
 
34587
34777
  // src/suite-train.ts
34588
34778
  var import_node_fs29 = require("node:fs");
34589
- var import_node_path28 = require("node:path");
34590
- var import_node_crypto11 = require("node:crypto");
34779
+ var import_node_path29 = require("node:path");
34780
+ var import_node_crypto12 = require("node:crypto");
34591
34781
  var SCHEMA = "mmi-suite-cut/v1";
34592
34782
  var SHA2 = /^[a-f0-9]{40}$/i;
34593
34783
  var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
@@ -34622,8 +34812,8 @@ function readSuiteCut(path2) {
34622
34812
  return record;
34623
34813
  }
34624
34814
  function writeSuiteCut(path2, record) {
34625
- (0, import_node_fs29.mkdirSync)((0, import_node_path28.dirname)(path2), { recursive: true });
34626
- const temp = `${path2}.${(0, import_node_crypto11.randomBytes)(8).toString("hex")}.tmp`;
34815
+ (0, import_node_fs29.mkdirSync)((0, import_node_path29.dirname)(path2), { recursive: true });
34816
+ const temp = `${path2}.${(0, import_node_crypto12.randomBytes)(8).toString("hex")}.tmp`;
34627
34817
  try {
34628
34818
  (0, import_node_fs29.writeFileSync)(temp, JSON.stringify(record, null, 2), { flag: "wx" });
34629
34819
  (0, import_node_fs29.renameSync)(temp, path2);
@@ -34652,6 +34842,17 @@ async function carriedPins(deps, member, bundles) {
34652
34842
  }
34653
34843
  throw new Error(`${member.repo}: nothing new since rc, but no pinned MM-Work bundle carries development ${member.development.slice(0, 12)}; pin it before the suite cut`);
34654
34844
  }
34845
+ function suiteAbandon(recordPath) {
34846
+ const record = readSuiteCut(recordPath);
34847
+ if (!record) throw new Error("no suite cut record to abandon");
34848
+ if (record.pinPr) {
34849
+ throw new Error(`the cut already opened pin PR ${record.pinPr}; its pins may be visible, so resume it (suite rcand --owner-go) or release it (suite release) instead`);
34850
+ }
34851
+ const archive = `${recordPath}.abandoned-${record.leadDevelopment.slice(0, 12)}.json`;
34852
+ if ((0, import_node_fs29.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
34853
+ (0, import_node_fs29.renameSync)(recordPath, archive);
34854
+ return { archive, orphanTags: record.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
34855
+ }
34655
34856
  async function suiteRcand(deps, opts) {
34656
34857
  if (!opts.ownerGo) throw new Error("suite rcand requires --owner-go (rc uses live data); nothing was written");
34657
34858
  const members = suiteMembers(await deps.registry());
@@ -34669,6 +34870,10 @@ async function suiteRcand(deps, opts) {
34669
34870
  (0, import_node_fs29.renameSync)(deps.recordPath, archive);
34670
34871
  record = void 0;
34671
34872
  }
34873
+ if (record && !record.pinPr && record.members.every((m) => !m.tag)) {
34874
+ (0, import_node_fs29.rmSync)(deps.recordPath, { force: true });
34875
+ record = void 0;
34876
+ }
34672
34877
  if (!record) {
34673
34878
  const [leadDevelopment, leadPreviousRc, snapshots] = await Promise.all([
34674
34879
  deps.head(SUITE_REPO, "development"),
@@ -34813,7 +35018,7 @@ async function suiteRelease(deps, ownerGo) {
34813
35018
 
34814
35019
  // src/suite-train-io.ts
34815
35020
  var import_node_fs43 = require("node:fs");
34816
- var import_node_path42 = require("node:path");
35021
+ var import_node_path43 = require("node:path");
34817
35022
  init_cli_shared();
34818
35023
 
34819
35024
  // src/command-register-train-operations.ts
@@ -35093,9 +35298,9 @@ A hotfix branch is cut from origin/main and carries ONLY the fixes --from names,
35093
35298
  async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
35094
35299
  if (sources.length === 0) return { failures: [], skipped: [] };
35095
35300
  const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
35096
- const { tmpdir: tmpdir10 } = await import("node:os");
35097
- const { join: join44 } = await import("node:path");
35098
- const dir = await mkdtemp2(join44(tmpdir10(), "mmi-hotfix-preflight-"));
35301
+ const { tmpdir: tmpdir11 } = await import("node:os");
35302
+ const { join: join45 } = await import("node:path");
35303
+ const dir = await mkdtemp2(join45(tmpdir11(), "mmi-hotfix-preflight-"));
35099
35304
  const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
35100
35305
  const failures = [];
35101
35306
  const skipped = [];
@@ -36294,13 +36499,13 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag, workflows = H
36294
36499
  }
36295
36500
 
36296
36501
  // src/hotfix-coverage.ts
36297
- var import_node_child_process10 = require("node:child_process");
36502
+ var import_node_child_process11 = require("node:child_process");
36298
36503
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
36299
36504
  var GIT_MAX_BUFFER = 512 * 1024 * 1024;
36300
36505
  function checkHotfixCoverage(options = {}) {
36301
36506
  const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
36302
36507
  const ack = (options.ack ?? []).filter(Boolean);
36303
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process10.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
36508
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process11.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
36304
36509
  const revList = (range) => {
36305
36510
  const out = git3(["rev-list", "--no-merges", range]).trim();
36306
36511
  return out ? out.split("\n") : [];
@@ -36368,7 +36573,7 @@ function checkHotfixCoverage(options = {}) {
36368
36573
  }
36369
36574
  function checkHotfixCarries(options) {
36370
36575
  const { cwd = process.cwd(), branch, baseRef, targets } = options;
36371
- const git3 = options.git ?? ((args, opts) => (0, import_node_child_process10.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
36576
+ const git3 = options.git ?? ((args, opts) => (0, import_node_child_process11.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: GIT_MAX_BUFFER }));
36372
36577
  const isAncestor = (sha, ref) => {
36373
36578
  try {
36374
36579
  git3(["merge-base", "--is-ancestor", sha, ref]);
@@ -36401,8 +36606,8 @@ function checkHotfixCarries(options) {
36401
36606
 
36402
36607
  // src/merge-cleanup.ts
36403
36608
  var import_node_fs31 = require("node:fs");
36404
- var import_node_path30 = require("node:path");
36405
- var import_node_os17 = require("node:os");
36609
+ var import_node_path31 = require("node:path");
36610
+ var import_node_os18 = require("node:os");
36406
36611
  init_cli_shared();
36407
36612
  init_github_client();
36408
36613
 
@@ -36691,12 +36896,12 @@ async function runPrLand(prNumber, options, deps) {
36691
36896
  }
36692
36897
 
36693
36898
  // src/test-policy-core.ts
36694
- var import_node_child_process12 = require("node:child_process");
36899
+ var import_node_child_process13 = require("node:child_process");
36695
36900
  var import_node_fs30 = require("node:fs");
36696
- var import_node_path29 = require("node:path");
36901
+ var import_node_path30 = require("node:path");
36697
36902
 
36698
36903
  // src/test-command-policy-shared.mjs
36699
- var import_node_child_process11 = require("node:child_process");
36904
+ var import_node_child_process12 = require("node:child_process");
36700
36905
  var TEST_COMMAND_CLASS = "test";
36701
36906
  var TRAILER_KEY = "Test-Policy-Override";
36702
36907
  var OVERRIDE_RE = /^Test-Policy-Override:\s*(.+)$/im;
@@ -36813,7 +37018,7 @@ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true, overrid
36813
37018
  };
36814
37019
  }
36815
37020
  function git(args, cwd) {
36816
- return (0, import_node_child_process11.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37021
+ return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
36817
37022
  }
36818
37023
  function parseScope(value) {
36819
37024
  const scoped = /^\[([^\]]*)\]\s*([\s\S]*)$/.exec(value);
@@ -37169,7 +37374,7 @@ function isMeaningfulRow(file) {
37169
37374
  return file.meaningful !== false;
37170
37375
  }
37171
37376
  function loadPolicy(root, readFile9 = readFileOrNull2) {
37172
- const raw = readFile9((0, import_node_path29.join)(root, POLICY_FILE));
37377
+ const raw = readFile9((0, import_node_path30.join)(root, POLICY_FILE));
37173
37378
  if (raw == null) return { mandatory: [], declared: false };
37174
37379
  return parsePolicy(raw, POLICY_FILE);
37175
37380
  }
@@ -37275,11 +37480,11 @@ function classify(changed, policy, present = () => false) {
37275
37480
  return { mandatoryHits, untestedHits, testChanges, meaningfulTestChanges, addedTests, removedProtected };
37276
37481
  }
37277
37482
  function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs30.existsSync)(path2)) {
37278
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path29.join)(root, p)));
37483
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path30.join)(root, p)));
37279
37484
  }
37280
37485
  function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs30.existsSync)(path2)) {
37281
37486
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
37282
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path29.join)(root, p)));
37487
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path30.join)(root, p)));
37283
37488
  }
37284
37489
  function evaluate(changed, policy, present = () => false) {
37285
37490
  const { mandatoryHits, untestedHits, meaningfulTestChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -37312,14 +37517,14 @@ function evaluate(changed, policy, present = () => false) {
37312
37517
  return findings;
37313
37518
  }
37314
37519
  function git2(args, cwd) {
37315
- return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37520
+ return (0, import_node_child_process13.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
37316
37521
  }
37317
37522
  var COAUTHOR_KEY = "Co-authored-by";
37318
37523
  var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
37319
37524
  var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
37320
37525
  function parseTrailers(message2, cwd) {
37321
37526
  try {
37322
- return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
37527
+ return (0, import_node_child_process13.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
37323
37528
  windowsHide: true,
37324
37529
  cwd,
37325
37530
  input: message2,
@@ -37425,7 +37630,7 @@ function runTestPolicy(root, deps = {}) {
37425
37630
  const raw = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
37426
37631
  const changed = deps.changed ? raw : annotateChangeMeaning(raw, policy, (path2) => ({
37427
37632
  before: blobAt(base, path2, root),
37428
- after: readFileOrNull2((0, import_node_path29.join)(root, path2))
37633
+ after: readFileOrNull2((0, import_node_path30.join)(root, path2))
37429
37634
  }));
37430
37635
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride2(base, root) : { override: null, refusals: [] };
37431
37636
  const policyRefusals = policy.declared === false ? [{
@@ -37435,8 +37640,8 @@ function runTestPolicy(root, deps = {}) {
37435
37640
  A gate with no policy cannot decide which test commands are required or permitted.
37436
37641
  ` + (deps.base === HOTFIX_DIFF_BASE ? ` Hotfix lane (--base ${HOTFIX_DIFF_BASE}): pass --policy-ref <exact-${HOTFIX_POLICY_REF}-commit> so the current policy is read from ${HOTFIX_POLICY_REF}.` : ` Restore ${POLICY_FILE} on this branch; the policy file belongs on every non-hotfix lane.`)
37437
37642
  }] : [];
37438
- const present = (path2) => exists((0, import_node_path29.join)(root, path2));
37439
- const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path29.join)(root, p))) : null;
37643
+ const present = (path2) => exists((0, import_node_path30.join)(root, path2));
37644
+ const policyTree = policySource.sha ? new Set(git2(["ls-tree", "-r", "-z", "--name-only", policySource.sha], root).split("\0").filter(Boolean).map((p) => (0, import_node_path30.join)(root, p))) : null;
37440
37645
  const policyTreeExists = policyTree ? (abs) => policyTree.has(abs) : exists;
37441
37646
  const where = policySource.sha ? `${policySource.ref}@${policySource.sha}` : "this worktree";
37442
37647
  const removedByThisDiff = removedPaths(changed);
@@ -37681,8 +37886,8 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
37681
37886
  return rewritten === base ? null : rewritten;
37682
37887
  }
37683
37888
  function writeSquashBodyFile(body) {
37684
- const dir = (0, import_node_fs31.mkdtempSync)((0, import_node_path30.join)((0, import_node_os17.tmpdir)(), "mmi-squash-body-"));
37685
- const path2 = (0, import_node_path30.join)(dir, "body.txt");
37889
+ const dir = (0, import_node_fs31.mkdtempSync)((0, import_node_path31.join)((0, import_node_os18.tmpdir)(), "mmi-squash-body-"));
37890
+ const path2 = (0, import_node_path31.join)(dir, "body.txt");
37686
37891
  (0, import_node_fs31.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
37687
37892
  `, "utf8");
37688
37893
  return { path: path2, cleanup: () => {
@@ -37895,7 +38100,7 @@ async function deleteMergedRemoteBranch(options) {
37895
38100
 
37896
38101
  // src/project-info-sync.ts
37897
38102
  var import_node_fs32 = require("node:fs");
37898
- var import_node_path31 = require("node:path");
38103
+ var import_node_path32 = require("node:path");
37899
38104
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
37900
38105
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
37901
38106
  projectV2 { id }
@@ -37942,7 +38147,7 @@ function sharedName(entries, fallback) {
37942
38147
  }
37943
38148
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
37944
38149
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
37945
- const readmePath = (0, import_node_path31.join)(repoRoot2, "README.md");
38150
+ const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
37946
38151
  if (!(0, import_node_fs32.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
37947
38152
  const entries = entriesFor(project2, projects);
37948
38153
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
@@ -37968,8 +38173,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
37968
38173
  const targetBase = `https://github.com/${targetRepo2}`;
37969
38174
  const targetBranch = branchFor(targetRepo2, projects);
37970
38175
  const orgDocs = [
37971
- (0, import_node_fs32.existsSync)((0, import_node_path31.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
37972
- (0, import_node_fs32.existsSync)((0, import_node_path31.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
38176
+ (0, import_node_fs32.existsSync)((0, import_node_path32.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
38177
+ (0, import_node_fs32.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
37973
38178
  ].filter(Boolean);
37974
38179
  if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
37975
38180
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
@@ -37992,7 +38197,7 @@ async function syncProjectInfo(plan, client, apply) {
37992
38197
 
37993
38198
  // src/train-commands.ts
37994
38199
  var import_node_fs33 = require("node:fs");
37995
- var import_node_path32 = require("node:path");
38200
+ var import_node_path33 = require("node:path");
37996
38201
  init_cli_shared();
37997
38202
  init_clean_exit();
37998
38203
  init_client_version();
@@ -38007,7 +38212,7 @@ function resolveReleaseBumpIntent(raw) {
38007
38212
  }
38008
38213
  function readRepoVersion() {
38009
38214
  try {
38010
- return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path32.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
38215
+ return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path33.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
38011
38216
  } catch {
38012
38217
  return void 0;
38013
38218
  }
@@ -38118,7 +38323,7 @@ function releaseProgressVerdict(status, subject = "Release") {
38118
38323
 
38119
38324
  // src/suite-pin.ts
38120
38325
  var import_promises5 = require("node:fs/promises");
38121
- var import_node_path33 = require("node:path");
38326
+ var import_node_path34 = require("node:path");
38122
38327
  async function openSuiteCutPinPr(deps, input) {
38123
38328
  if (!/^[a-f0-9]{40}$/.test(input.cut) || !input.pins.length) throw new Error("suite pin PR needs an anchored cut and at least one module");
38124
38329
  const ids = input.pins.map((p) => p.module);
@@ -38154,15 +38359,15 @@ async function openSuiteCutPinPr(deps, input) {
38154
38359
  const row = rows.find((r) => r.id === p.module);
38155
38360
  return row?.bundles.rc === p.rc && row?.bundles.main === p.main;
38156
38361
  })) throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
38157
- await (0, import_promises5.mkdir)((0, import_node_path33.join)(deps.root, "tmp"), { recursive: true });
38158
- scratch = await (0, import_promises5.mkdtemp)((0, import_node_path33.join)(deps.root, "tmp", "suite-pin-"));
38362
+ await (0, import_promises5.mkdir)((0, import_node_path34.join)(deps.root, "tmp"), { recursive: true });
38363
+ scratch = await (0, import_promises5.mkdtemp)((0, import_node_path34.join)(deps.root, "tmp", "suite-pin-"));
38159
38364
  const run = (file, args) => deps.run(file, args, scratch);
38160
38365
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
38161
38366
  if ((await run("git", ["rev-parse", "HEAD"])).trim().toLowerCase() !== input.cut.toLowerCase())
38162
38367
  throw new Error("MM-Work development moved after the suite snapshot; no pin PR opened");
38163
38368
  await run("git", ["switch", "-c", branch]);
38164
38369
  for (const stage of ["rc", "main"]) {
38165
- const path2 = (0, import_node_path33.join)(scratch, `${stage}-pins.json`);
38370
+ const path2 = (0, import_node_path34.join)(scratch, `${stage}-pins.json`);
38166
38371
  await (0, import_promises5.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
38167
38372
  await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
38168
38373
  }
@@ -38208,8 +38413,8 @@ async function openSuitePinPr(deps, input) {
38208
38413
  url.searchParams.set("v", tag);
38209
38414
  const pin = url.toString();
38210
38415
  const branch = `suite-pin/${stage}-${member.id}-${tag}`;
38211
- await (0, import_promises5.mkdir)((0, import_node_path33.join)(deps.root, "tmp"), { recursive: true });
38212
- scratch = await (0, import_promises5.mkdtemp)((0, import_node_path33.join)(deps.root, "tmp", "suite-pin-"));
38416
+ await (0, import_promises5.mkdir)((0, import_node_path34.join)(deps.root, "tmp"), { recursive: true });
38417
+ scratch = await (0, import_promises5.mkdtemp)((0, import_node_path34.join)(deps.root, "tmp", "suite-pin-"));
38213
38418
  const run = (file, args) => deps.run(file, args, scratch);
38214
38419
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
38215
38420
  const existing = JSON.parse(await run("gh", ["pr", "list", "--repo", SUITE_REPO, "--head", branch, "--state", "all", "--json", "url,state"]));
@@ -38252,7 +38457,7 @@ async function openSuitePinPr(deps, input) {
38252
38457
  }
38253
38458
 
38254
38459
  // src/command-register-collaboration.ts
38255
- var import_node_child_process16 = require("node:child_process");
38460
+ var import_node_child_process17 = require("node:child_process");
38256
38461
  var import_node_fs42 = require("node:fs");
38257
38462
  var import_promises7 = require("node:fs/promises");
38258
38463
  init_clean_exit();
@@ -38277,11 +38482,11 @@ function spawnDetachedSelf(args, deps, opts = {}) {
38277
38482
  }
38278
38483
 
38279
38484
  // src/command-register-collaboration.ts
38280
- var import_node_path41 = require("node:path");
38485
+ var import_node_path42 = require("node:path");
38281
38486
  init_house_map();
38282
38487
 
38283
38488
  // src/issue-body.ts
38284
- var import_node_os18 = require("node:os");
38489
+ var import_node_os19 = require("node:os");
38285
38490
  init_error_codes();
38286
38491
  var TextArgError = class extends Error {
38287
38492
  constructor(message2, code, offendingFlag) {
@@ -38294,7 +38499,7 @@ var TextArgError = class extends Error {
38294
38499
  offendingFlag;
38295
38500
  };
38296
38501
  function emptyStdinMessage(fileFlag) {
38297
- if ((0, import_node_os18.platform)() === "win32") {
38502
+ if ((0, import_node_os19.platform)() === "win32") {
38298
38503
  return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
38299
38504
  }
38300
38505
  return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
@@ -38387,7 +38592,7 @@ init_github_client();
38387
38592
 
38388
38593
  // src/issue-commands.ts
38389
38594
  var import_node_fs34 = require("node:fs");
38390
- var import_node_crypto12 = require("node:crypto");
38595
+ var import_node_crypto13 = require("node:crypto");
38391
38596
  init_cli_shared();
38392
38597
  init_clean_exit();
38393
38598
  init_error_codes();
@@ -38860,7 +39065,7 @@ function rowIdempotencyKey(batchKey, spec) {
38860
39065
  const identity = `${spec.type}
38861
39066
  ${spec.title.trim()}
38862
39067
  ${spec.body ?? ""}`;
38863
- const hash = (0, import_node_crypto12.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
39068
+ const hash = (0, import_node_crypto13.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
38864
39069
  return `${batchKey}:${hash}`;
38865
39070
  }
38866
39071
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -39320,7 +39525,7 @@ ${lines2}`, {
39320
39525
 
39321
39526
  // src/post-merge-recon.ts
39322
39527
  var import_node_fs35 = require("node:fs");
39323
- var import_node_path34 = require("node:path");
39528
+ var import_node_path35 = require("node:path");
39324
39529
 
39325
39530
  // src/cross-repo-filing-issue.ts
39326
39531
  init_github_client();
@@ -39487,7 +39692,7 @@ function buildPostMergeReconRecovery(input) {
39487
39692
  }
39488
39693
  function writePostMergeReconRecovery(cwd, recovery) {
39489
39694
  const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
39490
- (0, import_node_fs35.mkdirSync)((0, import_node_path34.dirname)(path2), { recursive: true });
39695
+ (0, import_node_fs35.mkdirSync)((0, import_node_path35.dirname)(path2), { recursive: true });
39491
39696
  (0, import_node_fs35.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
39492
39697
  `, "utf8");
39493
39698
  return path2;
@@ -39922,10 +40127,10 @@ function registerPrLifecycleCommands(program3) {
39922
40127
  }
39923
40128
 
39924
40129
  // src/review-verdict.ts
39925
- var import_node_child_process13 = require("node:child_process");
40130
+ var import_node_child_process14 = require("node:child_process");
39926
40131
  var import_node_fs36 = require("node:fs");
39927
- var import_node_os19 = require("node:os");
39928
- var import_node_path35 = require("node:path");
40132
+ var import_node_os20 = require("node:os");
40133
+ var import_node_path36 = require("node:path");
39929
40134
  init_cli_shared();
39930
40135
  var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
39931
40136
  var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
@@ -40022,8 +40227,8 @@ async function checkPrReview(number, repo, head, deps = {
40022
40227
  }
40023
40228
  function computePrPatchId(number, repo) {
40024
40229
  return new Promise((resolve7, reject) => {
40025
- const gh2 = (0, import_node_child_process13.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
40026
- const git3 = (0, import_node_child_process13.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
40230
+ const gh2 = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
40231
+ const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
40027
40232
  let out = "";
40028
40233
  let ghErr = "";
40029
40234
  let gitErr = "";
@@ -40090,8 +40295,8 @@ async function readPrIssueComments(number, repo) {
40090
40295
  return comments;
40091
40296
  }
40092
40297
  async function postPrCommentFromFile(number, repo, body) {
40093
- const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path35.join)((0, import_node_os19.tmpdir)(), "mmi-review-verdict-"));
40094
- const path2 = (0, import_node_path35.join)(dir, "body.md");
40298
+ const dir = (0, import_node_fs36.mkdtempSync)((0, import_node_path36.join)((0, import_node_os20.tmpdir)(), "mmi-review-verdict-"));
40299
+ const path2 = (0, import_node_path36.join)(dir, "body.md");
40095
40300
  try {
40096
40301
  (0, import_node_fs36.writeFileSync)(path2, body, "utf8");
40097
40302
  const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
@@ -40105,7 +40310,7 @@ async function postPrCommentFromFile(number, repo, body) {
40105
40310
  }
40106
40311
 
40107
40312
  // src/pr-create-docs-check.ts
40108
- var import_node_child_process14 = require("node:child_process");
40313
+ var import_node_child_process15 = require("node:child_process");
40109
40314
  init_cli_shared();
40110
40315
  var GIT_TIMEOUT_MS2 = 15e3;
40111
40316
  function catFileBatch(root, ref, paths) {
@@ -40113,7 +40318,7 @@ function catFileBatch(root, ref, paths) {
40113
40318
  return new Promise((resolve7) => {
40114
40319
  const chunks = [];
40115
40320
  let settled = false;
40116
- const child2 = (0, import_node_child_process14.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
40321
+ const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
40117
40322
  const finish = () => {
40118
40323
  if (settled) return;
40119
40324
  settled = true;
@@ -40325,17 +40530,17 @@ async function prCreateClaimRefusal(body, repoOption, deps = {}) {
40325
40530
 
40326
40531
  // src/worktree-residue-sweep.ts
40327
40532
  var import_node_fs41 = require("node:fs");
40328
- var import_node_path40 = require("node:path");
40533
+ var import_node_path41 = require("node:path");
40329
40534
 
40330
40535
  // src/worktree-merge-cleanup.ts
40331
40536
  var import_node_fs40 = require("node:fs");
40332
- var import_node_path39 = require("node:path");
40537
+ var import_node_path40 = require("node:path");
40333
40538
  init_cli_shared();
40334
40539
 
40335
40540
  // src/stage-runner.ts
40336
- var import_node_child_process15 = require("node:child_process");
40541
+ var import_node_child_process16 = require("node:child_process");
40337
40542
  var import_node_fs38 = require("node:fs");
40338
- var import_node_path37 = require("node:path");
40543
+ var import_node_path38 = require("node:path");
40339
40544
  var import_node_net = require("node:net");
40340
40545
  var import_node_util5 = require("node:util");
40341
40546
 
@@ -40346,7 +40551,7 @@ function normalizeEol2(s) {
40346
40551
 
40347
40552
  // src/env-detection.ts
40348
40553
  var import_node_fs37 = require("node:fs");
40349
- var import_node_path36 = require("node:path");
40554
+ var import_node_path37 = require("node:path");
40350
40555
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
40351
40556
  function isEnvBaseName(name) {
40352
40557
  return name.toLowerCase() === ".env";
@@ -40364,13 +40569,13 @@ function findEnvFiles(root) {
40364
40569
  const relPath = rel ? `${rel}/${entry.name}` : entry.name;
40365
40570
  if (entry.isDirectory()) {
40366
40571
  if (SKIPPED_DIRS.has(entry.name)) continue;
40367
- walk2((0, import_node_path36.join)(dir, entry.name), relPath);
40572
+ walk2((0, import_node_path37.join)(dir, entry.name), relPath);
40368
40573
  continue;
40369
40574
  }
40370
40575
  if (isEnvBaseName(entry.name)) {
40371
40576
  let isDir = false;
40372
40577
  try {
40373
- isDir = (0, import_node_fs37.lstatSync)((0, import_node_path36.join)(dir, entry.name)).isDirectory();
40578
+ isDir = (0, import_node_fs37.lstatSync)((0, import_node_path37.join)(dir, entry.name)).isDirectory();
40374
40579
  } catch {
40375
40580
  }
40376
40581
  if (!isDir) found.push(relPath);
@@ -40427,15 +40632,15 @@ function stageComposeFileEnv(files) {
40427
40632
  }
40428
40633
 
40429
40634
  // src/stage-runner.ts
40430
- var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process15.execFile);
40635
+ var execFileP3 = (0, import_node_util5.promisify)(import_node_child_process16.execFile);
40431
40636
  var DOCKER_TIMEOUT_MS = 15e3;
40432
40637
  function stageBash() {
40433
40638
  if (process.platform !== "win32") return "bash";
40434
40639
  const configured = process.env.SHELL;
40435
- if (configured && (0, import_node_path37.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs38.existsSync)(configured)) return configured;
40640
+ if (configured && (0, import_node_path38.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs38.existsSync)(configured)) return configured;
40436
40641
  const candidates = [
40437
- (0, import_node_path37.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
40438
- ...process.env.LOCALAPPDATA ? [(0, import_node_path37.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
40642
+ (0, import_node_path38.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
40643
+ ...process.env.LOCALAPPDATA ? [(0, import_node_path38.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
40439
40644
  ];
40440
40645
  const bash = candidates.find((path2) => (0, import_node_fs38.existsSync)(path2));
40441
40646
  if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
@@ -40568,7 +40773,7 @@ function appendForceRecreate(up) {
40568
40773
  return `${up.trimEnd()} --force-recreate`;
40569
40774
  }
40570
40775
  function stageStatePath(cwd = process.cwd()) {
40571
- return (0, import_node_path37.join)(cwd, "tmp", "stage", "state.json");
40776
+ return (0, import_node_path38.join)(cwd, "tmp", "stage", "state.json");
40572
40777
  }
40573
40778
  function normPath(path2) {
40574
40779
  return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -40785,7 +40990,7 @@ function stageProcessEnv(stagePort, extraEnv) {
40785
40990
  }
40786
40991
  function composeResolvesPort(cwd) {
40787
40992
  if (process.env.PORT) return true;
40788
- const envFile = (0, import_node_path37.join)(cwd, ".env");
40993
+ const envFile = (0, import_node_path38.join)(cwd, ".env");
40789
40994
  return (0, import_node_fs38.existsSync)(envFile) && envFileKeys((0, import_node_fs38.readFileSync)(envFile, "utf8")).has("PORT");
40790
40995
  }
40791
40996
  function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
@@ -40799,13 +41004,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
40799
41004
  function stageComposeFiles(cwd) {
40800
41005
  const files = ["docker-compose.yml"];
40801
41006
  for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
40802
- if ((0, import_node_fs38.existsSync)((0, import_node_path37.join)(cwd, conventional))) files.push(conventional);
41007
+ if ((0, import_node_fs38.existsSync)((0, import_node_path38.join)(cwd, conventional))) files.push(conventional);
40803
41008
  }
40804
41009
  return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
40805
41010
  }
40806
41011
  async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
40807
41012
  const keys = Object.keys(vaultEnvMerge ?? {});
40808
- if (!keys.length || !(0, import_node_fs38.existsSync)((0, import_node_path37.join)(cwd, "docker-compose.yml"))) return {};
41013
+ if (!keys.length || !(0, import_node_fs38.existsSync)((0, import_node_path38.join)(cwd, "docker-compose.yml"))) return {};
40809
41014
  let configJson;
40810
41015
  try {
40811
41016
  const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
@@ -40821,15 +41026,15 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
40821
41026
  }
40822
41027
  const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys);
40823
41028
  if (!body) return {};
40824
- const overridePath = (0, import_node_path37.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
40825
- (0, import_node_fs38.mkdirSync)((0, import_node_path37.join)(cwd, "tmp", "stage"), { recursive: true });
41029
+ const overridePath = (0, import_node_path38.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
41030
+ (0, import_node_fs38.mkdirSync)((0, import_node_path38.join)(cwd, "tmp", "stage"), { recursive: true });
40826
41031
  (0, import_node_fs38.writeFileSync)(overridePath, body, "utf8");
40827
41032
  return stageComposeFileEnv(stageComposeFiles(cwd));
40828
41033
  }
40829
41034
  async function ensureStageRuntimeEnv(config, opts, cwd) {
40830
41035
  if (!config.ensureEnv) return;
40831
- const target = (0, import_node_path37.join)(cwd, config.ensureEnv.target);
40832
- const example = (0, import_node_path37.join)(cwd, config.ensureEnv.example);
41036
+ const target = (0, import_node_path38.join)(cwd, config.ensureEnv.target);
41037
+ const example = (0, import_node_path38.join)(cwd, config.ensureEnv.example);
40833
41038
  if (!(0, import_node_fs38.existsSync)(target) && (0, import_node_fs38.existsSync)(example)) {
40834
41039
  (0, import_node_fs38.copyFileSync)(example, target);
40835
41040
  } else if ((0, import_node_fs38.existsSync)(target) && (0, import_node_fs38.existsSync)(example)) {
@@ -41010,7 +41215,7 @@ async function startStage(config = {}, opts = {}) {
41010
41215
  let up = sub(config.up.trim());
41011
41216
  if (opts.forceRecreate) up = appendForceRecreate(up);
41012
41217
  const identity = await resolveStageIdentity(cwd);
41013
- const child2 = (0, import_node_child_process15.spawn)(bash, ["-c", up], {
41218
+ const child2 = (0, import_node_child_process16.spawn)(bash, ["-c", up], {
41014
41219
  cwd,
41015
41220
  // POSIX-only: the process group exists for the group-kill in stopStage. On win32 teardown is
41016
41221
  // `taskkill /T /F` (no group needed), and detached+shell defeats windowsHide — every spawn would
@@ -41120,7 +41325,7 @@ async function runStage(config = {}, opts = {}) {
41120
41325
 
41121
41326
  // src/worktree-evidence-archive.ts
41122
41327
  var import_node_fs39 = require("node:fs");
41123
- var import_node_path38 = require("node:path");
41328
+ var import_node_path39 = require("node:path");
41124
41329
  var JERV_ARTIFACT_RUN_SCOPE_ENV_VARS = ["JERV_RUN_ID", ...SESSION_ID_ENV_VARS];
41125
41330
  function sanitizeArchiveSegment(value, max = 80) {
41126
41331
  const scrubbed = value.replace(/[^A-Za-z0-9._@+-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -41152,7 +41357,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
41152
41357
  const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
41153
41358
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
41154
41359
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
41155
- const source = (0, import_node_path38.join)(args.worktreePath, ".jerv");
41360
+ const source = (0, import_node_path39.join)(args.worktreePath, ".jerv");
41156
41361
  if (!exists(source)) return { status: "absent" };
41157
41362
  if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
41158
41363
  const runScope = resolveArtifactRunScope(env);
@@ -41160,7 +41365,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
41160
41365
  const stamp = now().toISOString().replace(/[:.]/g, "-");
41161
41366
  const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
41162
41367
  try {
41163
- mkdirp((0, import_node_path38.dirname)(dest));
41368
+ mkdirp((0, import_node_path39.dirname)(dest));
41164
41369
  copyDir(source, dest);
41165
41370
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
41166
41371
  return { status: "archived", path: dest, runScope };
@@ -41176,7 +41381,7 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
41176
41381
  const exists = deps.exists ?? import_node_fs39.existsSync;
41177
41382
  const stat4 = deps.stat ?? defaultStat;
41178
41383
  const readdir2 = deps.readdir ?? import_node_fs39.readdirSync;
41179
- const tmpRoot = (0, import_node_path38.join)(worktreePath, "tmp");
41384
+ const tmpRoot = (0, import_node_path39.join)(worktreePath, "tmp");
41180
41385
  if (!exists(tmpRoot)) return [];
41181
41386
  const entries = [];
41182
41387
  const walk2 = (dir) => {
@@ -41187,14 +41392,14 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
41187
41392
  return;
41188
41393
  }
41189
41394
  for (const name of names) {
41190
- const full = (0, import_node_path38.join)(dir, name);
41395
+ const full = (0, import_node_path39.join)(dir, name);
41191
41396
  let st;
41192
41397
  try {
41193
41398
  st = stat4(full);
41194
41399
  } catch {
41195
41400
  continue;
41196
41401
  }
41197
- const relPath = (0, import_node_path38.relative)(worktreePath, full).replace(/\\/g, "/");
41402
+ const relPath = (0, import_node_path39.relative)(worktreePath, full).replace(/\\/g, "/");
41198
41403
  if (st.isDirectory()) {
41199
41404
  if (st.mtimeMs > newerThanMs) entries.push({ relPath, bytes: 0, mtimeMs: st.mtimeMs });
41200
41405
  walk2(full);
@@ -41219,7 +41424,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
41219
41424
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
41220
41425
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
41221
41426
  const scanned = scanWorktreeTmpEvidence(args.worktreePath, args.newerThanMs, deps);
41222
- const source = (0, import_node_path38.join)(args.worktreePath, "tmp");
41427
+ const source = (0, import_node_path39.join)(args.worktreePath, "tmp");
41223
41428
  if (!scanned.length) return { status: "absent" };
41224
41429
  if (!exists(source)) return { status: "absent" };
41225
41430
  const runScope = resolveArtifactRunScope(env);
@@ -41227,7 +41432,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
41227
41432
  const stamp = now().toISOString().replace(/[:.]/g, "-");
41228
41433
  const dest = resolveRoot(args.primaryRoot, "worktree-artifacts", runScope, branchSlug, stamp, "tmp");
41229
41434
  try {
41230
- mkdirp((0, import_node_path38.dirname)(dest));
41435
+ mkdirp((0, import_node_path39.dirname)(dest));
41231
41436
  copyDir(source, dest);
41232
41437
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
41233
41438
  const bytes = scanned.reduce((sum, e) => sum + e.bytes, 0);
@@ -41296,7 +41501,7 @@ function normPath2(p) {
41296
41501
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
41297
41502
  }
41298
41503
  function unlinkNodeModulesJunction(wtPath) {
41299
- const nm = (0, import_node_path39.join)(wtPath, "node_modules");
41504
+ const nm = (0, import_node_path40.join)(wtPath, "node_modules");
41300
41505
  try {
41301
41506
  if ((0, import_node_fs40.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs40.rmdirSync)(nm);
41302
41507
  return { ok: true };
@@ -41433,7 +41638,7 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
41433
41638
  }
41434
41639
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
41435
41640
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
41436
- return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path39.join)(wtPath, line.slice(0, -1)));
41641
+ return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path40.join)(wtPath, line.slice(0, -1)));
41437
41642
  }
41438
41643
  function safeRemoveTree(path2) {
41439
41644
  const stat4 = (0, import_node_fs40.lstatSync)(path2);
@@ -41446,7 +41651,7 @@ function safeRemoveTree(path2) {
41446
41651
  return;
41447
41652
  }
41448
41653
  if (stat4.isDirectory()) {
41449
- for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0, import_node_path39.join)(path2, entry));
41654
+ for (const entry of (0, import_node_fs40.readdirSync)(path2)) safeRemoveTree((0, import_node_path40.join)(path2, entry));
41450
41655
  (0, import_node_fs40.rmdirSync)(path2);
41451
41656
  return;
41452
41657
  }
@@ -41454,7 +41659,7 @@ function safeRemoveTree(path2) {
41454
41659
  }
41455
41660
  async function removeWorktreeNodeModules(wtPath) {
41456
41661
  try {
41457
- safeRemoveTree((0, import_node_path39.join)(wtPath, "node_modules"));
41662
+ safeRemoveTree((0, import_node_path40.join)(wtPath, "node_modules"));
41458
41663
  return { ok: true };
41459
41664
  } catch (e) {
41460
41665
  if (e.code === "ENOENT") return { ok: true };
@@ -41498,7 +41703,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
41498
41703
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
41499
41704
  }
41500
41705
  for (const entry of entries) {
41501
- const child2 = (0, import_node_path39.join)(dir, entry.name);
41706
+ const child2 = (0, import_node_path40.join)(dir, entry.name);
41502
41707
  if (entry.isSymbolicLink()) {
41503
41708
  let target = "";
41504
41709
  try {
@@ -41825,7 +42030,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
41825
42030
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
41826
42031
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
41827
42032
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
41828
- if (pathExists((0, import_node_path39.join)(wtPath, "node_modules"))) {
42033
+ if (pathExists((0, import_node_path40.join)(wtPath, "node_modules"))) {
41829
42034
  const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
41830
42035
  if (!nmRemoved.ok) {
41831
42036
  report.worktree = {
@@ -41965,7 +42170,7 @@ function norm(p) {
41965
42170
  return process.platform === "win32" ? s.toLowerCase() : s;
41966
42171
  }
41967
42172
  function residueLedgerPath(gitCommonDir) {
41968
- return (0, import_node_path40.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
42173
+ return (0, import_node_path41.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
41969
42174
  }
41970
42175
  function readPaths(file, deps) {
41971
42176
  try {
@@ -41978,7 +42183,7 @@ function readPaths(file, deps) {
41978
42183
  function writePaths(file, paths, deps) {
41979
42184
  try {
41980
42185
  const write = deps.writeLedger ?? ((f, t) => {
41981
- (0, import_node_fs41.mkdirSync)((0, import_node_path40.dirname)(f), { recursive: true });
42186
+ (0, import_node_fs41.mkdirSync)((0, import_node_path41.dirname)(f), { recursive: true });
41982
42187
  (0, import_node_fs41.writeFileSync)(f, t, "utf8");
41983
42188
  });
41984
42189
  write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
@@ -42029,7 +42234,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
42029
42234
  }
42030
42235
  if (recorded.length) writePaths(file, remaining, deps);
42031
42236
  const roots = /* @__PURE__ */ new Set();
42032
- for (const p of [...registered.slice(1), ...recorded]) roots.add((0, import_node_path40.dirname)(p));
42237
+ for (const p of [...registered.slice(1), ...recorded]) roots.add((0, import_node_path41.dirname)(p));
42033
42238
  const keptKeys = new Set(remaining.map(norm));
42034
42239
  for (const root of roots) {
42035
42240
  if (!isDir(root) || live.has(norm(root)) || norm(root) === primary) continue;
@@ -42040,7 +42245,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
42040
42245
  continue;
42041
42246
  }
42042
42247
  for (const name of children) {
42043
- const child2 = (0, import_node_path40.join)(root, name);
42248
+ const child2 = (0, import_node_path41.join)(root, name);
42044
42249
  const key = norm(child2);
42045
42250
  if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
42046
42251
  let entries;
@@ -42108,10 +42313,10 @@ function argvWantsJson2() {
42108
42313
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
42109
42314
  }
42110
42315
  function hubRoot() {
42111
- const fromPkg = (0, import_node_path41.join)(__dirname, "..", "..");
42316
+ const fromPkg = (0, import_node_path42.join)(__dirname, "..", "..");
42112
42317
  const marker = "skills/bootstrap/seeds/manifest.json";
42113
- if ((0, import_node_fs42.existsSync)((0, import_node_path41.join)(fromPkg, marker))) return fromPkg;
42114
- if ((0, import_node_fs42.existsSync)((0, import_node_path41.join)(process.cwd(), marker))) return process.cwd();
42318
+ if ((0, import_node_fs42.existsSync)((0, import_node_path42.join)(fromPkg, marker))) return fromPkg;
42319
+ if ((0, import_node_fs42.existsSync)((0, import_node_path42.join)(process.cwd(), marker))) return process.cwd();
42115
42320
  return null;
42116
42321
  }
42117
42322
  function ciAuditDeps() {
@@ -42123,7 +42328,7 @@ function ciAuditDeps() {
42123
42328
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
42124
42329
  readSeedFile: (path2) => {
42125
42330
  if (!root) return null;
42126
- const fullPath = (0, import_node_path41.join)(root, path2);
42331
+ const fullPath = (0, import_node_path42.join)(root, path2);
42127
42332
  return (0, import_node_fs42.existsSync)(fullPath) ? (0, import_node_fs42.readFileSync)(fullPath, "utf8") : null;
42128
42333
  }
42129
42334
  };
@@ -42231,7 +42436,7 @@ function scheduleRelatedDiscovery(o) {
42231
42436
  try {
42232
42437
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
42233
42438
  if (o.repo) args.push("--repo", o.repo);
42234
- spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
42439
+ spawnDetachedSelf(args, { spawn: import_node_child_process17.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
42235
42440
  } catch {
42236
42441
  }
42237
42442
  }
@@ -42882,11 +43087,11 @@ ${list}`);
42882
43087
  }
42883
43088
  });
42884
43089
  async function listCiWorkflowPaths(cwd = process.cwd()) {
42885
- const wfDir = (0, import_node_path41.join)(cwd, ".github", "workflows");
43090
+ const wfDir = (0, import_node_path42.join)(cwd, ".github", "workflows");
42886
43091
  if (!(0, import_node_fs42.existsSync)(wfDir)) return [];
42887
43092
  return (0, import_node_fs42.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
42888
43093
  try {
42889
- return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path41.join)(wfDir, name), "utf8"));
43094
+ return workflowReportsPrChecks((0, import_node_fs42.readFileSync)((0, import_node_path42.join)(wfDir, name), "utf8"));
42890
43095
  } catch {
42891
43096
  return true;
42892
43097
  }
@@ -43391,7 +43596,7 @@ ${list}`);
43391
43596
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
43392
43597
  let foreignCheckout;
43393
43598
  if (foreignCwd) {
43394
- const sibling = (0, import_node_path41.join)((0, import_node_path41.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
43599
+ const sibling = (0, import_node_path42.join)((0, import_node_path42.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
43395
43600
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
43396
43601
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
43397
43602
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -43600,7 +43805,7 @@ ${list}`);
43600
43805
  let residueSweep;
43601
43806
  if (!(foreignCwd && !foreignCheckout)) {
43602
43807
  try {
43603
- gitCommonDir = (0, import_node_path41.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
43808
+ gitCommonDir = (0, import_node_path42.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
43604
43809
  residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
43605
43810
  } catch {
43606
43811
  }
@@ -44694,15 +44899,15 @@ function rcRunCauseFromLog(log) {
44694
44899
  }
44695
44900
  function suiteTrainIo(root, summaryFile) {
44696
44901
  const recordPath = repoRuntimeStatePath(root, "suite", "cut.json");
44697
- const base = (0, import_node_path42.join)(repoRuntimeStatePath(root, "suite"), "checkouts");
44902
+ const base = (0, import_node_path43.join)(repoRuntimeStatePath(root, "suite"), "checkouts");
44698
44903
  const checkout = async (repo) => {
44699
44904
  if (repo === SUITE_REPO) return root;
44700
- const target = (0, import_node_path42.join)(base, repo.split("/")[1]);
44905
+ const target = (0, import_node_path43.join)(base, repo.split("/")[1]);
44701
44906
  if (!(0, import_node_fs43.existsSync)(target)) {
44702
44907
  (0, import_node_fs43.mkdirSync)(base, { recursive: true });
44703
44908
  await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
44704
44909
  }
44705
- if (!(0, import_node_fs43.existsSync)((0, import_node_path42.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
44910
+ if (!(0, import_node_fs43.existsSync)((0, import_node_path43.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
44706
44911
  const origin = await exec("git", ["remote", "get-url", "origin"], target);
44707
44912
  if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
44708
44913
  throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
@@ -44930,6 +45135,25 @@ function registerSuiteCommands(program3) {
44930
45135
  return fail(`suite rcand: ${e.message}`);
44931
45136
  }
44932
45137
  });
45138
+ suite.command("abandon").description("retire an unfinished cut that opened no pin PR: archive the record; member rc tags stay (#7396)").option("--json", "structured receipt").action(async (o) => {
45139
+ try {
45140
+ const root = await leadRoot();
45141
+ const unlock = await acquireTrainWriterLock(root, "suite abandon");
45142
+ let result;
45143
+ try {
45144
+ result = suiteAbandon(suiteTrainIo(root).recordPath);
45145
+ } finally {
45146
+ await unlock();
45147
+ }
45148
+ console.log(o.json ? JSON.stringify(result, null, 2) : [
45149
+ `suite abandon: archived the cut record to ${result.archive}`,
45150
+ ...result.orphanTags.length ? [" member rc tags left in place (immutable; the next rc supersedes them):", ...result.orphanTags.map((t) => ` - ${t}`)] : [" no member rc tag was minted"],
45151
+ " the next suite rcand --owner-go re-snapshots every member"
45152
+ ].join("\n"));
45153
+ } catch (e) {
45154
+ return fail(`suite abandon: ${e.message}`);
45155
+ }
45156
+ });
44933
45157
  suite.command("release").description("verify the cut and unchanged live backends, ship members then MM-Work, announce once").option("--owner-go", "explicit owner approval to release the tested candidate").option("--announce-summary-file <path>", "curated suite announcement, posted once after the train").option("--json", "structured receipt").action(async (o) => {
44934
45158
  try {
44935
45159
  if (!o.ownerGo) throw new Error("suite release requires --owner-go; nothing was written");
@@ -45072,8 +45296,8 @@ async function collectWaveStatus(deps) {
45072
45296
  init_github_client();
45073
45297
  init_cli_shared();
45074
45298
  var import_node_fs46 = require("node:fs");
45075
- var import_node_os20 = require("node:os");
45076
- var import_node_path43 = require("node:path");
45299
+ var import_node_os21 = require("node:os");
45300
+ var import_node_path44 = require("node:path");
45077
45301
  var GC_GH_TIMEOUT_MS3 = 2e4;
45078
45302
  async function collectStatus() {
45079
45303
  const repo = await resolveRepo();
@@ -45262,10 +45486,10 @@ async function collectOnboardStatus(opts = {}) {
45262
45486
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
45263
45487
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
45264
45488
  }
45265
- const home = (0, import_node_os20.homedir)();
45489
+ const home = (0, import_node_os21.homedir)();
45266
45490
  const plugin = onboardPluginGate({
45267
- readKnown: () => readFileSyncSafe((0, import_node_path43.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs46.readFileSync),
45268
- readSettings: () => readFileSyncSafe((0, import_node_path43.join)(home, ".claude", "settings.json"), import_node_fs46.readFileSync)
45491
+ readKnown: () => readFileSyncSafe((0, import_node_path44.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs46.readFileSync),
45492
+ readSettings: () => readFileSyncSafe((0, import_node_path44.join)(home, ".claude", "settings.json"), import_node_fs46.readFileSync)
45269
45493
  });
45270
45494
  return {
45271
45495
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -48379,7 +48603,7 @@ function registerSessionReport(program3) {
48379
48603
 
48380
48604
  // src/stage-commands.ts
48381
48605
  var import_node_fs47 = require("node:fs");
48382
- var import_node_path44 = require("node:path");
48606
+ var import_node_path45 = require("node:path");
48383
48607
  init_cli_shared();
48384
48608
  init_clean_exit();
48385
48609
 
@@ -48625,8 +48849,8 @@ function registerStageCommands(program3) {
48625
48849
  return {
48626
48850
  resolution: decideStage({
48627
48851
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
48628
- hasCompose: (0, import_node_fs47.existsSync)((0, import_node_path44.join)(process.cwd(), "docker-compose.yml")),
48629
- hasEnvExample: (0, import_node_fs47.existsSync)((0, import_node_path44.join)(process.cwd(), ".env.example")),
48852
+ hasCompose: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), "docker-compose.yml")),
48853
+ hasEnvExample: (0, import_node_fs47.existsSync)((0, import_node_path45.join)(process.cwd(), ".env.example")),
48630
48854
  repo
48631
48855
  }),
48632
48856
  project: project2,
@@ -48910,20 +49134,20 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
48910
49134
  }
48911
49135
 
48912
49136
  // src/tenant-artifact.ts
48913
- var import_node_crypto13 = require("node:crypto");
49137
+ var import_node_crypto14 = require("node:crypto");
48914
49138
  var import_node_fs48 = require("node:fs");
48915
49139
  var import_promises10 = require("node:fs/promises");
48916
- var import_node_path45 = require("node:path");
49140
+ var import_node_path46 = require("node:path");
48917
49141
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
48918
49142
  var MAX_BYTES = 5 * 1024 * 1024 * 1024;
48919
49143
  async function sha256File(path2) {
48920
- const hash = (0, import_node_crypto13.createHash)("sha256");
49144
+ const hash = (0, import_node_crypto14.createHash)("sha256");
48921
49145
  for await (const chunk of (0, import_node_fs48.createReadStream)(path2)) hash.update(chunk);
48922
49146
  return hash.digest("hex");
48923
49147
  }
48924
49148
  async function putTenantArtifact(repo, stage, inputPath, deps) {
48925
49149
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
48926
- const path2 = (0, import_node_path45.resolve)(inputPath);
49150
+ const path2 = (0, import_node_path46.resolve)(inputPath);
48927
49151
  const info = await (0, import_promises10.stat)(path2);
48928
49152
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
48929
49153
  if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
@@ -49113,7 +49337,7 @@ async function resolveWhoami(deps) {
49113
49337
  }
49114
49338
 
49115
49339
  // src/command-register-developer.ts
49116
- var import_node_path50 = require("node:path");
49340
+ var import_node_path51 = require("node:path");
49117
49341
  init_house_map();
49118
49342
  init_hub_url();
49119
49343
 
@@ -49146,27 +49370,27 @@ async function executeWaveLand(plan, deps) {
49146
49370
  }
49147
49371
 
49148
49372
  // src/dist-drift.ts
49149
- var import_node_child_process17 = require("node:child_process");
49150
- var import_node_crypto15 = require("node:crypto");
49373
+ var import_node_child_process18 = require("node:child_process");
49374
+ var import_node_crypto16 = require("node:crypto");
49151
49375
  var import_node_fs50 = require("node:fs");
49152
- var import_node_os21 = require("node:os");
49153
- var import_node_path47 = require("node:path");
49376
+ var import_node_os22 = require("node:os");
49377
+ var import_node_path48 = require("node:path");
49154
49378
 
49155
49379
  // ../scripts/distribution-digest.mjs
49156
- var import_node_crypto14 = require("node:crypto");
49380
+ var import_node_crypto15 = require("node:crypto");
49157
49381
  var import_node_fs49 = require("node:fs");
49158
- var import_node_path46 = require("node:path");
49382
+ var import_node_path47 = require("node:path");
49159
49383
  var slash = (value) => value.replaceAll("\\", "/");
49160
49384
  function repoPath(root, declaredPath, label) {
49161
- const absoluteRoot = (0, import_node_path46.resolve)(root);
49162
- const target = (0, import_node_path46.resolve)(root, declaredPath);
49163
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path46.sep}`)) {
49385
+ const absoluteRoot = (0, import_node_path47.resolve)(root);
49386
+ const target = (0, import_node_path47.resolve)(root, declaredPath);
49387
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path47.sep}`)) {
49164
49388
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
49165
49389
  }
49166
49390
  return target;
49167
49391
  }
49168
49392
  function digestFiles(files) {
49169
- const hash = (0, import_node_crypto14.createHash)("sha256");
49393
+ const hash = (0, import_node_crypto15.createHash)("sha256");
49170
49394
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
49171
49395
  const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs49.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs49.readFileSync)(file.absolute);
49172
49396
  hash.update(file.relative, "utf8");
@@ -49196,7 +49420,7 @@ var DIST_ARTIFACTS = [
49196
49420
  ];
49197
49421
  var BOM_DIST_TREE_ID = "mmi-cli-dist";
49198
49422
  var ABSENT = "absent";
49199
- var sha256 = (bytes) => `sha256:${(0, import_node_crypto15.createHash)("sha256").update(bytes).digest("hex")}`;
49423
+ var sha256 = (bytes) => `sha256:${(0, import_node_crypto16.createHash)("sha256").update(bytes).digest("hex")}`;
49200
49424
  function artifactDrift(path2, committedBytes, rebuiltBytes) {
49201
49425
  const committed = committedBytes ? sha256(committedBytes) : ABSENT;
49202
49426
  const rebuiltExpected = rebuiltBytes ? sha256(rebuiltBytes) : ABSENT;
@@ -49274,7 +49498,7 @@ function walkFiles(root) {
49274
49498
  const files = [];
49275
49499
  const walk2 = (directory) => {
49276
49500
  for (const entry of (0, import_node_fs50.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
49277
- const child2 = (0, import_node_path47.join)(directory, entry.name);
49501
+ const child2 = (0, import_node_path48.join)(directory, entry.name);
49278
49502
  if (entry.isDirectory()) walk2(child2);
49279
49503
  else files.push(child2);
49280
49504
  }
@@ -49284,14 +49508,14 @@ function walkFiles(root) {
49284
49508
  }
49285
49509
  function bomPathFor(root) {
49286
49510
  try {
49287
- const registry2 = JSON.parse((0, import_node_fs50.readFileSync)((0, import_node_path47.join)(root, "surfaces.json"), "utf8"));
49288
- return (0, import_node_path47.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
49511
+ const registry2 = JSON.parse((0, import_node_fs50.readFileSync)((0, import_node_path48.join)(root, "surfaces.json"), "utf8"));
49512
+ return (0, import_node_path48.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
49289
49513
  } catch {
49290
- return (0, import_node_path47.join)(root, "distribution-bom.json");
49514
+ return (0, import_node_path48.join)(root, "distribution-bom.json");
49291
49515
  }
49292
49516
  }
49293
49517
  function rebuildTo(packageRoot, outDir) {
49294
- (0, import_node_child_process17.execFileSync)(process.execPath, ["build.mjs"], {
49518
+ (0, import_node_child_process18.execFileSync)(process.execPath, ["build.mjs"], {
49295
49519
  cwd: packageRoot,
49296
49520
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
49297
49521
  windowsHide: true,
@@ -49300,28 +49524,28 @@ function rebuildTo(packageRoot, outDir) {
49300
49524
  });
49301
49525
  }
49302
49526
  function runDistStatus(root) {
49303
- const stage = (0, import_node_fs50.mkdtempSync)((0, import_node_path47.join)((0, import_node_os21.tmpdir)(), "mmi-dist-drift-"));
49527
+ const stage = (0, import_node_fs50.mkdtempSync)((0, import_node_path48.join)((0, import_node_os22.tmpdir)(), "mmi-dist-drift-"));
49304
49528
  let overlayCount = 0;
49305
49529
  try {
49306
- const cliOut = (0, import_node_path47.join)(stage, "cli-dist");
49307
- const hubOut = (0, import_node_path47.join)(stage, "hub-dist");
49308
- rebuildTo((0, import_node_path47.join)(root, "cli"), cliOut);
49309
- rebuildTo((0, import_node_path47.join)(root, "updater"), hubOut);
49530
+ const cliOut = (0, import_node_path48.join)(stage, "cli-dist");
49531
+ const hubOut = (0, import_node_path48.join)(stage, "hub-dist");
49532
+ rebuildTo((0, import_node_path48.join)(root, "cli"), cliOut);
49533
+ rebuildTo((0, import_node_path48.join)(root, "updater"), hubOut);
49310
49534
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
49311
49535
  const rebuilt = (path2) => {
49312
49536
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
49313
- return spec ? readOrNull((0, import_node_path47.join)(outDirFor(spec.packageDir), spec.output)) : null;
49537
+ return spec ? readOrNull((0, import_node_path48.join)(outDirFor(spec.packageDir), spec.output)) : null;
49314
49538
  };
49315
- const committed = (path2) => readOrNull((0, import_node_path47.join)(root, path2));
49316
- const tree = (path2) => readOrNull((0, import_node_path47.join)(root, path2));
49317
- const distRoot = (0, import_node_path47.join)(root, "cli", "dist");
49318
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path47.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
49539
+ const committed = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
49540
+ const tree = (path2) => readOrNull((0, import_node_path48.join)(root, path2));
49541
+ const distRoot = (0, import_node_path48.join)(root, "cli", "dist");
49542
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path48.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
49319
49543
  const bom = JSON.parse((0, import_node_fs50.readFileSync)(bomPathFor(root), "utf8"));
49320
49544
  const digest = (entries) => {
49321
- const overlay = (0, import_node_path47.join)(stage, `overlay-${overlayCount++}`);
49545
+ const overlay = (0, import_node_path48.join)(stage, `overlay-${overlayCount++}`);
49322
49546
  for (const entry of entries) {
49323
- const target = (0, import_node_path47.join)(overlay, entry.path);
49324
- (0, import_node_fs50.mkdirSync)((0, import_node_path47.dirname)(target), { recursive: true });
49547
+ const target = (0, import_node_path48.join)(overlay, entry.path);
49548
+ (0, import_node_fs50.mkdirSync)((0, import_node_path48.dirname)(target), { recursive: true });
49325
49549
  (0, import_node_fs50.writeFileSync)(target, entry.bytes);
49326
49550
  }
49327
49551
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
@@ -49407,7 +49631,7 @@ init_hub_auth();
49407
49631
 
49408
49632
  // src/schedules-lift-command.ts
49409
49633
  var import_promises11 = require("node:fs/promises");
49410
- var import_node_path48 = require("node:path");
49634
+ var import_node_path49 = require("node:path");
49411
49635
  init_clean_exit();
49412
49636
  init_cli_shared();
49413
49637
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -49436,7 +49660,7 @@ async function readWorkflowFiles(dir) {
49436
49660
  const files = [];
49437
49661
  for (const name of names.sort()) {
49438
49662
  if (!/\.ya?ml$/.test(name)) continue;
49439
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises11.readFile)((0, import_node_path48.join)(dir, name), "utf8") });
49663
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises11.readFile)((0, import_node_path49.join)(dir, name), "utf8") });
49440
49664
  }
49441
49665
  return files;
49442
49666
  }
@@ -49518,9 +49742,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
49518
49742
  }
49519
49743
 
49520
49744
  // src/spawn-policy-core.ts
49521
- var import_node_child_process18 = require("node:child_process");
49745
+ var import_node_child_process19 = require("node:child_process");
49522
49746
  var import_node_fs51 = require("node:fs");
49523
- var import_node_path49 = require("node:path");
49747
+ var import_node_path50 = require("node:path");
49524
49748
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
49525
49749
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
49526
49750
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -49589,7 +49813,7 @@ function findViolationsInSource(raw) {
49589
49813
  return found;
49590
49814
  }
49591
49815
  function policedFiles(root) {
49592
- const r = (0, import_node_child_process18.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
49816
+ const r = (0, import_node_child_process19.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
49593
49817
  cwd: root,
49594
49818
  encoding: "utf8",
49595
49819
  windowsHide: true,
@@ -49606,7 +49830,7 @@ function runSpawnPolicy(root) {
49606
49830
  for (const file of files) {
49607
49831
  let raw;
49608
49832
  try {
49609
- raw = (0, import_node_fs51.readFileSync)((0, import_node_path49.join)(root, file), "utf8");
49833
+ raw = (0, import_node_fs51.readFileSync)((0, import_node_path50.join)(root, file), "utf8");
49610
49834
  } catch {
49611
49835
  continue;
49612
49836
  }
@@ -49626,7 +49850,7 @@ function runSpawnPolicy(root) {
49626
49850
  function registerDeveloperCommands(program3) {
49627
49851
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
49628
49852
  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) => {
49629
- const path2 = (0, import_node_path50.join)(process.cwd(), ".gitignore");
49853
+ const path2 = (0, import_node_path51.join)(process.cwd(), ".gitignore");
49630
49854
  const current = (0, import_node_fs52.existsSync)(path2) ? (0, import_node_fs52.readFileSync)(path2, "utf8") : null;
49631
49855
  const plan = planManagedGitignore(current);
49632
49856
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
@@ -49901,12 +50125,12 @@ ${r.stderr ?? ""}`).catch(() => "");
49901
50125
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
49902
50126
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
49903
50127
  function envHealLockPath(home) {
49904
- return (0, import_node_path51.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
50128
+ return (0, import_node_path52.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
49905
50129
  }
49906
50130
  async function withEnvHealLock(what, run) {
49907
50131
  try {
49908
50132
  return await withFileLock(
49909
- envHealLockPath((0, import_node_os22.homedir)()),
50133
+ envHealLockPath((0, import_node_os23.homedir)()),
49910
50134
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
49911
50135
  run
49912
50136
  );
@@ -50003,7 +50227,7 @@ function mmiDoctorDeps(opts = {}) {
50003
50227
  const configRoot = surfaceConfigRoot(surface);
50004
50228
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
50005
50229
  const plan = buildPluginCachePlan(
50006
- (0, import_node_os22.homedir)(),
50230
+ (0, import_node_os23.homedir)(),
50007
50231
  running,
50008
50232
  pluginCacheFsDeps(configRoot, () => 0),
50009
50233
  { configRoot, includeStaging: surface !== "codex" }
@@ -50031,7 +50255,7 @@ function mmiDoctorDeps(opts = {}) {
50031
50255
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
50032
50256
  const installed = installedActivePluginVersion(surface);
50033
50257
  const plan = buildPluginCachePlan(
50034
- (0, import_node_os22.homedir)(),
50258
+ (0, import_node_os23.homedir)(),
50035
50259
  running,
50036
50260
  pluginCacheFsDeps(configRoot, () => 0),
50037
50261
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
@@ -50073,7 +50297,7 @@ function mmiDoctorDeps(opts = {}) {
50073
50297
  // has no generated routing index would
50074
50298
  // get a permanent — demanding an artifact it never asked for.
50075
50299
  docsIndexState: (root) => {
50076
- if (!(0, import_node_fs53.existsSync)((0, import_node_path51.join)(root, DOCS_INDEX_PATH))) return void 0;
50300
+ if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return void 0;
50077
50301
  const real = createDocsIndexDeps(root);
50078
50302
  let docs;
50079
50303
  const listDocs = () => docs ??= real.listDocs();
@@ -50082,7 +50306,7 @@ function mmiDoctorDeps(opts = {}) {
50082
50306
  },
50083
50307
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
50084
50308
  healDocsIndex: (root) => {
50085
- if (!(0, import_node_fs53.existsSync)((0, import_node_path51.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
50309
+ if (!(0, import_node_fs53.existsSync)((0, import_node_path52.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
50086
50310
  const real = createDocsIndexDeps(root);
50087
50311
  let docs;
50088
50312
  const listDocs = () => docs ??= real.listDocs();
@@ -51169,16 +51393,16 @@ function ciAuditDeps2() {
51169
51393
  // gate re-seed step is skipped gracefully rather than failing mid-run.
51170
51394
  readSeedFile: (path2) => {
51171
51395
  if (!root) return null;
51172
- const fullPath = (0, import_node_path51.join)(root, path2);
51396
+ const fullPath = (0, import_node_path52.join)(root, path2);
51173
51397
  return (0, import_node_fs53.existsSync)(fullPath) ? (0, import_node_fs53.readFileSync)(fullPath, "utf8") : null;
51174
51398
  }
51175
51399
  };
51176
51400
  }
51177
51401
  function hubRoot2() {
51178
- const fromPkg = (0, import_node_path51.join)(__dirname, "..", "..");
51402
+ const fromPkg = (0, import_node_path52.join)(__dirname, "..", "..");
51179
51403
  const marker = "skills/bootstrap/seeds/manifest.json";
51180
- if ((0, import_node_fs53.existsSync)((0, import_node_path51.join)(fromPkg, marker))) return fromPkg;
51181
- if ((0, import_node_fs53.existsSync)((0, import_node_path51.join)(process.cwd(), marker))) return process.cwd();
51404
+ if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(fromPkg, marker))) return fromPkg;
51405
+ if ((0, import_node_fs53.existsSync)((0, import_node_path52.join)(process.cwd(), marker))) return process.cwd();
51182
51406
  return null;
51183
51407
  }
51184
51408
  registerQueryCommands(program2);
@@ -51319,7 +51543,7 @@ function directoryBytes(path2) {
51319
51543
  return 0;
51320
51544
  }
51321
51545
  for (const entry of entries) {
51322
- const child2 = (0, import_node_path51.join)(path2, entry.name);
51546
+ const child2 = (0, import_node_path52.join)(path2, entry.name);
51323
51547
  if (entry.isDirectory()) total += directoryBytes(child2);
51324
51548
  else {
51325
51549
  try {
@@ -51349,7 +51573,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
51349
51573
  dirBytes,
51350
51574
  listStagingDirs: (root) => (0, import_node_fs53.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
51351
51575
  try {
51352
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path51.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs53.statSync)(p).mtimeMs) };
51576
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path52.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs53.statSync)(p).mtimeMs) };
51353
51577
  } catch {
51354
51578
  return { name: d.name, mtimeMs: Date.now() };
51355
51579
  }
@@ -51363,7 +51587,7 @@ function stagingApplyFsGuard(configRoot) {
51363
51587
  return {
51364
51588
  referencedPaths: () => readInstalledPluginRefs(configRoot),
51365
51589
  mtimeMs: (name) => {
51366
- const p = (0, import_node_path51.join)(stagingRoot, name);
51590
+ const p = (0, import_node_path52.join)(stagingRoot, name);
51367
51591
  if (!(0, import_node_fs53.existsSync)(p)) return null;
51368
51592
  try {
51369
51593
  return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs53.statSync)(q).mtimeMs);
@@ -51386,7 +51610,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
51386
51610
  return;
51387
51611
  }
51388
51612
  const plan = buildPluginCachePlan(
51389
- (0, import_node_os22.homedir)(),
51613
+ (0, import_node_os23.homedir)(),
51390
51614
  running,
51391
51615
  pluginCacheFsDeps(configRoot, directoryBytes),
51392
51616
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }