@mutmutco/cli 3.82.0 → 3.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +421 -253
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3416,7 +3416,7 @@ var program = new Command();
3416
3416
 
3417
3417
  // src/index.ts
3418
3418
  var import_promises10 = require("node:fs/promises");
3419
- var import_node_fs35 = require("node:fs");
3419
+ var import_node_fs36 = require("node:fs");
3420
3420
  var import_node_child_process16 = require("node:child_process");
3421
3421
 
3422
3422
  // src/cli-shared.ts
@@ -6431,7 +6431,14 @@ async function provisionWorktree(worktreeRoot, deps) {
6431
6431
  });
6432
6432
  const allDirs = scanInstallDirs(worktreeRoot, fs2);
6433
6433
  const targets = npmInstallTargets(allDirs);
6434
- const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules).map((d) => d.dir);
6434
+ if (deps.validateInstall) {
6435
+ for (const dir of allDirs.filter((d) => d.hasPackageJson && d.hasLockfile && d.hasNodeModules)) {
6436
+ const cwd = dir.dir ? (0, import_node_path10.join)(worktreeRoot, dir.dir) : worktreeRoot;
6437
+ if (!await deps.validateInstall(cwd)) targets.push({ dir: dir.dir, command: "npm ci" });
6438
+ }
6439
+ }
6440
+ const targetDirs = new Set(targets.map((target) => target.dir));
6441
+ const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
6435
6442
  const installed = [];
6436
6443
  for (const target of targets) {
6437
6444
  const cwd = target.dir ? (0, import_node_path10.join)(worktreeRoot, target.dir) : worktreeRoot;
@@ -6595,7 +6602,7 @@ function commandLadderHint() {
6595
6602
  }
6596
6603
 
6597
6604
  // src/index.ts
6598
- var import_node_path34 = require("node:path");
6605
+ var import_node_path35 = require("node:path");
6599
6606
 
6600
6607
  // src/merge-ci-policy.ts
6601
6608
  function resolveMergeCiPolicy(input) {
@@ -8027,7 +8034,7 @@ async function foldReleaseVersion(deps, model, tag, foldPaths) {
8027
8034
  if (foldPaths.length === 0) return "no version manifest to fold \u2014 the tag is the version";
8028
8035
  const version = tag.replace(/^v/, "");
8029
8036
  if (model === "hub-serverless") {
8030
- await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version]);
8037
+ await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", "HEAD"]);
8031
8038
  } else {
8032
8039
  await installAppFoldDeps(deps);
8033
8040
  await deps.run("npm", ["version", version, "--no-git-tag-version", "--allow-same-version"]);
@@ -11683,10 +11690,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
11683
11690
  }
11684
11691
 
11685
11692
  // src/index.ts
11686
- var import_node_os13 = require("node:os");
11693
+ var import_node_os14 = require("node:os");
11687
11694
 
11688
11695
  // src/board.ts
11689
11696
  var import_node_child_process8 = require("node:child_process");
11697
+ var import_node_fs20 = require("node:fs");
11698
+ var import_node_os7 = require("node:os");
11699
+ var import_node_path18 = require("node:path");
11690
11700
  var import_node_util6 = require("node:util");
11691
11701
 
11692
11702
  // src/board-priority.ts
@@ -14841,6 +14851,25 @@ function applyOrgMarketplacePins(path2, names) {
14841
14851
  "pin"
14842
14852
  );
14843
14853
  }
14854
+ function writeMarketplacePinPending(path2, names, now = Date.now()) {
14855
+ try {
14856
+ (0, import_node_fs18.mkdirSync)((0, import_node_path16.dirname)(path2), { recursive: true });
14857
+ (0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify({ v: 1, names: [...names], at: new Date(now).toISOString() })}
14858
+ `, "utf8");
14859
+ } catch {
14860
+ }
14861
+ }
14862
+ function readMarketplacePinPending(path2, name, now = Date.now()) {
14863
+ try {
14864
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
14865
+ const at = typeof parsed.at === "string" ? Date.parse(parsed.at) : Number.NaN;
14866
+ if (parsed.v !== 1 || !Array.isArray(parsed.names) || !parsed.names.includes(name) || !Number.isFinite(at)) return void 0;
14867
+ if (now - at < 0 || now - at > 12 * 60 * 6e4) return void 0;
14868
+ return { v: 1, names: parsed.names.filter((n) => typeof n === "string"), at: parsed.at };
14869
+ } catch {
14870
+ return void 0;
14871
+ }
14872
+ }
14844
14873
  function restoredPinsLine(pins) {
14845
14874
  const named = [...pins].map(([name, want]) => {
14846
14875
  const fields = [
@@ -16129,6 +16158,38 @@ async function postClaimMarkerComment(client, item) {
16129
16158
  `);
16130
16159
  }
16131
16160
  }
16161
+ var CLAIM_SESSION_ACTIVITY_MS = 30 * 6e4;
16162
+ var CLAIM_SESSION_PROBE_CACHE_MS = 6e4;
16163
+ var claimSessionProbeCache = /* @__PURE__ */ new Map();
16164
+ function probeLocalClaimSession(marker, now = Date.now()) {
16165
+ if (!marker.session || !marker.host || marker.host.toLowerCase() !== (0, import_node_os7.hostname)().toLowerCase()) return void 0;
16166
+ if (!marker.surface?.toLowerCase().startsWith("claude")) return void 0;
16167
+ const cacheKey = `${marker.host.toLowerCase()}/${marker.session}`;
16168
+ const cached = claimSessionProbeCache.get(cacheKey);
16169
+ if (cached && now - cached.checkedAt <= CLAIM_SESSION_PROBE_CACHE_MS) return cached.state;
16170
+ const remember = (state) => {
16171
+ claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
16172
+ return state;
16173
+ };
16174
+ const root = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".claude", "projects");
16175
+ try {
16176
+ const wanted = `${marker.session}.jsonl`.toLowerCase();
16177
+ const pending = [root];
16178
+ while (pending.length) {
16179
+ const dir = pending.pop();
16180
+ for (const entry of (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true })) {
16181
+ const path2 = (0, import_node_path18.join)(dir, entry.name);
16182
+ if (entry.isDirectory()) pending.push(path2);
16183
+ else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
16184
+ return remember(now - (0, import_node_fs20.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
16185
+ }
16186
+ }
16187
+ }
16188
+ return remember("dead");
16189
+ } catch {
16190
+ return remember("unverifiable");
16191
+ }
16192
+ }
16132
16193
  function openPullsFetcher(client) {
16133
16194
  const byRepo = /* @__PURE__ */ new Map();
16134
16195
  return (repo) => {
@@ -16146,6 +16207,11 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
16146
16207
  const comments = await client.restPaginate(`repos/${repo}/issues/${number}/comments`);
16147
16208
  out.marker = latestClaimMarker(comments.map((comment) => ({ body: comment.body ?? "" })));
16148
16209
  out.markerAgeMs = out.marker ? Date.now() - Date.parse(out.marker.ts) : void 0;
16210
+ if (out.marker) {
16211
+ const state = probeLocalClaimSession(out.marker);
16212
+ if (state === "live" || state === "dead") out.sessionState = state;
16213
+ else if (state === "unverifiable") out.failed.push("session");
16214
+ }
16149
16215
  } catch {
16150
16216
  out.failed.push("comments");
16151
16217
  }
@@ -16177,8 +16243,10 @@ async function gatherClaimLiveness(client, repo, number, fetchOpenPulls) {
16177
16243
  }
16178
16244
  function liveEvidenceLines(evidence, repo) {
16179
16245
  const live = [];
16180
- if (evidence.marker && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS) {
16181
- live.push(`claim marker from ${describeClaimMarker(evidence.marker)} is only ${formatClaimAge(evidence.markerAgeMs)} old`);
16246
+ if (evidence.marker && evidence.sessionState === "live") {
16247
+ live.push(`claim session ${describeClaimMarker(evidence.marker)} has recent local transcript activity`);
16248
+ } else if (evidence.marker && evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS) {
16249
+ live.push(`claim marker from ${describeClaimMarker(evidence.marker)} is only ${formatClaimAge(evidence.markerAgeMs)} old (session not locally probeable)`);
16182
16250
  }
16183
16251
  if (evidence.openPr) live.push(evidence.openPr);
16184
16252
  if (evidence.branch) live.push(`live branch ${evidence.branch} on ${repo}`);
@@ -16274,9 +16342,9 @@ async function boardDoctor(options, deps = {}) {
16274
16342
  while (next < claimedItems.length) {
16275
16343
  const item = claimedItems[next++];
16276
16344
  const evidence = await gatherClaimLiveness(client, item.repository, item.number, fetchOpenPulls);
16277
- const freshMarker = evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS;
16345
+ const freshMarker = evidence.sessionState === "live" || evidence.sessionState !== "dead" && evidence.markerAgeMs !== void 0 && evidence.markerAgeMs < CLAIM_MARKER_LIVE_MS;
16278
16346
  const bits = [
16279
- evidence.marker ? `claim marker ${describeClaimMarker(evidence.marker)} ${formatClaimAge(evidence.markerAgeMs)} old` : evidence.failed.includes("comments") ? "claim marker unreadable" : "no claim marker",
16347
+ evidence.marker ? `claim marker ${describeClaimMarker(evidence.marker)} ${formatClaimAge(evidence.markerAgeMs)} old` + (evidence.sessionState ? `; local session ${evidence.sessionState}` : "") : evidence.failed.includes("comments") ? "claim marker unreadable" : "no claim marker",
16280
16348
  evidence.openPr,
16281
16349
  evidence.branch ? `branch ${evidence.branch}` : void 0
16282
16350
  ].filter((bit) => Boolean(bit)).join("; ");
@@ -16380,7 +16448,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
16380
16448
  }
16381
16449
 
16382
16450
  // src/issue-body.ts
16383
- var import_node_os7 = require("node:os");
16451
+ var import_node_os8 = require("node:os");
16384
16452
  var TextArgError = class extends Error {
16385
16453
  constructor(message, code, offendingFlag) {
16386
16454
  super(message);
@@ -16392,7 +16460,7 @@ var TextArgError = class extends Error {
16392
16460
  offendingFlag;
16393
16461
  };
16394
16462
  function emptyStdinMessage(fileFlag) {
16395
- if ((0, import_node_os7.platform)() === "win32") {
16463
+ if ((0, import_node_os8.platform)() === "win32") {
16396
16464
  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)`;
16397
16465
  }
16398
16466
  return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
@@ -17717,7 +17785,7 @@ async function runHotfixStart(deps, options) {
17717
17785
  }
17718
17786
  notes.push(`cherry-picked ${label} onto ${branch} (from origin/main, -x trailer recorded)`);
17719
17787
  if (deployModel === "hub-serverless") {
17720
- await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version]);
17788
+ await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", "HEAD"]);
17721
17789
  const changedFiles = (await deps.run("node", ["scripts/release-distribution.mjs", "changed-files"])).split("\n").map((s) => s.trim()).filter(Boolean);
17722
17790
  await deps.run("git", ["add", "-f", "--", ...changedFiles]);
17723
17791
  const staged = await deps.run("git", ["diff", "--cached", "--name-only"]);
@@ -18575,8 +18643,8 @@ function renderAccessReport(report) {
18575
18643
 
18576
18644
  // src/doc-refs-core.ts
18577
18645
  var import_node_child_process10 = require("node:child_process");
18578
- var import_node_fs20 = require("node:fs");
18579
- var import_node_path18 = require("node:path");
18646
+ var import_node_fs21 = require("node:fs");
18647
+ var import_node_path19 = require("node:path");
18580
18648
  var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
18581
18649
  var PIN_MENTION_RE = /<!--\s*pinned by\b/;
18582
18650
  var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
@@ -18632,7 +18700,7 @@ function checkPins(root, readFile9, docs2) {
18632
18700
  findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
18633
18701
  continue;
18634
18702
  }
18635
- const source = readFile9((0, import_node_path18.join)(root, pin.file));
18703
+ const source = readFile9((0, import_node_path19.join)(root, pin.file));
18636
18704
  if (source == null) {
18637
18705
  findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
18638
18706
  continue;
@@ -18692,7 +18760,7 @@ function checkRefs(root, deps, docs2) {
18692
18760
  const candidates = [];
18693
18761
  const direct = [];
18694
18762
  for (const [doc, markdown] of Object.entries(docs2)) {
18695
- const docDir = import_node_path18.posix.dirname(doc);
18763
+ const docDir = import_node_path19.posix.dirname(doc);
18696
18764
  const base = docDir === "." ? "" : docDir;
18697
18765
  const covered = /* @__PURE__ */ new Set();
18698
18766
  const markers = [];
@@ -18701,21 +18769,21 @@ function checkRefs(root, deps, docs2) {
18701
18769
  direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
18702
18770
  continue;
18703
18771
  }
18704
- const docRel = import_node_path18.posix.normalize(import_node_path18.posix.join(base, fwd.target));
18705
- const rootRel = import_node_path18.posix.normalize(fwd.target.replace(/^\/+/, ""));
18772
+ const docRel = import_node_path19.posix.normalize(import_node_path19.posix.join(base, fwd.target));
18773
+ const rootRel = import_node_path19.posix.normalize(fwd.target.replace(/^\/+/, ""));
18706
18774
  markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
18707
18775
  covered.add(docRel);
18708
18776
  covered.add(rootRel);
18709
18777
  }
18710
18778
  const links = extractLinks(markdown).map(({ target, line }) => {
18711
- const resolved = import_node_path18.posix.normalize(import_node_path18.posix.join(base, target));
18712
- return { target, line, resolved, missing: !exists((0, import_node_path18.join)(root, resolved)) };
18779
+ const resolved = import_node_path19.posix.normalize(import_node_path19.posix.join(base, target));
18780
+ return { target, line, resolved, missing: !exists((0, import_node_path19.join)(root, resolved)) };
18713
18781
  });
18714
18782
  for (const marker of markers) {
18715
18783
  const coversMissing = links.some(
18716
18784
  (l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
18717
18785
  );
18718
- if (!coversMissing && (exists((0, import_node_path18.join)(root, marker.docRel)) || exists((0, import_node_path18.join)(root, marker.rootRel)))) {
18786
+ if (!coversMissing && (exists((0, import_node_path19.join)(root, marker.docRel)) || exists((0, import_node_path19.join)(root, marker.rootRel)))) {
18719
18787
  direct.push({
18720
18788
  kind: "stale-forward-ref",
18721
18789
  doc,
@@ -18726,8 +18794,8 @@ function checkRefs(root, deps, docs2) {
18726
18794
  }
18727
18795
  for (const { ref, line } of extractRefs(markdown)) {
18728
18796
  const first = ref.split("/")[0];
18729
- if (!exists((0, import_node_path18.join)(root, first))) continue;
18730
- if (!exists((0, import_node_path18.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
18797
+ if (!exists((0, import_node_path19.join)(root, first))) continue;
18798
+ if (!exists((0, import_node_path19.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
18731
18799
  }
18732
18800
  for (const { target, line, resolved, missing } of links) {
18733
18801
  if (resolved.startsWith("..")) {
@@ -18775,22 +18843,22 @@ function checkCommands(docs2, commandPaths) {
18775
18843
  return { ok: findings.length === 0, findings, warnings: [] };
18776
18844
  }
18777
18845
  function readFileOrNull(path2) {
18778
- return (0, import_node_fs20.existsSync)(path2) ? (0, import_node_fs20.readFileSync)(path2, "utf8") : null;
18846
+ return (0, import_node_fs21.existsSync)(path2) ? (0, import_node_fs21.readFileSync)(path2, "utf8") : null;
18779
18847
  }
18780
18848
  function walk(dir, root, out) {
18781
- for (const entry of (0, import_node_fs20.readdirSync)(dir)) {
18782
- const full = (0, import_node_path18.join)(dir, entry);
18783
- if ((0, import_node_fs20.statSync)(full).isDirectory()) walk(full, root, out);
18849
+ for (const entry of (0, import_node_fs21.readdirSync)(dir)) {
18850
+ const full = (0, import_node_path19.join)(dir, entry);
18851
+ if ((0, import_node_fs21.statSync)(full).isDirectory()) walk(full, root, out);
18784
18852
  else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
18785
18853
  }
18786
18854
  return out;
18787
18855
  }
18788
18856
  function defaultListDocs(root) {
18789
- const docsDir = (0, import_node_path18.join)(root, "docs");
18790
- const docs2 = ((0, import_node_fs20.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
18857
+ const docsDir = (0, import_node_path19.join)(root, "docs");
18858
+ const docs2 = ((0, import_node_fs21.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
18791
18859
  (rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
18792
18860
  );
18793
- return [...ROOT_DOCS.filter((rel) => (0, import_node_fs20.existsSync)((0, import_node_path18.join)(root, rel))), ...docs2];
18861
+ return [...ROOT_DOCS.filter((rel) => (0, import_node_fs21.existsSync)((0, import_node_path19.join)(root, rel))), ...docs2];
18794
18862
  }
18795
18863
  function defaultIsIgnored(root, relPaths, exec = import_node_child_process10.execFileSync) {
18796
18864
  const inRepo = relPaths.filter((p) => !p.startsWith(".."));
@@ -18810,14 +18878,14 @@ function defaultIsIgnored(root, relPaths, exec = import_node_child_process10.exe
18810
18878
  }
18811
18879
  function runDocRefs(root, deps = {}) {
18812
18880
  const readFile9 = deps.readFile ?? readFileOrNull;
18813
- const exists = deps.exists ?? import_node_fs20.existsSync;
18881
+ const exists = deps.exists ?? import_node_fs21.existsSync;
18814
18882
  const listDocs = deps.listDocs ?? defaultListDocs;
18815
18883
  const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
18816
18884
  const commandPaths = deps.commandPaths ?? null;
18817
18885
  const walked = listDocs(root);
18818
18886
  const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
18819
18887
  const docs2 = Object.fromEntries(
18820
- walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path18.join)(root, rel))]).filter(([, body]) => body != null)
18888
+ walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path19.join)(root, rel))]).filter(([, body]) => body != null)
18821
18889
  );
18822
18890
  const refResult = checkRefs(root, { exists, isIgnored }, docs2);
18823
18891
  const findings = [
@@ -18844,8 +18912,8 @@ function runDocRefs(root, deps = {}) {
18844
18912
 
18845
18913
  // src/spawn-policy-core.ts
18846
18914
  var import_node_child_process11 = require("node:child_process");
18847
- var import_node_fs21 = require("node:fs");
18848
- var import_node_path19 = require("node:path");
18915
+ var import_node_fs22 = require("node:fs");
18916
+ var import_node_path20 = require("node:path");
18849
18917
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
18850
18918
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
18851
18919
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -18931,7 +18999,7 @@ function runSpawnPolicy(root) {
18931
18999
  for (const file of files) {
18932
19000
  let raw;
18933
19001
  try {
18934
- raw = (0, import_node_fs21.readFileSync)((0, import_node_path19.join)(root, file), "utf8");
19002
+ raw = (0, import_node_fs22.readFileSync)((0, import_node_path20.join)(root, file), "utf8");
18935
19003
  } catch {
18936
19004
  continue;
18937
19005
  }
@@ -18949,8 +19017,8 @@ function runSpawnPolicy(root) {
18949
19017
 
18950
19018
  // src/test-policy-core.ts
18951
19019
  var import_node_child_process12 = require("node:child_process");
18952
- var import_node_fs22 = require("node:fs");
18953
- var import_node_path20 = require("node:path");
19020
+ var import_node_fs23 = require("node:fs");
19021
+ var import_node_path21 = require("node:path");
18954
19022
  var POLICY_FILE = "test-policy.json";
18955
19023
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
18956
19024
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -19003,7 +19071,7 @@ function isTestPath(path2) {
19003
19071
  return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
19004
19072
  }
19005
19073
  function loadPolicy(root, readFile9 = readFileOrNull2) {
19006
- const raw = readFile9((0, import_node_path20.join)(root, POLICY_FILE));
19074
+ const raw = readFile9((0, import_node_path21.join)(root, POLICY_FILE));
19007
19075
  if (raw == null) return { mandatory: [], declared: false };
19008
19076
  try {
19009
19077
  return { ...JSON.parse(raw), declared: true };
@@ -19013,7 +19081,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
19013
19081
  }
19014
19082
  function readFileOrNull2(path2) {
19015
19083
  try {
19016
- return (0, import_node_fs22.readFileSync)(path2, "utf8");
19084
+ return (0, import_node_fs23.readFileSync)(path2, "utf8");
19017
19085
  } catch {
19018
19086
  return null;
19019
19087
  }
@@ -19040,12 +19108,12 @@ function classify(changed, policy, present = () => false) {
19040
19108
  const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
19041
19109
  return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
19042
19110
  }
19043
- function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
19044
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path20.join)(root, p)));
19111
+ function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
19112
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path21.join)(root, p)));
19045
19113
  }
19046
- function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs22.existsSync)(path2)) {
19114
+ function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
19047
19115
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
19048
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path20.join)(root, p)));
19116
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path21.join)(root, p)));
19049
19117
  }
19050
19118
  function evaluate(changed, policy, present = () => false) {
19051
19119
  const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -19227,13 +19295,13 @@ function changedFilesSince(base, cwd) {
19227
19295
  }
19228
19296
  function runTestPolicy(root, deps = {}) {
19229
19297
  const policy = deps.policy ?? loadPolicy(root);
19230
- const exists = deps.exists ?? ((path2) => (0, import_node_fs22.existsSync)(path2));
19298
+ const exists = deps.exists ?? ((path2) => (0, import_node_fs23.existsSync)(path2));
19231
19299
  const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
19232
19300
  const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
19233
19301
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
19234
19302
  const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
19235
19303
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
19236
- const present = (path2) => exists((0, import_node_path20.join)(root, path2));
19304
+ const present = (path2) => exists((0, import_node_path21.join)(root, path2));
19237
19305
  const removedByThisDiff = removedPaths(changed);
19238
19306
  const staleFindings = [];
19239
19307
  const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
@@ -19389,8 +19457,8 @@ function docsAuditStatus(fetch2, opts) {
19389
19457
  }
19390
19458
 
19391
19459
  // src/project-info-sync.ts
19392
- var import_node_fs23 = require("node:fs");
19393
- var import_node_path21 = require("node:path");
19460
+ var import_node_fs24 = require("node:fs");
19461
+ var import_node_path22 = require("node:path");
19394
19462
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
19395
19463
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
19396
19464
  projectV2 { id }
@@ -19435,14 +19503,14 @@ function sharedName(entries, fallback) {
19435
19503
  }
19436
19504
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19437
19505
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
19438
- const readmePath = (0, import_node_path21.join)(repoRoot2, "README.md");
19439
- if (!(0, import_node_fs23.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
19506
+ const readmePath = (0, import_node_path22.join)(repoRoot2, "README.md");
19507
+ if (!(0, import_node_fs24.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
19440
19508
  const entries = entriesFor(project2, projects);
19441
19509
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
19442
19510
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
19443
19511
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
19444
19512
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
19445
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs23.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
19513
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs24.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
19446
19514
  const lines = [
19447
19515
  `# ${projectName}`,
19448
19516
  "",
@@ -19461,8 +19529,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19461
19529
  const targetBase = `https://github.com/${targetRepo2}`;
19462
19530
  const targetBranch = branchFor(targetRepo2, projects);
19463
19531
  const orgDocs = [
19464
- (0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
19465
- (0, import_node_fs23.existsSync)((0, import_node_path21.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
19532
+ (0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
19533
+ (0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
19466
19534
  ].filter(Boolean);
19467
19535
  if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
19468
19536
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
@@ -20321,9 +20389,9 @@ function writeError(res) {
20321
20389
  }
20322
20390
 
20323
20391
  // src/secrets-commands.ts
20324
- var import_node_fs24 = require("node:fs");
20325
- var import_node_path22 = require("node:path");
20326
- var import_node_os8 = require("node:os");
20392
+ var import_node_fs25 = require("node:fs");
20393
+ var import_node_path23 = require("node:path");
20394
+ var import_node_os9 = require("node:os");
20327
20395
 
20328
20396
  // src/project-runtime.ts
20329
20397
  function hasRuntimeSecretContract(contract) {
@@ -20446,18 +20514,18 @@ function collectMap(value, previous = []) {
20446
20514
  return [...previous, value];
20447
20515
  }
20448
20516
  async function decryptRailsCredentials(input) {
20449
- const appDir = (0, import_node_path22.resolve)(input.appDir ?? process.cwd());
20517
+ const appDir = (0, import_node_path23.resolve)(input.appDir ?? process.cwd());
20450
20518
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
20451
20519
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
20452
- const credentialsPath = (0, import_node_path22.resolve)(appDir, credentialsFile);
20453
- const masterKeyPath = (0, import_node_path22.resolve)(appDir, masterKeyFile);
20520
+ const credentialsPath = (0, import_node_path23.resolve)(appDir, credentialsFile);
20521
+ const masterKeyPath = (0, import_node_path23.resolve)(appDir, masterKeyFile);
20454
20522
  const env = {
20455
20523
  ...process.env,
20456
20524
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
20457
20525
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
20458
20526
  };
20459
- if ((0, import_node_fs24.existsSync)(masterKeyPath)) {
20460
- env.RAILS_MASTER_KEY = (0, import_node_fs24.readFileSync)(masterKeyPath, "utf8").trim();
20527
+ if ((0, import_node_fs25.existsSync)(masterKeyPath)) {
20528
+ env.RAILS_MASTER_KEY = (0, import_node_fs25.readFileSync)(masterKeyPath, "utf8").trim();
20461
20529
  }
20462
20530
  const script = [
20463
20531
  'require "json"',
@@ -20467,9 +20535,9 @@ async function decryptRailsCredentials(input) {
20467
20535
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
20468
20536
  "puts JSON.generate(config.config)"
20469
20537
  ].join("\n");
20470
- const scriptDir = (0, import_node_fs24.mkdtempSync)((0, import_node_path22.join)((0, import_node_os8.tmpdir)(), "mmi-rails-decrypt-"));
20471
- const scriptPath = (0, import_node_path22.join)(scriptDir, "decrypt.rb");
20472
- (0, import_node_fs24.writeFileSync)(scriptPath, script, "utf8");
20538
+ const scriptDir = (0, import_node_fs25.mkdtempSync)((0, import_node_path23.join)((0, import_node_os9.tmpdir)(), "mmi-rails-decrypt-"));
20539
+ const scriptPath = (0, import_node_path23.join)(scriptDir, "decrypt.rb");
20540
+ (0, import_node_fs25.writeFileSync)(scriptPath, script, "utf8");
20473
20541
  try {
20474
20542
  const args = ["exec", "ruby", scriptPath];
20475
20543
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -20481,7 +20549,7 @@ async function decryptRailsCredentials(input) {
20481
20549
  });
20482
20550
  return JSON.parse(stdout);
20483
20551
  } finally {
20484
- (0, import_node_fs24.rmSync)(scriptDir, { recursive: true, force: true });
20552
+ (0, import_node_fs25.rmSync)(scriptDir, { recursive: true, force: true });
20485
20553
  }
20486
20554
  }
20487
20555
  async function readSecretStdin() {
@@ -20571,7 +20639,7 @@ function registerSecretsCommands(program3) {
20571
20639
  let body;
20572
20640
  if (o.file) {
20573
20641
  try {
20574
- body = (0, import_node_fs24.readFileSync)((0, import_node_path22.resolve)(o.file), "utf8");
20642
+ body = (0, import_node_fs25.readFileSync)((0, import_node_path23.resolve)(o.file), "utf8");
20575
20643
  } catch (e) {
20576
20644
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
20577
20645
  }
@@ -20676,7 +20744,7 @@ function registerSecretsCommands(program3) {
20676
20744
  {
20677
20745
  ...d,
20678
20746
  decryptRailsCredentials,
20679
- removeFile: (path2) => (0, import_node_fs24.unlinkSync)((0, import_node_path22.resolve)(o.appDir ?? process.cwd(), path2))
20747
+ removeFile: (path2) => (0, import_node_fs25.unlinkSync)((0, import_node_path23.resolve)(o.appDir ?? process.cwd(), path2))
20680
20748
  },
20681
20749
  {
20682
20750
  repo: o.repo,
@@ -20840,7 +20908,7 @@ async function activateAppActor(commandPath3, env, mint) {
20840
20908
  }
20841
20909
 
20842
20910
  // src/box-commands.ts
20843
- var import_node_fs25 = require("node:fs");
20911
+ var import_node_fs26 = require("node:fs");
20844
20912
 
20845
20913
  // src/box.ts
20846
20914
  var BOX_KEYS = {
@@ -21043,7 +21111,7 @@ function registerBoxCommands(program3) {
21043
21111
  }
21044
21112
  if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
21045
21113
  else if (o.ssh && o.script) {
21046
- (0, import_node_fs25.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
21114
+ (0, import_node_fs26.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
21047
21115
  console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
21048
21116
  } else if (o.ssh) console.log(`${formatSshRecipe(found)}
21049
21117
  ${SSH_RECIPE_AGENT_NOTE}`);
@@ -21729,7 +21797,7 @@ function registerSchedulesCommands(program3) {
21729
21797
 
21730
21798
  // src/file-lock.ts
21731
21799
  var import_promises4 = require("node:fs/promises");
21732
- var import_node_path23 = require("node:path");
21800
+ var import_node_path24 = require("node:path");
21733
21801
  var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
21734
21802
  var IMMEDIATE_RETRY_BUDGET = 3;
21735
21803
  var FileLockBusyError = class extends Error {
@@ -21814,7 +21882,7 @@ async function releaseFileLock(lockPath, guard) {
21814
21882
  }
21815
21883
  async function withFileLock(lockPath, opts, fn) {
21816
21884
  const resolved = resolveFileLockOpts(opts);
21817
- await (0, import_promises4.mkdir)((0, import_node_path23.dirname)(lockPath), { recursive: true }).catch(() => void 0);
21885
+ await (0, import_promises4.mkdir)((0, import_node_path24.dirname)(lockPath), { recursive: true }).catch(() => void 0);
21818
21886
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
21819
21887
  try {
21820
21888
  return await fn();
@@ -21825,7 +21893,7 @@ async function withFileLock(lockPath, opts, fn) {
21825
21893
 
21826
21894
  // src/schedules-lift-command.ts
21827
21895
  var import_promises5 = require("node:fs/promises");
21828
- var import_node_path24 = require("node:path");
21896
+ var import_node_path25 = require("node:path");
21829
21897
 
21830
21898
  // src/schedules-lift.ts
21831
21899
  var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
@@ -21931,7 +21999,7 @@ async function readWorkflowFiles(dir) {
21931
21999
  const files = [];
21932
22000
  for (const name of names.sort()) {
21933
22001
  if (!/\.ya?ml$/.test(name)) continue;
21934
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path24.join)(dir, name), "utf8") });
22002
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises5.readFile)((0, import_node_path25.join)(dir, name), "utf8") });
21935
22003
  }
21936
22004
  return files;
21937
22005
  }
@@ -22015,13 +22083,13 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
22015
22083
  // src/edge-tunnel.ts
22016
22084
  var HOSTNAME_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
22017
22085
  var UPSTREAM_RE = /^https?:\/\/[^/\s]+(?::\d+)?(?:\/.*)?$/;
22018
- function tunnelNameFromHostname(hostname2) {
22019
- return hostname2.replace(/\./g, "-").slice(0, 63);
22086
+ function tunnelNameFromHostname(hostname3) {
22087
+ return hostname3.replace(/\./g, "-").slice(0, 63);
22020
22088
  }
22021
- function planInfraTunnel(hostname2, upstream) {
22022
- const host = hostname2.trim().toLowerCase();
22089
+ function planInfraTunnel(hostname3, upstream) {
22090
+ const host = hostname3.trim().toLowerCase();
22023
22091
  const origin = upstream.trim();
22024
- if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname2)}`);
22092
+ if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname3)}`);
22025
22093
  if (!UPSTREAM_RE.test(origin)) throw new Error(`invalid upstream ${JSON.stringify(upstream)} \u2014 expected http(s)://host:port`);
22026
22094
  const tunnelName = tunnelNameFromHostname(host);
22027
22095
  const configYaml = [
@@ -22473,9 +22541,9 @@ function registerQueryCommands(program3) {
22473
22541
  }
22474
22542
 
22475
22543
  // src/bootstrap-commands.ts
22476
- var import_node_fs26 = require("node:fs");
22477
- var import_node_os9 = require("node:os");
22478
- var import_node_path25 = require("node:path");
22544
+ var import_node_fs27 = require("node:fs");
22545
+ var import_node_os10 = require("node:os");
22546
+ var import_node_path26 = require("node:path");
22479
22547
 
22480
22548
  // src/bootstrap-drift.ts
22481
22549
  function byteComparableSeeds(manifest, cls) {
@@ -23089,13 +23157,13 @@ function registerBootstrapCommands(program3) {
23089
23157
  client: defaultGitHubClient(),
23090
23158
  projectMeta: meta,
23091
23159
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
23092
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
23160
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs27.existsSync)(path2) ? (0, import_node_fs27.readFileSync)(path2, "utf8") : null,
23093
23161
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
23094
23162
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
23095
23163
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
23096
23164
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
23097
23165
  // sanction, which is the pre-#3664 behaviour.
23098
- sanctionedAdmins: (0, import_node_fs26.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
23166
+ sanctionedAdmins: (0, import_node_fs27.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs27.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
23099
23167
  requiredGcpApis: (() => {
23100
23168
  const v = meta?.requiredGcpApis;
23101
23169
  if (Array.isArray(v)) return v;
@@ -23133,12 +23201,12 @@ function registerBootstrapCommands(program3) {
23133
23201
  bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
23134
23202
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
23135
23203
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23136
- if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
23137
- const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
23204
+ if (!(0, import_node_fs27.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
23205
+ const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
23138
23206
  const hubContents = /* @__PURE__ */ new Map();
23139
23207
  for (const s of manifest.seeds) {
23140
23208
  if (s.ownership !== "org" || s.source !== "self") continue;
23141
- hubContents.set(s.target, (0, import_node_fs26.existsSync)(s.target) ? (0, import_node_fs26.readFileSync)(s.target, "utf8") : null);
23209
+ hubContents.set(s.target, (0, import_node_fs27.existsSync)(s.target) ? (0, import_node_fs27.readFileSync)(s.target, "utf8") : null);
23142
23210
  }
23143
23211
  let targets;
23144
23212
  let classOf = (_repo) => "deployable";
@@ -23215,8 +23283,8 @@ function registerBootstrapCommands(program3) {
23215
23283
  return fail(`bootstrap apply: ${e.message}`);
23216
23284
  }
23217
23285
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23218
- if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
23219
- const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
23286
+ if (!(0, import_node_fs27.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
23287
+ const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
23220
23288
  const baseBranch = o.class === "content" ? "main" : "development";
23221
23289
  const slug = parsedRepo.slug;
23222
23290
  const onlyTarget = o.only.trim();
@@ -23227,16 +23295,16 @@ function registerBootstrapCommands(program3) {
23227
23295
  ${known}`);
23228
23296
  }
23229
23297
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
23230
- const readFile9 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
23298
+ const readFile9 = (p) => (0, import_node_fs27.existsSync)(p) ? (0, import_node_fs27.readFileSync)(p, "utf8") : null;
23231
23299
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
23232
23300
  const putSeed = async (target, content, ref, sha) => {
23233
- const tmp = (0, import_node_path25.join)((0, import_node_os9.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
23234
- (0, import_node_fs26.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
23301
+ const tmp = (0, import_node_path26.join)((0, import_node_os10.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
23302
+ (0, import_node_fs27.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
23235
23303
  try {
23236
23304
  await gh(contentPutInputArgs(repo, target, tmp));
23237
23305
  } finally {
23238
23306
  try {
23239
- (0, import_node_fs26.unlinkSync)(tmp);
23307
+ (0, import_node_fs27.unlinkSync)(tmp);
23240
23308
  } catch {
23241
23309
  }
23242
23310
  }
@@ -23488,12 +23556,12 @@ LIVE apply to ${repo}:
23488
23556
  }
23489
23557
 
23490
23558
  // src/stage-commands.ts
23491
- var import_node_fs28 = require("node:fs");
23492
- var import_node_path27 = require("node:path");
23559
+ var import_node_fs29 = require("node:fs");
23560
+ var import_node_path28 = require("node:path");
23493
23561
 
23494
23562
  // src/port-registry.ts
23495
- var import_node_fs27 = require("node:fs");
23496
- var import_node_path26 = require("node:path");
23563
+ var import_node_fs28 = require("node:fs");
23564
+ var import_node_path27 = require("node:path");
23497
23565
 
23498
23566
  // ../infra/port-geometry.mjs
23499
23567
  var PORT_BLOCK = 100;
@@ -23507,8 +23575,8 @@ function nextPortBlock(registry2) {
23507
23575
  return [base, base + PORT_SPAN];
23508
23576
  }
23509
23577
  function loadPortRegistry(path2) {
23510
- if (!(0, import_node_fs27.existsSync)(path2)) return {};
23511
- const raw = JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8"));
23578
+ if (!(0, import_node_fs28.existsSync)(path2)) return {};
23579
+ const raw = JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8"));
23512
23580
  const out = {};
23513
23581
  for (const [key, value] of Object.entries(raw)) {
23514
23582
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -23522,9 +23590,9 @@ function ensurePortRange(repo, path2) {
23522
23590
  const existing = registry2[repo];
23523
23591
  if (existing) return existing;
23524
23592
  const range = nextPortBlock(registry2);
23525
- const raw = (0, import_node_fs27.existsSync)(path2) ? JSON.parse((0, import_node_fs27.readFileSync)(path2, "utf8")) : {};
23593
+ const raw = (0, import_node_fs28.existsSync)(path2) ? JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8")) : {};
23526
23594
  raw[repo] = range;
23527
- (0, import_node_fs27.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
23595
+ (0, import_node_fs28.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
23528
23596
  return range;
23529
23597
  }
23530
23598
  function portCursorSeed(registry2) {
@@ -23546,22 +23614,22 @@ function existingPortRange(repo, registry2) {
23546
23614
  return registry2[repo] ?? null;
23547
23615
  }
23548
23616
  function portRangeInfraAt(root, source) {
23549
- const registryPath = (0, import_node_path26.join)(root, "infra", "port-ranges.json");
23550
- const ddbScriptPath = (0, import_node_path26.join)(root, "infra", "port-ddb.mjs");
23551
- if (!(0, import_node_fs27.existsSync)(registryPath) || !(0, import_node_fs27.existsSync)(ddbScriptPath)) return null;
23617
+ const registryPath = (0, import_node_path27.join)(root, "infra", "port-ranges.json");
23618
+ const ddbScriptPath = (0, import_node_path27.join)(root, "infra", "port-ddb.mjs");
23619
+ if (!(0, import_node_fs28.existsSync)(registryPath) || !(0, import_node_fs28.existsSync)(ddbScriptPath)) return null;
23552
23620
  return { root, source, registryPath, ddbScriptPath };
23553
23621
  }
23554
23622
  function resolvePortRangeInfra(cwd, packageDir) {
23555
23623
  const direct = portRangeInfraAt(cwd, "cwd");
23556
23624
  if (direct) return direct;
23557
- for (let dir = cwd; ; dir = (0, import_node_path26.dirname)(dir)) {
23558
- const sibling = portRangeInfraAt((0, import_node_path26.join)(dir, "MMI-Hub"), "sibling-hub");
23625
+ for (let dir = cwd; ; dir = (0, import_node_path27.dirname)(dir)) {
23626
+ const sibling = portRangeInfraAt((0, import_node_path27.join)(dir, "MMI-Hub"), "sibling-hub");
23559
23627
  if (sibling) return sibling;
23560
- const parent = (0, import_node_path26.dirname)(dir);
23628
+ const parent = (0, import_node_path27.dirname)(dir);
23561
23629
  if (parent === dir) break;
23562
23630
  }
23563
23631
  if (packageDir) {
23564
- const pkgRoot = (0, import_node_path26.join)(packageDir, "..", "..");
23632
+ const pkgRoot = (0, import_node_path27.join)(packageDir, "..", "..");
23565
23633
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
23566
23634
  if (pkgFrom) return pkgFrom;
23567
23635
  }
@@ -23737,8 +23805,8 @@ function registerStageCommands(program3) {
23737
23805
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
23738
23806
  return decideStage({
23739
23807
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
23740
- hasCompose: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), "docker-compose.yml")),
23741
- hasEnvExample: (0, import_node_fs28.existsSync)((0, import_node_path27.join)(process.cwd(), ".env.example"))
23808
+ hasCompose: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), "docker-compose.yml")),
23809
+ hasEnvExample: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), ".env.example"))
23742
23810
  });
23743
23811
  }
23744
23812
  async function fetchStageVaultEnvMerge() {
@@ -24176,10 +24244,10 @@ function registerBoardCommands(program3) {
24176
24244
  }
24177
24245
 
24178
24246
  // src/merge-cleanup.ts
24179
- var import_node_fs29 = require("node:fs");
24247
+ var import_node_fs30 = require("node:fs");
24180
24248
  var import_promises7 = require("node:fs/promises");
24181
- var import_node_path29 = require("node:path");
24182
- var import_node_os10 = require("node:os");
24249
+ var import_node_path30 = require("node:path");
24250
+ var import_node_os11 = require("node:os");
24183
24251
  var import_node_child_process14 = require("node:child_process");
24184
24252
 
24185
24253
  // src/board-advance.ts
@@ -24266,7 +24334,7 @@ function boardAdvanceFailureMessage(result) {
24266
24334
 
24267
24335
  // src/deferred-registry-store.ts
24268
24336
  var import_promises6 = require("node:fs/promises");
24269
- var import_node_path28 = require("node:path");
24337
+ var import_node_path29 = require("node:path");
24270
24338
  var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
24271
24339
  async function atomicWrite(target, contents) {
24272
24340
  const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
@@ -24317,12 +24385,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
24317
24385
  },
24318
24386
  // Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
24319
24387
  write: async (entries) => {
24320
- await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
24388
+ await (0, import_promises6.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
24321
24389
  await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
24322
24390
  },
24323
24391
  // Serialized read-modify-write under the repo-wide lock (#2846).
24324
24392
  update: async (mutate) => {
24325
- await (0, import_promises6.mkdir)((0, import_node_path28.dirname)(registryPath), { recursive: true });
24393
+ await (0, import_promises6.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
24326
24394
  const deadline = Date.now() + opts.maxWaitMs;
24327
24395
  for (; ; ) {
24328
24396
  const guard = await acquireLock(lockPath, opts, deadline);
@@ -24483,7 +24551,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24483
24551
  );
24484
24552
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
24485
24553
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
24486
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path29.dirname)((0, import_node_path29.dirname)(worktreeGitRoot)) : repoRoot2;
24554
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
24487
24555
  const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
24488
24556
  const owners = readWorktreeOwners(primaryRepoRoot);
24489
24557
  const removalNow = Date.now();
@@ -24514,7 +24582,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24514
24582
  const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
24515
24583
  beforeWorktrees,
24516
24584
  startingPath: branch.worktreePath,
24517
- pathExists: (p) => (0, import_node_fs29.existsSync)(p),
24585
+ pathExists: (p) => (0, import_node_fs30.existsSync)(p),
24518
24586
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
24519
24587
  teardownWorktreeStage,
24520
24588
  deferredStore,
@@ -24542,7 +24610,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
24542
24610
  for (const wt of worktreeDirsToRemove) {
24543
24611
  try {
24544
24612
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
24545
- realpath: (path2) => (0, import_node_fs29.realpathSync)(path2)
24613
+ realpath: (path2) => (0, import_node_fs30.realpathSync)(path2)
24546
24614
  });
24547
24615
  if (!cleanupTarget.ok) {
24548
24616
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -24607,13 +24675,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
24607
24675
  const commits = JSON.parse(raw).commits ?? [];
24608
24676
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
24609
24677
  if (!body) return void 0;
24610
- const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path29.join)((0, import_node_os10.tmpdir)(), "mmi-squash-body-"));
24611
- const path2 = (0, import_node_path29.join)(dir, "body.txt");
24612
- (0, import_node_fs29.writeFileSync)(path2, `${body}
24678
+ const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path30.join)((0, import_node_os11.tmpdir)(), "mmi-squash-body-"));
24679
+ const path2 = (0, import_node_path30.join)(dir, "body.txt");
24680
+ (0, import_node_fs30.writeFileSync)(path2, `${body}
24613
24681
  `, "utf8");
24614
24682
  return { path: path2, cleanup: () => {
24615
24683
  try {
24616
- (0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
24684
+ (0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
24617
24685
  } catch {
24618
24686
  }
24619
24687
  } };
@@ -24735,13 +24803,13 @@ var realWorktreeDirRemover = {
24735
24803
  probe: (p) => {
24736
24804
  let st;
24737
24805
  try {
24738
- st = (0, import_node_fs29.lstatSync)(p);
24806
+ st = (0, import_node_fs30.lstatSync)(p);
24739
24807
  } catch {
24740
24808
  return null;
24741
24809
  }
24742
24810
  if (st.isSymbolicLink()) return "link";
24743
24811
  try {
24744
- (0, import_node_fs29.readlinkSync)(p);
24812
+ (0, import_node_fs30.readlinkSync)(p);
24745
24813
  return "link";
24746
24814
  } catch {
24747
24815
  }
@@ -24749,7 +24817,7 @@ var realWorktreeDirRemover = {
24749
24817
  },
24750
24818
  readdir: (p) => {
24751
24819
  try {
24752
- return (0, import_node_fs29.readdirSync)(p);
24820
+ return (0, import_node_fs30.readdirSync)(p);
24753
24821
  } catch {
24754
24822
  return [];
24755
24823
  }
@@ -24758,9 +24826,9 @@ var realWorktreeDirRemover = {
24758
24826
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
24759
24827
  detachLink: (p) => {
24760
24828
  try {
24761
- (0, import_node_fs29.rmdirSync)(p);
24829
+ (0, import_node_fs30.rmdirSync)(p);
24762
24830
  } catch {
24763
- (0, import_node_fs29.unlinkSync)(p);
24831
+ (0, import_node_fs30.unlinkSync)(p);
24764
24832
  }
24765
24833
  },
24766
24834
  removeTree: (p) => (0, import_promises7.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
@@ -24793,9 +24861,9 @@ async function worktreeHasStageState(worktreePath) {
24793
24861
  }
24794
24862
  }
24795
24863
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
24796
- if (!(0, import_node_fs29.existsSync)(statePath)) return false;
24864
+ if (!(0, import_node_fs30.existsSync)(statePath)) return false;
24797
24865
  try {
24798
- const state = JSON.parse((0, import_node_fs29.readFileSync)(statePath, "utf8"));
24866
+ const state = JSON.parse((0, import_node_fs30.readFileSync)(statePath, "utf8"));
24799
24867
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
24800
24868
  return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
24801
24869
  } catch {
@@ -25019,9 +25087,9 @@ async function fetchRestCorePool(gh = defaultGhApi) {
25019
25087
  }
25020
25088
 
25021
25089
  // src/worktree-lifecycle-commands.ts
25022
- var import_node_fs30 = require("node:fs");
25090
+ var import_node_fs31 = require("node:fs");
25023
25091
  var import_promises8 = require("node:fs/promises");
25024
- var import_node_path30 = require("node:path");
25092
+ var import_node_path31 = require("node:path");
25025
25093
  var GH_TIMEOUT_MS = 2e4;
25026
25094
  var DEFAULT_BASE = "origin/development";
25027
25095
  var DEFAULT_REMOTE = "origin";
@@ -25093,6 +25161,16 @@ function orphanLandSteps(lostBranch, hasStage) {
25093
25161
  steps.push("prune worktree metadata");
25094
25162
  return steps;
25095
25163
  }
25164
+ function detachedLandSteps(hasStage) {
25165
+ const steps = [];
25166
+ if (hasStage) steps.push("stop spawned dev stage");
25167
+ steps.push("remove worktree (detached HEAD)");
25168
+ steps.push("prune worktree metadata");
25169
+ return steps;
25170
+ }
25171
+ function shouldContinueLandCleanup(removeStatus) {
25172
+ return removeStatus === "removed";
25173
+ }
25096
25174
  function classifyStaleLeaks(input) {
25097
25175
  const protectedBranches = input.protectedBranches ?? PROTECTED_BRANCHES2;
25098
25176
  const leaks = [];
@@ -25157,7 +25235,7 @@ function classifyStaleLeaks(input) {
25157
25235
  var defaultOrphanDirScanDeps = {
25158
25236
  listDirs: (root) => {
25159
25237
  try {
25160
- return (0, import_node_fs30.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path30.join)(root, e.name));
25238
+ return (0, import_node_fs31.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path31.join)(root, e.name));
25161
25239
  } catch {
25162
25240
  return [];
25163
25241
  }
@@ -25302,15 +25380,21 @@ function registerWorktreeCommands(program3) {
25302
25380
  try {
25303
25381
  const wtPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
25304
25382
  const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
25305
- const branch = headBorn ? (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim() : (await execFileP2("git", ["symbolic-ref", "--quiet", "--short", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
25383
+ const symbolicBranch = (await execFileP2(
25384
+ "git",
25385
+ ["-C", wtPath || ".", "symbolic-ref", "--quiet", "--short", "HEAD"],
25386
+ { timeout: GIT_TIMEOUT_MS }
25387
+ ).catch(() => ({ stdout: "" }))).stdout.trim();
25388
+ const detached = headBorn && !symbolicBranch;
25389
+ const branch = symbolicBranch || (detached ? "HEAD" : "");
25306
25390
  if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
25307
- const gitFile = (0, import_node_path30.join)(wtPath, ".git");
25308
- const isLinked = (0, import_node_fs30.existsSync)(gitFile) && (0, import_node_fs30.statSync)(gitFile).isFile();
25391
+ const gitFile = (0, import_node_path31.join)(wtPath, ".git");
25392
+ const isLinked = (0, import_node_fs31.existsSync)(gitFile) && (0, import_node_fs31.statSync)(gitFile).isFile();
25309
25393
  if (apply && !isLinked) {
25310
25394
  return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
25311
25395
  }
25312
25396
  const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
25313
- const primaryCheckout = commonDir ? (0, import_node_path30.dirname)(commonDir) : wtPath;
25397
+ const primaryCheckout = commonDir ? (0, import_node_path31.dirname)(commonDir) : wtPath;
25314
25398
  const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
25315
25399
  const orphan = classifyOrphanedWorktree({
25316
25400
  branch,
@@ -25323,7 +25407,7 @@ function registerWorktreeCommands(program3) {
25323
25407
  }
25324
25408
  const stageSummary = readStageSummary(wtPath);
25325
25409
  const hasStage = stageSummary != null;
25326
- const steps = orphan.orphan ? orphanLandSteps(orphan.lostBranch, hasStage) : landSteps(branch, true, hasStage);
25410
+ const steps = detached ? detachedLandSteps(hasStage) : orphan.orphan ? orphanLandSteps(orphan.lostBranch, hasStage) : landSteps(branch, true, hasStage);
25327
25411
  const plan = {
25328
25412
  command: "worktree land",
25329
25413
  branch,
@@ -25332,13 +25416,15 @@ function registerWorktreeCommands(program3) {
25332
25416
  remote: o.remote,
25333
25417
  hasStage,
25334
25418
  steps,
25335
- ...orphan.orphan ? { orphan: { kind: orphan.kind, lostBranch: orphan.lostBranch, reason: orphan.reason } } : {}
25419
+ ...orphan.orphan ? { orphan: { kind: orphan.kind, lostBranch: orphan.lostBranch, reason: orphan.reason } } : {},
25420
+ ...detached ? { detached: true } : {}
25336
25421
  };
25337
25422
  if (!apply) {
25338
25423
  const preview = { dryRun: true, ...plan, ...o.keepRemote ? { keepRemote: true } : {} };
25339
25424
  if (o.json) return console.log(JSON.stringify(preview, null, 2));
25340
25425
  const lines = [`worktree land: dry-run (pass --apply to execute)`, ` branch: ${branch}`, ` worktree: ${wtPath}`];
25341
- if (orphan.orphan) lines.push(` orphaned: ${orphan.reason} \u2014 no branch refs will be deleted`);
25426
+ if (detached) lines.push(" detached HEAD: no branch refs will be deleted");
25427
+ else if (orphan.orphan) lines.push(` orphaned: ${orphan.reason} \u2014 no branch refs will be deleted`);
25342
25428
  if (hasStage) lines.push(` stage: port ${stageSummary.port} (will be stopped)`);
25343
25429
  if (o.keepRemote && !orphan.orphan) lines.push(` remote branch: kept (--keep-remote)`);
25344
25430
  for (const s of steps) lines.push(` - ${s}`);
@@ -25360,15 +25446,16 @@ function registerWorktreeCommands(program3) {
25360
25446
  unreferenced: await isCommitUnreferenced(orphanTip, async (args) => (await execFileP2("git", ["-C", primaryCheckout, ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
25361
25447
  recoverCommand: `git -C "${primaryCheckout}" branch <name> ${orphanTip}`
25362
25448
  } : void 0;
25363
- const landPrs = orphan.orphan ? void 0 : await fetchLandBranchPrs(branch);
25449
+ const treeOnly = orphan.orphan || detached;
25450
+ const landPrs = treeOnly ? void 0 : await fetchLandBranchPrs(branch);
25364
25451
  const mergeVerdict = classifyLandBranchMergeState(landPrs);
25365
- const landRefs = orphan.orphan ? { action: "proceed" } : decideWorktreeLandRefCleanup({ branch, prs: landPrs, keepRemote: Boolean(o.keepRemote) });
25452
+ const landRefs = treeOnly ? { action: "proceed" } : decideWorktreeLandRefCleanup({ branch, prs: landPrs, keepRemote: Boolean(o.keepRemote) });
25366
25453
  if (landRefs.action === "refuse") {
25367
25454
  return fail(`worktree land: ${landRefs.message}`, { code: ERROR_CODES.ERR_BAD_ENUM });
25368
25455
  }
25369
25456
  const keepRemoteBranch = Boolean(o.keepRemote) || landRefs.action === "keep-remote";
25370
25457
  if (landRefs.action === "keep-remote") console.warn(`worktree land: ${landRefs.message}.`);
25371
- const reportedMergeState = orphan.orphan ? "not-checked" : mergeVerdict.state;
25458
+ const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
25372
25459
  const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
25373
25460
  const report = [];
25374
25461
  if (hasStage) {
@@ -25388,6 +25475,24 @@ function registerWorktreeCommands(program3) {
25388
25475
  step: "remove worktree",
25389
25476
  status: removeOutcome.status === "removed" ? removeOutcome.recovery ? `done (${removeOutcome.recovery})` : "done" : `failed: ${removeOutcome.error ?? "lock held"} \u2014 run: git -C "${primaryCheckout}" worktree remove --force "${wtPath}"`
25390
25477
  });
25478
+ if (!shouldContinueLandCleanup(removeOutcome.status)) {
25479
+ const result2 = {
25480
+ dryRun: false,
25481
+ ...plan,
25482
+ ...o.keepRemote ? { keepRemote: true } : {},
25483
+ mergeState: reportedMergeState,
25484
+ prNumbers: mergeVerdict.numbers,
25485
+ cleanupState: "partial",
25486
+ report
25487
+ };
25488
+ if (o.json) console.log(JSON.stringify(result2, null, 2));
25489
+ else {
25490
+ console.error(`worktree land: cleanup stopped after the worktree removal failed; branch refs and metadata were left untouched`);
25491
+ for (const row of report) console.log(` ${row.step}: ${row.status}`);
25492
+ }
25493
+ process.exitCode = 1;
25494
+ return;
25495
+ }
25391
25496
  const landActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: wtPath });
25392
25497
  appendWorktreeEvent(primaryCheckout, {
25393
25498
  action: removeOutcome.status === "removed" ? "removed" : "failed",
@@ -25402,8 +25507,11 @@ function registerWorktreeCommands(program3) {
25402
25507
  reason: orphan.orphan ? `orphaned worktree \u2014 ${orphan.reason}${lastCommit ? `; last commit ${lastCommit.oid}${lastCommit.unreferenced ? " (on no ref)" : ""}` : ""}` : describeLandMergeState(mergeVerdict)
25403
25508
  });
25404
25509
  if (removeOutcome.status === "removed") dropWorktreeOwner(primaryCheckout, wtPath);
25405
- if (orphan.orphan) {
25406
- report.push({ step: "delete branch refs", status: `skipped: orphaned worktree \u2014 ${orphan.reason}` });
25510
+ if (treeOnly) {
25511
+ report.push({
25512
+ step: "delete branch refs",
25513
+ status: detached ? "skipped: detached HEAD has no branch refs" : `skipped: orphaned worktree \u2014 ${orphan.reason}`
25514
+ });
25407
25515
  if (lastCommit) {
25408
25516
  report.push({
25409
25517
  step: "last commit",
@@ -25431,12 +25539,12 @@ function registerWorktreeCommands(program3) {
25431
25539
  report
25432
25540
  };
25433
25541
  if (o.json) return console.log(JSON.stringify(result, null, 2));
25434
- console.log(orphan.orphan ? `worktree land: removed orphaned worktree ${wtPath} (${orphan.reason}); no branch refs touched` : `worktree land: cleaned up branch ${branch} (${describeLandMergeState(mergeVerdict)})`);
25542
+ console.log(treeOnly ? `worktree land: removed ${detached ? "detached" : "orphaned"} worktree ${wtPath}${detached ? "" : ` (${orphan.reason})`}; no branch refs touched` : `worktree land: cleaned up branch ${branch} (${describeLandMergeState(mergeVerdict)})`);
25435
25543
  for (const r of report) console.log(` ${r.step}: ${r.status}`);
25436
25544
  if (lastCommit?.unreferenced) {
25437
25545
  console.warn(`worktree land: commit ${lastCommit.oid} was on no ref but this worktree's reflog \u2014 normal after a squash merge, but if that work was NOT merged, recover it now: ${lastCommit.recoverCommand}`);
25438
25546
  }
25439
- if (!orphan.orphan && mergeVerdict.state !== "merged") {
25547
+ if (!treeOnly && mergeVerdict.state !== "merged") {
25440
25548
  console.warn(`worktree land: '${branch}' was cleaned up but NOT merged \u2014 ${describeLandMergeState(mergeVerdict)}. Do not report this work as shipped.`);
25441
25549
  }
25442
25550
  if (report.some((r) => r.status.startsWith("failed"))) process.exitCode = 1;
@@ -25497,10 +25605,10 @@ async function gatherWorktreeContext() {
25497
25605
  if (s) stages.push({ path: wt.path, port: s.port });
25498
25606
  }
25499
25607
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
25500
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
25608
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path31.dirname)((0, import_node_path31.dirname)(worktreeGitRoot)) : repoRoot2;
25501
25609
  const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
25502
25610
  let orphanDirs = [];
25503
- if ((0, import_node_fs30.existsSync)(wtRoot)) {
25611
+ if ((0, import_node_fs31.existsSync)(wtRoot)) {
25504
25612
  orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
25505
25613
  ...defaultOrphanDirScanDeps,
25506
25614
  listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
@@ -25523,7 +25631,7 @@ async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
25523
25631
  }
25524
25632
 
25525
25633
  // src/issue-commands.ts
25526
- var import_node_fs31 = require("node:fs");
25634
+ var import_node_fs32 = require("node:fs");
25527
25635
  var import_node_crypto5 = require("node:crypto");
25528
25636
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
25529
25637
  var ReparentConflictError = class extends Error {
@@ -25541,7 +25649,7 @@ async function editIssue(client, options, deps = {}) {
25541
25649
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
25542
25650
  const patch = {};
25543
25651
  let bodyChanged = false;
25544
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs31.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
25652
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs32.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
25545
25653
  if (options.titleFile !== void 0) {
25546
25654
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
25547
25655
  } else if (options.title !== void 0) {
@@ -26104,7 +26212,7 @@ function extendCreateCommand(issue2, batchAttach) {
26104
26212
  if (opts.batch) {
26105
26213
  let specs;
26106
26214
  try {
26107
- const raw = (0, import_node_fs31.readFileSync)(opts.batch, "utf8");
26215
+ const raw = (0, import_node_fs32.readFileSync)(opts.batch, "utf8");
26108
26216
  specs = JSON.parse(raw);
26109
26217
  if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
26110
26218
  } catch (e) {
@@ -26178,8 +26286,8 @@ ${lines}`, {
26178
26286
  }
26179
26287
 
26180
26288
  // src/train-commands.ts
26181
- var import_node_fs32 = require("node:fs");
26182
- var import_node_path31 = require("node:path");
26289
+ var import_node_fs33 = require("node:fs");
26290
+ var import_node_path32 = require("node:path");
26183
26291
 
26184
26292
  // src/train-status.ts
26185
26293
  function buildTrainStatusReport(input) {
@@ -26219,7 +26327,7 @@ function formatTrainStatus(r) {
26219
26327
  // src/train-commands.ts
26220
26328
  function readRepoVersion() {
26221
26329
  try {
26222
- return JSON.parse((0, import_node_fs32.readFileSync)((0, import_node_path31.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
26330
+ return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path32.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
26223
26331
  } catch {
26224
26332
  return void 0;
26225
26333
  }
@@ -26365,11 +26473,12 @@ function registerDeployCommands(program3) {
26365
26473
  }
26366
26474
 
26367
26475
  // src/discovery-commands.ts
26368
- var import_node_fs33 = require("node:fs");
26369
- var import_node_os11 = require("node:os");
26370
- var import_node_path32 = require("node:path");
26476
+ var import_node_fs34 = require("node:fs");
26477
+ var import_node_os12 = require("node:os");
26478
+ var import_node_path33 = require("node:path");
26371
26479
  var GC_GH_TIMEOUT_MS3 = 2e4;
26372
26480
  async function collectStatus() {
26481
+ const repo = await resolveRepo();
26373
26482
  let branch = "";
26374
26483
  try {
26375
26484
  branch = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
@@ -26398,7 +26507,7 @@ async function collectStatus() {
26398
26507
  }
26399
26508
  let claimedItems = [];
26400
26509
  try {
26401
- const cfg = await loadConfig();
26510
+ const cfg = await loadConfigOrDiscover();
26402
26511
  if (cfg.sagaApiUrl) {
26403
26512
  const report = await readBoard({ config: cfg });
26404
26513
  claimedItems = report.primary.userOwned.map((item) => ({
@@ -26421,7 +26530,7 @@ async function collectStatus() {
26421
26530
  } catch {
26422
26531
  stage = { running: false };
26423
26532
  }
26424
- return { branch, worktrees, myOpenPrs, claimedItems, stage };
26533
+ return { repo, branch, worktrees, myOpenPrs, claimedItems, stage };
26425
26534
  }
26426
26535
  var PRIORITY_RANK = {
26427
26536
  Urgent: 0,
@@ -26429,14 +26538,14 @@ var PRIORITY_RANK = {
26429
26538
  Medium: 2,
26430
26539
  Low: 3
26431
26540
  };
26432
- async function recommendNext(deps) {
26433
- const load = deps?.loadConfig ?? loadConfig;
26541
+ async function recommendNext(repo, deps) {
26542
+ const load = deps?.loadConfig ?? loadConfigForRepo;
26434
26543
  const reader = deps?.readBoard ?? readBoard;
26435
- const cfg = await load();
26544
+ const cfg = await load(repo);
26436
26545
  if (!cfg.sagaApiUrl) throw new Error("Hub API URL not configured \u2014 the board was NOT read (run `mmi-cli doctor`)");
26437
26546
  let report;
26438
26547
  try {
26439
- report = await reader({ config: cfg });
26548
+ report = await reader({ config: cfg, repo });
26440
26549
  } catch (e) {
26441
26550
  throw new Error(
26442
26551
  `board unreachable \u2014 ${e.message}. This is NOT an empty board: confirm the ACTIVE gh account can see this repo (\`gh auth status\`, \`gh repo view <owner/repo>\`; \`gh auth switch --user <other>\` when more than one account is present).`
@@ -26542,10 +26651,10 @@ async function collectOnboardStatus() {
26542
26651
  else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
26543
26652
  else nextCommand = "mmi-cli board read \u2014 no claimable items found";
26544
26653
  }
26545
- const home = (0, import_node_os11.homedir)();
26654
+ const home = (0, import_node_os12.homedir)();
26546
26655
  const plugin = onboardPluginGate({
26547
- readKnown: () => readFileSyncSafe((0, import_node_path32.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs33.readFileSync),
26548
- readSettings: () => readFileSyncSafe((0, import_node_path32.join)(home, ".claude", "settings.json"), import_node_fs33.readFileSync)
26656
+ readKnown: () => readFileSyncSafe((0, import_node_path33.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs34.readFileSync),
26657
+ readSettings: () => readFileSyncSafe((0, import_node_path33.join)(home, ".claude", "settings.json"), import_node_fs34.readFileSync)
26549
26658
  });
26550
26659
  return { track, board, registry: registry2, secrets, plugin, nextCommand };
26551
26660
  }
@@ -26556,6 +26665,7 @@ function registerDiscoveryCommands(program3) {
26556
26665
  if (o.json) {
26557
26666
  console.log(JSON.stringify(report, null, 2));
26558
26667
  } else {
26668
+ console.log(`repo: ${report.repo ?? "unknown"}`);
26559
26669
  console.log(`branch: ${report.branch}`);
26560
26670
  console.log(`worktrees: ${report.worktrees.length} linked`);
26561
26671
  console.log(`my open PRs: ${report.myOpenPrs.length}`);
@@ -26566,9 +26676,9 @@ function registerDiscoveryCommands(program3) {
26566
26676
  fail(`status: ${e.message}`);
26567
26677
  }
26568
26678
  });
26569
- program3.command("next").description("recommend the next actionable board item (claimable, unblocked, priority-ranked)").action(async () => {
26679
+ program3.command("next").description("recommend the next actionable board item (claimable, unblocked, priority-ranked)").option("--repo <owner/repo>", "current repo (defaults to git origin)").action(async (o) => {
26570
26680
  try {
26571
- const item = await recommendNext();
26681
+ const item = await recommendNext(o.repo);
26572
26682
  if (!item) {
26573
26683
  console.log("No claimable board items found (board read OK).");
26574
26684
  return;
@@ -28688,17 +28798,17 @@ function parseOriginRepo(remoteUrl) {
28688
28798
  }
28689
28799
  function ghHostsConfigPath(env, platform2) {
28690
28800
  const sep2 = platform2 === "win32" ? "\\" : "/";
28691
- const join29 = (...parts) => parts.join(sep2);
28801
+ const join30 = (...parts) => parts.join(sep2);
28692
28802
  const explicit = env.GH_CONFIG_DIR?.trim();
28693
- if (explicit) return join29(explicit, "hosts.yml");
28803
+ if (explicit) return join30(explicit, "hosts.yml");
28694
28804
  if (platform2 === "win32") {
28695
28805
  const appData = (env.AppData ?? env.APPDATA)?.trim();
28696
- return appData ? join29(appData, "GitHub CLI", "hosts.yml") : void 0;
28806
+ return appData ? join30(appData, "GitHub CLI", "hosts.yml") : void 0;
28697
28807
  }
28698
28808
  const xdg = env.XDG_CONFIG_HOME?.trim();
28699
- if (xdg) return join29(xdg, "gh", "hosts.yml");
28809
+ if (xdg) return join30(xdg, "gh", "hosts.yml");
28700
28810
  const home = env.HOME?.trim();
28701
- return home ? join29(home, ".config", "gh", "hosts.yml") : void 0;
28811
+ return home ? join30(home, ".config", "gh", "hosts.yml") : void 0;
28702
28812
  }
28703
28813
  function parseGhHostsAccounts(yaml, host = "github.com") {
28704
28814
  let hostIndent = null;
@@ -28748,9 +28858,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
28748
28858
  }
28749
28859
 
28750
28860
  // src/doctor-io.ts
28751
- var import_node_fs34 = require("node:fs");
28752
- var import_node_os12 = require("node:os");
28753
- var import_node_path33 = require("node:path");
28861
+ var import_node_fs35 = require("node:fs");
28862
+ var import_node_os13 = require("node:os");
28863
+ var import_node_path34 = require("node:path");
28754
28864
  var import_node_child_process15 = require("node:child_process");
28755
28865
  var import_node_util8 = require("node:util");
28756
28866
  var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
@@ -28758,7 +28868,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
28758
28868
  function installedClaudePluginVersion() {
28759
28869
  try {
28760
28870
  const file = JSON.parse(
28761
- (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
28871
+ (0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
28762
28872
  );
28763
28873
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
28764
28874
  if (versions.length === 0) return void 0;
@@ -28769,7 +28879,7 @@ function installedClaudePluginVersion() {
28769
28879
  }
28770
28880
  function manifestVersion(path2) {
28771
28881
  try {
28772
- const manifest = JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
28882
+ const manifest = JSON.parse((0, import_node_fs35.readFileSync)(path2, "utf8"));
28773
28883
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
28774
28884
  } catch {
28775
28885
  return void 0;
@@ -28779,17 +28889,17 @@ function installedSurfacePluginVersion(surface) {
28779
28889
  const token = surfaceToken(surface);
28780
28890
  if (token === "kilo") {
28781
28891
  try {
28782
- const stamp = (0, import_node_fs34.readFileSync)((0, import_node_path33.join)((0, import_node_os12.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
28892
+ const stamp = (0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
28783
28893
  return stamp || void 0;
28784
28894
  } catch {
28785
28895
  return void 0;
28786
28896
  }
28787
28897
  }
28788
28898
  if (token === "cursor") {
28789
- return manifestVersion((0, import_node_path33.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
28899
+ return manifestVersion((0, import_node_path34.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
28790
28900
  }
28791
28901
  if (token === "kimi") {
28792
- return manifestVersion((0, import_node_path33.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
28902
+ return manifestVersion((0, import_node_path34.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
28793
28903
  }
28794
28904
  if (token === "claude") return installedClaudePluginVersion();
28795
28905
  if (token !== "codex") return void 0;
@@ -28827,13 +28937,13 @@ function worktreeRootSync() {
28827
28937
  }
28828
28938
  var gitignorePath = () => {
28829
28939
  const root = worktreeRootSync();
28830
- return root === null ? null : (0, import_node_path33.join)(root, ".gitignore");
28940
+ return root === null ? null : (0, import_node_path34.join)(root, ".gitignore");
28831
28941
  };
28832
28942
  function readGitignore() {
28833
28943
  const path2 = gitignorePath();
28834
28944
  if (path2 === null) return null;
28835
28945
  try {
28836
- return (0, import_node_fs34.readFileSync)(path2, "utf8");
28946
+ return (0, import_node_fs35.readFileSync)(path2, "utf8");
28837
28947
  } catch {
28838
28948
  return null;
28839
28949
  }
@@ -28842,7 +28952,7 @@ function writeGitignore(content) {
28842
28952
  const path2 = gitignorePath();
28843
28953
  if (path2 === null) return false;
28844
28954
  try {
28845
- (0, import_node_fs34.writeFileSync)(path2, content, "utf8");
28955
+ (0, import_node_fs35.writeFileSync)(path2, content, "utf8");
28846
28956
  return true;
28847
28957
  } catch {
28848
28958
  return false;
@@ -28866,7 +28976,7 @@ async function repoRoot() {
28866
28976
  }
28867
28977
  function hasRepoLocalWorktrees() {
28868
28978
  const root = worktreeRootSync();
28869
- return root !== null && (0, import_node_fs34.existsSync)((0, import_node_path33.join)(root, ".worktrees"));
28979
+ return root !== null && (0, import_node_fs35.existsSync)((0, import_node_path34.join)(root, ".worktrees"));
28870
28980
  }
28871
28981
 
28872
28982
  // src/index.ts
@@ -28902,8 +29012,8 @@ ${r.stderr ?? ""}`).catch(() => "");
28902
29012
  function ghMultiAccountCaveat(announcedLogin) {
28903
29013
  try {
28904
29014
  const hostsPath = ghHostsConfigPath(process.env, process.platform);
28905
- if (!hostsPath || !(0, import_node_fs35.existsSync)(hostsPath)) return void 0;
28906
- return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs35.readFileSync)(hostsPath, "utf8")));
29015
+ if (!hostsPath || !(0, import_node_fs36.existsSync)(hostsPath)) return void 0;
29016
+ return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs36.readFileSync)(hostsPath, "utf8")));
28907
29017
  } catch {
28908
29018
  return void 0;
28909
29019
  }
@@ -28911,12 +29021,12 @@ function ghMultiAccountCaveat(announcedLogin) {
28911
29021
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
28912
29022
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
28913
29023
  function envHealLockPath(home) {
28914
- return (0, import_node_path34.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
29024
+ return (0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
28915
29025
  }
28916
29026
  async function withEnvHealLock(what, run) {
28917
29027
  try {
28918
29028
  return await withFileLock(
28919
- envHealLockPath((0, import_node_os13.homedir)()),
29029
+ envHealLockPath((0, import_node_os14.homedir)()),
28920
29030
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
28921
29031
  run
28922
29032
  );
@@ -29013,7 +29123,7 @@ function mmiDoctorDeps(opts = {}) {
29013
29123
  const configRoot = surfaceConfigRoot(surface);
29014
29124
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
29015
29125
  const plan = buildPluginCachePlan(
29016
- (0, import_node_os13.homedir)(),
29126
+ (0, import_node_os14.homedir)(),
29017
29127
  running,
29018
29128
  pluginCacheFsDeps(configRoot, () => 0),
29019
29129
  { configRoot, includeStaging: surface !== "codex" }
@@ -29034,15 +29144,31 @@ function mmiDoctorDeps(opts = {}) {
29034
29144
  marketplaceRows: () => {
29035
29145
  try {
29036
29146
  if (detectSurface(process.env) === "codex") return [];
29037
- const home = (0, import_node_os13.homedir)();
29038
- return marketplaceRows(
29147
+ const home = (0, import_node_os14.homedir)();
29148
+ const rows = marketplaceRows(
29039
29149
  MMI_MARKETPLACE_NAME,
29040
- readFileSyncSafe((0, import_node_path34.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs35.readFileSync),
29041
- readFileSyncSafe((0, import_node_path34.join)(home, ".claude", "settings.json"), import_node_fs35.readFileSync),
29150
+ readFileSyncSafe((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs36.readFileSync),
29151
+ readFileSyncSafe((0, import_node_path35.join)(home, ".claude", "settings.json"), import_node_fs36.readFileSync),
29042
29152
  // #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
29043
29153
  // edit. Unconditional — it is a fact about mmi-cli, not about the lane this run is on.
29044
29154
  true
29045
29155
  );
29156
+ const pending = readMarketplacePinPending(
29157
+ (0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
29158
+ MMI_MARKETPLACE_NAME
29159
+ );
29160
+ if (!pending) return rows;
29161
+ return rows.map((row) => {
29162
+ if (row.id !== "marketplace-catalog-ref" || row.ok) return row;
29163
+ const { fix: _fix, reportOnly: _reportOnly, ...rest } = row;
29164
+ return {
29165
+ ...rest,
29166
+ ok: true,
29167
+ warn: true,
29168
+ detail: `pending restart \u2014 doctor pinned main at ${pending.at}; restart Claude Code to load it`,
29169
+ verbose: [...row.verbose ?? [], "known_marketplaces.json was rewritten by the running Claude host after the verified doctor write"]
29170
+ };
29171
+ });
29046
29172
  } catch {
29047
29173
  return [];
29048
29174
  }
@@ -29052,7 +29178,13 @@ function mmiDoctorDeps(opts = {}) {
29052
29178
  healMarketplacePins: () => {
29053
29179
  try {
29054
29180
  if (detectSurface(process.env) === "codex") return void 0;
29055
- return applyOrgMarketplacePins((0, import_node_path34.join)((0, import_node_os13.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE), [MMI_MARKETPLACE_NAME]);
29181
+ const home = (0, import_node_os14.homedir)();
29182
+ const names = [MMI_MARKETPLACE_NAME];
29183
+ const result = applyOrgMarketplacePins((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
29184
+ if (result?.startsWith("pinned ")) {
29185
+ writeMarketplacePinPending((0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
29186
+ }
29187
+ return result;
29056
29188
  } catch {
29057
29189
  return void 0;
29058
29190
  }
@@ -29258,19 +29390,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
29258
29390
  });
29259
29391
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
29260
29392
  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) => {
29261
- const path2 = (0, import_node_path34.join)(process.cwd(), ".gitignore");
29262
- const current = (0, import_node_fs35.existsSync)(path2) ? (0, import_node_fs35.readFileSync)(path2, "utf8") : null;
29393
+ const path2 = (0, import_node_path35.join)(process.cwd(), ".gitignore");
29394
+ const current = (0, import_node_fs36.existsSync)(path2) ? (0, import_node_fs36.readFileSync)(path2, "utf8") : null;
29263
29395
  const plan = planManagedGitignore(current);
29264
29396
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
29265
29397
  if (opts.json) {
29266
- if (opts.write && plan.changed) (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
29398
+ if (opts.write && plan.changed) (0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
29267
29399
  console.log(JSON.stringify(plan, null, 2));
29268
29400
  if (!opts.write && plan.changed) process.exitCode = 1;
29269
29401
  return;
29270
29402
  }
29271
29403
  if (opts.write) {
29272
29404
  if (plan.changed) {
29273
- (0, import_node_fs35.writeFileSync)(path2, plan.content, "utf8");
29405
+ (0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
29274
29406
  console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
29275
29407
  } else {
29276
29408
  console.log("mmi-cli org rules gitignore: up to date");
@@ -29425,8 +29557,8 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
29425
29557
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
29426
29558
  let root;
29427
29559
  if (o.root !== void 0) {
29428
- root = (0, import_node_path34.resolve)(o.root);
29429
- if (!(0, import_node_fs35.existsSync)(root) || !(0, import_node_fs35.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
29560
+ root = (0, import_node_path35.resolve)(o.root);
29561
+ if (!(0, import_node_fs36.existsSync)(root) || !(0, import_node_fs36.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
29430
29562
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
29431
29563
  if (isPathUnderDirectory(gcRepoRoot, root)) {
29432
29564
  return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
@@ -29491,7 +29623,7 @@ async function primaryCheckoutRoot(from) {
29491
29623
  return primaryCheckoutRootOf(async (args) => (await execFileP2("git", ["-C", from, ...args], { timeout: GIT_TIMEOUT_MS })).stdout);
29492
29624
  }
29493
29625
  async function unprovenWorktreeReason(wtPath, repoRoot2) {
29494
- if (!(0, import_node_fs35.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
29626
+ if (!(0, import_node_fs36.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
29495
29627
  const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
29496
29628
  const registered = parseWorktreePorcelainEntries(porcelain);
29497
29629
  if (!registered.length) {
@@ -29505,6 +29637,7 @@ async function unprovenWorktreeReason(wtPath, repoRoot2) {
29505
29637
  function makeProvisionDeps(worktreeRoot, quiet, log) {
29506
29638
  return {
29507
29639
  runInstall: (command, cwd) => runWorktreeInstall(command, cwd, quiet),
29640
+ validateInstall: (cwd) => runWorktreeInstall("npm ls --depth=0 --json", cwd, true).then(() => true).catch(() => false),
29508
29641
  primaryCheckout: () => primaryCheckoutRoot(worktreeRoot),
29509
29642
  log
29510
29643
  };
@@ -29512,26 +29645,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
29512
29645
  function acquireWorktreeSetupLock(worktreeRoot) {
29513
29646
  const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
29514
29647
  const take = () => {
29515
- const fd = (0, import_node_fs35.openSync)(lockPath, "wx");
29648
+ const fd = (0, import_node_fs36.openSync)(lockPath, "wx");
29516
29649
  try {
29517
- (0, import_node_fs35.writeSync)(fd, String(Date.now()));
29650
+ (0, import_node_fs36.writeSync)(fd, String(Date.now()));
29518
29651
  } finally {
29519
- (0, import_node_fs35.closeSync)(fd);
29652
+ (0, import_node_fs36.closeSync)(fd);
29520
29653
  }
29521
29654
  return () => {
29522
29655
  try {
29523
- (0, import_node_fs35.rmSync)(lockPath, { force: true });
29656
+ (0, import_node_fs36.rmSync)(lockPath, { force: true });
29524
29657
  } catch {
29525
29658
  }
29526
29659
  };
29527
29660
  };
29528
29661
  try {
29529
- (0, import_node_fs35.mkdirSync)((0, import_node_path34.dirname)(lockPath), { recursive: true });
29662
+ (0, import_node_fs36.mkdirSync)((0, import_node_path35.dirname)(lockPath), { recursive: true });
29530
29663
  return take();
29531
29664
  } catch {
29532
29665
  try {
29533
- if (Date.now() - (0, import_node_fs35.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
29534
- (0, import_node_fs35.rmSync)(lockPath, { force: true });
29666
+ if (Date.now() - (0, import_node_fs36.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
29667
+ (0, import_node_fs36.rmSync)(lockPath, { force: true });
29535
29668
  return take();
29536
29669
  }
29537
29670
  } catch {
@@ -29587,22 +29720,52 @@ withExamples(mutating(
29587
29720
  const localOnly = preferRemote && base !== preferRemote ? ` (no ${preferRemote} \u2014 local ref)` : "";
29588
29721
  console.error(` base ${base} ${baseSha}${localOnly}`);
29589
29722
  }
29590
- step = `git worktree add ${wtPath}`;
29591
- await addWorktreeRobust(wtPath, branch, base, {
29592
- git: async (args) => (await execFileP2("git", args, { timeout: GH_MUTATION_TIMEOUT_MS })).stdout,
29593
- revParse: async (ref) => {
29594
- try {
29595
- return (await execFileP2("git", ["rev-parse", "--verify", ref], { timeout: GIT_TIMEOUT_MS })).stdout.trim() || void 0;
29596
- } catch {
29597
- return void 0;
29723
+ const registered = parseWorktreePorcelainEntries((await execFileP2(
29724
+ "git",
29725
+ ["-C", repoRoot2, "worktree", "list", "--porcelain"],
29726
+ { timeout: GIT_TIMEOUT_MS }
29727
+ )).stdout);
29728
+ const exact = registered.find((entry) => samePath(entry.path, wtPath));
29729
+ let resumed = false;
29730
+ if (exact) {
29731
+ step = `resume existing worktree ${wtPath}`;
29732
+ if (exact.branch !== branch) {
29733
+ return fail(`worktree create: ${wtPath} is already registered for '${exact.branch ?? "detached HEAD"}', not '${branch}'`);
29734
+ }
29735
+ const status = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
29736
+ if (status) return fail(`worktree create: refusing to resume ${wtPath} \u2014 it has uncommitted changes`);
29737
+ const head = await revParseRef(`refs/heads/${branch}`);
29738
+ const baseOid = await revParseRef(base);
29739
+ if (!head || !baseOid) return fail(`worktree create: could not prove '${branch}' and '${base}' before resume`);
29740
+ const canFastForward = await execFileP2(
29741
+ "git",
29742
+ ["-C", repoRoot2, "merge-base", "--is-ancestor", head, baseOid],
29743
+ { timeout: GIT_TIMEOUT_MS }
29744
+ ).then(() => true).catch(() => false);
29745
+ if (!canFastForward) {
29746
+ return fail(`worktree create: refusing to resume '${branch}' \u2014 it has local commits or diverges from ${base}`);
29747
+ }
29748
+ await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
29749
+ resumed = true;
29750
+ }
29751
+ if (!resumed) {
29752
+ step = `git worktree add ${wtPath}`;
29753
+ await addWorktreeRobust(wtPath, branch, base, {
29754
+ git: async (args) => (await execFileP2("git", args, { timeout: GH_MUTATION_TIMEOUT_MS })).stdout,
29755
+ revParse: async (ref) => {
29756
+ try {
29757
+ return (await execFileP2("git", ["rev-parse", "--verify", ref], { timeout: GIT_TIMEOUT_MS })).stdout.trim() || void 0;
29758
+ } catch {
29759
+ return void 0;
29760
+ }
29761
+ },
29762
+ deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
29763
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
29764
+ log: (m) => {
29765
+ if (!o.json) console.error(` ${m}`);
29598
29766
  }
29599
- },
29600
- deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
29601
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
29602
- log: (m) => {
29603
- if (!o.json) console.error(` ${m}`);
29604
- }
29605
- });
29767
+ });
29768
+ }
29606
29769
  step = "install deps + copy local-only config";
29607
29770
  const report = await provisionWorktree(wtPath, makeProvisionDeps(wtPath, Boolean(o.json), (m) => {
29608
29771
  if (!o.json) console.error(` ${m}`);
@@ -29642,12 +29805,13 @@ withExamples(mutating(
29642
29805
  branch,
29643
29806
  path: wtPath,
29644
29807
  base,
29808
+ resumed,
29645
29809
  ...report,
29646
29810
  ...issueForm && selector ? { issue: `${selector.repo}#${selector.number}` } : {},
29647
29811
  ...issueForm && o.claim ? { claim: claimError ? { ok: false, error: claimError } : claim } : {}
29648
29812
  }, null, 2));
29649
29813
  }
29650
- console.log(`worktree ready: ${wtPath} (branch ${branch} from ${base})`);
29814
+ console.log(`worktree ready: ${wtPath} (branch ${branch} ${resumed ? "resumed at" : "from"} ${base})`);
29651
29815
  console.log(` installed: ${report.installed.map((i) => i.dir || ".").join(", ") || "none"}`);
29652
29816
  console.log(` copied: ${report.copied.join(", ") || "none"}`);
29653
29817
  if (issueForm && o.claim && selector) {
@@ -30133,7 +30297,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
30133
30297
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
30134
30298
  if (o.secretsFile) {
30135
30299
  try {
30136
- vars.push(`secrets=${(0, import_node_fs35.readFileSync)(o.secretsFile, "utf8")}`);
30300
+ vars.push(`secrets=${(0, import_node_fs36.readFileSync)(o.secretsFile, "utf8")}`);
30137
30301
  } catch (e) {
30138
30302
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
30139
30303
  }
@@ -30881,11 +31045,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
30881
31045
  }
30882
31046
  });
30883
31047
  async function listCiWorkflowPaths(cwd = process.cwd()) {
30884
- const wfDir = (0, import_node_path34.join)(cwd, ".github", "workflows");
30885
- if (!(0, import_node_fs35.existsSync)(wfDir)) return [];
30886
- return (0, import_node_fs35.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
31048
+ const wfDir = (0, import_node_path35.join)(cwd, ".github", "workflows");
31049
+ if (!(0, import_node_fs36.existsSync)(wfDir)) return [];
31050
+ return (0, import_node_fs36.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
30887
31051
  try {
30888
- return workflowReportsPrChecks((0, import_node_fs35.readFileSync)((0, import_node_path34.join)(wfDir, name), "utf8"));
31052
+ return workflowReportsPrChecks((0, import_node_fs36.readFileSync)((0, import_node_path35.join)(wfDir, name), "utf8"));
30889
31053
  } catch {
30890
31054
  return true;
30891
31055
  }
@@ -30917,16 +31081,16 @@ function ciAuditDeps() {
30917
31081
  // gate re-seed step is skipped gracefully rather than failing mid-run.
30918
31082
  readSeedFile: (path2) => {
30919
31083
  if (!root) return null;
30920
- const fullPath = (0, import_node_path34.join)(root, path2);
30921
- return (0, import_node_fs35.existsSync)(fullPath) ? (0, import_node_fs35.readFileSync)(fullPath, "utf8") : null;
31084
+ const fullPath = (0, import_node_path35.join)(root, path2);
31085
+ return (0, import_node_fs36.existsSync)(fullPath) ? (0, import_node_fs36.readFileSync)(fullPath, "utf8") : null;
30922
31086
  }
30923
31087
  };
30924
31088
  }
30925
31089
  function hubRoot() {
30926
- const fromPkg = (0, import_node_path34.join)(__dirname, "..", "..");
31090
+ const fromPkg = (0, import_node_path35.join)(__dirname, "..", "..");
30927
31091
  const marker = "skills/bootstrap/seeds/manifest.json";
30928
- if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(fromPkg, marker))) return fromPkg;
30929
- if ((0, import_node_fs35.existsSync)((0, import_node_path34.join)(process.cwd(), marker))) return process.cwd();
31092
+ if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(fromPkg, marker))) return fromPkg;
31093
+ if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(process.cwd(), marker))) return process.cwd();
30930
31094
  return null;
30931
31095
  }
30932
31096
  pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
@@ -31055,6 +31219,10 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
31055
31219
  return false;
31056
31220
  }
31057
31221
  });
31222
+ if (result.status !== "failed") {
31223
+ const repoFlag = result.repo ? ` --repo ${result.repo}` : "";
31224
+ console.warn(`pr land: merge confirmed; cleanup pending \u2014 if this process is interrupted, resume with: mmi-cli pr merge ${number}${repoFlag} --squash`);
31225
+ }
31058
31226
  if (result.status !== "failed") {
31059
31227
  try {
31060
31228
  const { stdout } = await execFileP2(process.execPath, [
@@ -31229,7 +31397,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
31229
31397
  localCleanup = await cleanupPrMergeLocalBranch(headRef, {
31230
31398
  beforeWorktrees,
31231
31399
  startingPath,
31232
- pathExists: (p) => (0, import_node_fs35.existsSync)(p),
31400
+ pathExists: (p) => (0, import_node_fs36.existsSync)(p),
31233
31401
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
31234
31402
  teardownWorktreeStage,
31235
31403
  deferredStore,
@@ -31704,12 +31872,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
31704
31872
  targets = resolution.targets;
31705
31873
  }
31706
31874
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
31707
- const fileMatrix = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
31875
+ const fileMatrix = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
31708
31876
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
31709
31877
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
31710
- const fileContracts = (0, import_node_fs35.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs35.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
31878
+ const fileContracts = (0, import_node_fs36.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs36.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
31711
31879
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
31712
- const sanctioned = (0, import_node_fs35.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs35.readFileSync)("access-matrix.json", "utf8")) : {};
31880
+ const sanctioned = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
31713
31881
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
31714
31882
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
31715
31883
  if (!report.ok) process.exitCode = 1;
@@ -31741,16 +31909,16 @@ function directoryBytes(path2) {
31741
31909
  let total = 0;
31742
31910
  let entries;
31743
31911
  try {
31744
- entries = (0, import_node_fs35.readdirSync)(path2, { withFileTypes: true });
31912
+ entries = (0, import_node_fs36.readdirSync)(path2, { withFileTypes: true });
31745
31913
  } catch {
31746
31914
  return 0;
31747
31915
  }
31748
31916
  for (const entry of entries) {
31749
- const child2 = (0, import_node_path34.join)(path2, entry.name);
31917
+ const child2 = (0, import_node_path35.join)(path2, entry.name);
31750
31918
  if (entry.isDirectory()) total += directoryBytes(child2);
31751
31919
  else {
31752
31920
  try {
31753
- total += (0, import_node_fs35.statSync)(child2).size;
31921
+ total += (0, import_node_fs36.statSync)(child2).size;
31754
31922
  } catch {
31755
31923
  }
31756
31924
  }
@@ -31758,25 +31926,25 @@ function directoryBytes(path2) {
31758
31926
  return total;
31759
31927
  }
31760
31928
  function listDirEntries(dir) {
31761
- return (0, import_node_fs35.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
31929
+ return (0, import_node_fs36.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
31762
31930
  }
31763
31931
  function readInstalledPluginRefs(configRoot) {
31764
31932
  const p = installedPluginsPathForConfig(configRoot);
31765
- if (!(0, import_node_fs35.existsSync)(p)) return [];
31933
+ if (!(0, import_node_fs36.existsSync)(p)) return [];
31766
31934
  try {
31767
- return installedPluginPaths((0, import_node_fs35.readFileSync)(p, "utf8"));
31935
+ return installedPluginPaths((0, import_node_fs36.readFileSync)(p, "utf8"));
31768
31936
  } catch {
31769
31937
  return null;
31770
31938
  }
31771
31939
  }
31772
31940
  function pluginCacheFsDeps(configRoot, dirBytes) {
31773
31941
  return {
31774
- exists: (p) => (0, import_node_fs35.existsSync)(p),
31775
- listVersionDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
31942
+ exists: (p) => (0, import_node_fs36.existsSync)(p),
31943
+ listVersionDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
31776
31944
  dirBytes,
31777
- listStagingDirs: (root) => (0, import_node_fs35.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
31945
+ listStagingDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
31778
31946
  try {
31779
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path34.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs35.statSync)(p).mtimeMs) };
31947
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path35.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs36.statSync)(p).mtimeMs) };
31780
31948
  } catch {
31781
31949
  return { name: d.name, mtimeMs: Date.now() };
31782
31950
  }
@@ -31790,10 +31958,10 @@ function stagingApplyFsGuard(configRoot) {
31790
31958
  return {
31791
31959
  referencedPaths: () => readInstalledPluginRefs(configRoot),
31792
31960
  mtimeMs: (name) => {
31793
- const p = (0, import_node_path34.join)(stagingRoot, name);
31794
- if (!(0, import_node_fs35.existsSync)(p)) return null;
31961
+ const p = (0, import_node_path35.join)(stagingRoot, name);
31962
+ if (!(0, import_node_fs36.existsSync)(p)) return null;
31795
31963
  try {
31796
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs35.statSync)(q).mtimeMs);
31964
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs36.statSync)(q).mtimeMs);
31797
31965
  } catch {
31798
31966
  return null;
31799
31967
  }
@@ -31813,13 +31981,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
31813
31981
  return;
31814
31982
  }
31815
31983
  const plan = buildPluginCachePlan(
31816
- (0, import_node_os13.homedir)(),
31984
+ (0, import_node_os14.homedir)(),
31817
31985
  running,
31818
31986
  pluginCacheFsDeps(configRoot, directoryBytes),
31819
31987
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
31820
31988
  );
31821
31989
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
31822
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs35.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
31990
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs36.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
31823
31991
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
31824
31992
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
31825
31993
  else console.log(renderPluginCachePlan(plan, result));