@mutmutco/cli 3.126.0 → 3.128.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 +1136 -604
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -523,7 +523,7 @@ var require_graceful_fs = __commonJS({
523
523
  function patch(fs3) {
524
524
  polyfills(fs3);
525
525
  fs3.gracefulify = patch;
526
- fs3.createReadStream = createReadStream;
526
+ fs3.createReadStream = createReadStream2;
527
527
  fs3.createWriteStream = createWriteStream;
528
528
  var fs$readFile = fs3.readFile;
529
529
  fs3.readFile = readFile9;
@@ -733,7 +733,7 @@ var require_graceful_fs = __commonJS({
733
733
  }
734
734
  });
735
735
  }
736
- function createReadStream(path2, options) {
736
+ function createReadStream2(path2, options) {
737
737
  return new fs3.ReadStream(path2, options);
738
738
  }
739
739
  function createWriteStream(path2, options) {
@@ -1239,11 +1239,11 @@ var require_mtime_precision = __commonJS({
1239
1239
  function probe(file, fs2, callback) {
1240
1240
  const cachedPrecision = fs2[cacheSymbol];
1241
1241
  if (cachedPrecision) {
1242
- return fs2.stat(file, (err, stat3) => {
1242
+ return fs2.stat(file, (err, stat4) => {
1243
1243
  if (err) {
1244
1244
  return callback(err);
1245
1245
  }
1246
- callback(null, stat3.mtime, cachedPrecision);
1246
+ callback(null, stat4.mtime, cachedPrecision);
1247
1247
  });
1248
1248
  }
1249
1249
  const mtime = new Date(Math.ceil(Date.now() / 1e3) * 1e3 + 5);
@@ -1251,13 +1251,13 @@ var require_mtime_precision = __commonJS({
1251
1251
  if (err) {
1252
1252
  return callback(err);
1253
1253
  }
1254
- fs2.stat(file, (err2, stat3) => {
1254
+ fs2.stat(file, (err2, stat4) => {
1255
1255
  if (err2) {
1256
1256
  return callback(err2);
1257
1257
  }
1258
- const precision = stat3.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
1258
+ const precision = stat4.mtime.getTime() % 1e3 === 0 ? "s" : "ms";
1259
1259
  Object.defineProperty(fs2, cacheSymbol, { value: precision });
1260
- callback(null, stat3.mtime, precision);
1260
+ callback(null, stat4.mtime, precision);
1261
1261
  });
1262
1262
  });
1263
1263
  }
@@ -1311,14 +1311,14 @@ var require_lockfile = __commonJS({
1311
1311
  if (options.stale <= 0) {
1312
1312
  return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
1313
1313
  }
1314
- options.fs.stat(lockfilePath, (err2, stat3) => {
1314
+ options.fs.stat(lockfilePath, (err2, stat4) => {
1315
1315
  if (err2) {
1316
1316
  if (err2.code === "ENOENT") {
1317
1317
  return acquireLock2(file, { ...options, stale: 0 }, callback);
1318
1318
  }
1319
1319
  return callback(err2);
1320
1320
  }
1321
- if (!isLockStale(stat3, options)) {
1321
+ if (!isLockStale(stat4, options)) {
1322
1322
  return callback(Object.assign(new Error("Lock file is already being held"), { code: "ELOCKED", file }));
1323
1323
  }
1324
1324
  removeLock(file, options, (err3) => {
@@ -1330,8 +1330,8 @@ var require_lockfile = __commonJS({
1330
1330
  });
1331
1331
  });
1332
1332
  }
1333
- function isLockStale(stat3, options) {
1334
- return stat3.mtime.getTime() < Date.now() - options.stale;
1333
+ function isLockStale(stat4, options) {
1334
+ return stat4.mtime.getTime() < Date.now() - options.stale;
1335
1335
  }
1336
1336
  function removeLock(file, options, callback) {
1337
1337
  options.fs.rmdir(getLockFile(file, options), (err) => {
@@ -1349,7 +1349,7 @@ var require_lockfile = __commonJS({
1349
1349
  lock2.updateDelay = lock2.updateDelay || options.update;
1350
1350
  lock2.updateTimeout = setTimeout(() => {
1351
1351
  lock2.updateTimeout = null;
1352
- options.fs.stat(lock2.lockfilePath, (err, stat3) => {
1352
+ options.fs.stat(lock2.lockfilePath, (err, stat4) => {
1353
1353
  const isOverThreshold = lock2.lastUpdate + options.stale < Date.now();
1354
1354
  if (err) {
1355
1355
  if (err.code === "ENOENT" || isOverThreshold) {
@@ -1358,7 +1358,7 @@ var require_lockfile = __commonJS({
1358
1358
  lock2.updateDelay = 1e3;
1359
1359
  return updateLock(file, options);
1360
1360
  }
1361
- const isMtimeOurs = lock2.mtime.getTime() === stat3.mtime.getTime();
1361
+ const isMtimeOurs = lock2.mtime.getTime() === stat4.mtime.getTime();
1362
1362
  if (!isMtimeOurs) {
1363
1363
  return setLockAsCompromised(
1364
1364
  file,
@@ -1483,11 +1483,11 @@ var require_lockfile = __commonJS({
1483
1483
  if (err) {
1484
1484
  return callback(err);
1485
1485
  }
1486
- options.fs.stat(getLockFile(file2, options), (err2, stat3) => {
1486
+ options.fs.stat(getLockFile(file2, options), (err2, stat4) => {
1487
1487
  if (err2) {
1488
1488
  return err2.code === "ENOENT" ? callback(null, false) : callback(err2);
1489
1489
  }
1490
- return callback(null, !isLockStale(stat3, options));
1490
+ return callback(null, !isLockStale(stat4, options));
1491
1491
  });
1492
1492
  });
1493
1493
  }
@@ -1533,12 +1533,12 @@ var require_adapter = __commonJS({
1533
1533
  return newFs;
1534
1534
  }
1535
1535
  function toPromise(method) {
1536
- return (...args) => new Promise((resolve5, reject) => {
1536
+ return (...args) => new Promise((resolve6, reject) => {
1537
1537
  args.push((err, result) => {
1538
1538
  if (err) {
1539
1539
  reject(err);
1540
1540
  } else {
1541
- resolve5(result);
1541
+ resolve6(result);
1542
1542
  }
1543
1543
  });
1544
1544
  method(...args);
@@ -4999,8 +4999,8 @@ function useColor() {
4999
4999
  var program = new Command();
5000
5000
 
5001
5001
  // src/index.ts
5002
- var import_promises11 = require("node:fs/promises");
5003
- var import_node_fs47 = require("node:fs");
5002
+ var import_promises12 = require("node:fs/promises");
5003
+ var import_node_fs48 = require("node:fs");
5004
5004
  var import_node_child_process20 = require("node:child_process");
5005
5005
 
5006
5006
  // src/cli-shared.ts
@@ -5112,19 +5112,19 @@ function hardExit(code) {
5112
5112
  }
5113
5113
  var STDIO_FLUSH_TIMEOUT_MS = 2e3;
5114
5114
  function flushStream(stream) {
5115
- return new Promise((resolve5) => {
5115
+ return new Promise((resolve6) => {
5116
5116
  try {
5117
- stream.write("", () => resolve5());
5117
+ stream.write("", () => resolve6());
5118
5118
  } catch {
5119
- resolve5();
5119
+ resolve6();
5120
5120
  }
5121
5121
  });
5122
5122
  }
5123
5123
  async function flushStdio(timeoutMs = STDIO_FLUSH_TIMEOUT_MS) {
5124
5124
  await Promise.race([
5125
5125
  Promise.all([flushStream(process.stdout), flushStream(process.stderr)]),
5126
- new Promise((resolve5) => {
5127
- setTimeout(resolve5, timeoutMs).unref?.();
5126
+ new Promise((resolve6) => {
5127
+ setTimeout(resolve6, timeoutMs).unref?.();
5128
5128
  })
5129
5129
  ]);
5130
5130
  }
@@ -5132,7 +5132,7 @@ async function cleanExit(code) {
5132
5132
  process.exitCode = code;
5133
5133
  await closeHttpPool();
5134
5134
  await flushStdio();
5135
- await new Promise((resolve5) => setImmediate(resolve5));
5135
+ await new Promise((resolve6) => setImmediate(resolve6));
5136
5136
  return void 0;
5137
5137
  }
5138
5138
  var CLI_EXIT_WATCHDOG_MS = (() => {
@@ -5519,7 +5519,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
5519
5519
  const attempts = opts.attempts ?? 3;
5520
5520
  const baseDelayMs = opts.baseDelayMs ?? 250;
5521
5521
  const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
5522
- const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
5522
+ const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
5523
5523
  let lastErr;
5524
5524
  for (let i = 0; i < attempts; i++) {
5525
5525
  const isLast = i === attempts - 1;
@@ -5646,10 +5646,10 @@ var import_node_fs4 = require("node:fs");
5646
5646
  var injectedStdin;
5647
5647
  function stdinHasPipedInput(statFd = () => (0, import_node_fs4.fstatSync)(0), getIsTTY = () => process.stdin.isTTY) {
5648
5648
  try {
5649
- const stat3 = statFd();
5650
- if (stat3.isFIFO() || stat3.isFile()) return true;
5651
- if (stat3.isCharacterDevice()) return false;
5652
- if (stat3.isSocket()) return false;
5649
+ const stat4 = statFd();
5650
+ if (stat4.isFIFO() || stat4.isFile()) return true;
5651
+ if (stat4.isCharacterDevice()) return false;
5652
+ if (stat4.isSocket()) return false;
5653
5653
  if (getIsTTY() === true) return false;
5654
5654
  return true;
5655
5655
  } catch {
@@ -5680,8 +5680,8 @@ async function readStdin(opts = {}) {
5680
5680
  })().catch(() => {
5681
5681
  });
5682
5682
  let timer;
5683
- const timeout = new Promise((resolve5) => {
5684
- timer = setTimeout(resolve5, timeoutMs);
5683
+ const timeout = new Promise((resolve6) => {
5684
+ timer = setTimeout(resolve6, timeoutMs);
5685
5685
  });
5686
5686
  try {
5687
5687
  await Promise.race([drain, timeout]);
@@ -5735,12 +5735,12 @@ function killProcessTree(pid) {
5735
5735
  function execFileHard(file, args, options) {
5736
5736
  const { timeout, step, ...rest } = options;
5737
5737
  const started = Date.now();
5738
- return new Promise((resolve5, reject) => {
5738
+ return new Promise((resolve6, reject) => {
5739
5739
  const child2 = (0, import_node_child_process3.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
5740
5740
  clearTimeout(timer);
5741
5741
  if (expired) return;
5742
5742
  if (error) reject(error);
5743
- else resolve5({ stdout: String(stdout), stderr: String(stderr) });
5743
+ else resolve6({ stdout: String(stdout), stderr: String(stderr) });
5744
5744
  });
5745
5745
  let expired = false;
5746
5746
  const timer = setTimeout(() => {
@@ -6234,9 +6234,9 @@ function trackedPathStatus(c, realPath, repoAnchor) {
6234
6234
  return null;
6235
6235
  }
6236
6236
  }
6237
- function rootScratchDirSnapshot(root, readdir2, stat3) {
6237
+ function rootScratchDirSnapshot(root, readdir2, stat4) {
6238
6238
  try {
6239
- const rootStat = stat3(root);
6239
+ const rootStat = stat4(root);
6240
6240
  let mtimeMs = rootStat.mtimeMs;
6241
6241
  let bytes = 0;
6242
6242
  const stack = [root];
@@ -6250,7 +6250,7 @@ function rootScratchDirSnapshot(root, readdir2, stat3) {
6250
6250
  return null;
6251
6251
  } catch {
6252
6252
  }
6253
- const st = stat3(child2);
6253
+ const st = stat4(child2);
6254
6254
  mtimeMs = Math.max(mtimeMs, st.mtimeMs);
6255
6255
  if (ent.isDirectory()) stack.push(child2);
6256
6256
  else if (ent.isFile()) bytes += st.size;
@@ -6263,7 +6263,7 @@ function rootScratchDirSnapshot(root, readdir2, stat3) {
6263
6263
  }
6264
6264
  function collectScratchSnapshot(repoRoot2, deps = {}) {
6265
6265
  const readdir2 = deps.readdir ?? import_node_fs6.readdirSync;
6266
- const stat3 = deps.stat ?? import_node_fs6.statSync;
6266
+ const stat4 = deps.stat ?? import_node_fs6.statSync;
6267
6267
  const plansRoot = (0, import_node_path4.join)(repoRoot2, "plans");
6268
6268
  const rootScratchFiles = [];
6269
6269
  try {
@@ -6276,10 +6276,10 @@ function collectScratchSnapshot(repoRoot2, deps = {}) {
6276
6276
  const full = (0, import_node_path4.join)(repoRoot2, ent.name);
6277
6277
  try {
6278
6278
  if (isDir) {
6279
- const snap = rootScratchDirSnapshot(full, readdir2, stat3);
6279
+ const snap = rootScratchDirSnapshot(full, readdir2, stat4);
6280
6280
  if (snap) rootScratchFiles.push({ path: full, dir: repoRoot2, name: ent.name, mtimeMs: snap.mtimeMs, bytes: snap.bytes, kind: "dir" });
6281
6281
  } else {
6282
- const st = stat3(full);
6282
+ const st = stat4(full);
6283
6283
  rootScratchFiles.push({ path: full, dir: repoRoot2, name: ent.name, mtimeMs: st.mtimeMs, bytes: st.size, kind: "file" });
6284
6284
  }
6285
6285
  } catch {
@@ -6293,7 +6293,7 @@ function collectScratchSnapshot(repoRoot2, deps = {}) {
6293
6293
  if (!ent.isFile() || !ent.name.endsWith(".md")) continue;
6294
6294
  const full = (0, import_node_path4.join)(plansRoot, ent.name);
6295
6295
  try {
6296
- const st = stat3(full);
6296
+ const st = stat4(full);
6297
6297
  planMdFiles.push({ path: full, dir: plansRoot, name: ent.name, mtimeMs: st.mtimeMs, bytes: st.size, kind: "file" });
6298
6298
  } catch {
6299
6299
  }
@@ -6744,7 +6744,7 @@ function localTrainSyncBannerLine(result) {
6744
6744
  // src/gc.ts
6745
6745
  var import_node_fs11 = require("node:fs");
6746
6746
  var import_promises = require("node:fs/promises");
6747
- var import_node_path9 = require("node:path");
6747
+ var import_node_path10 = require("node:path");
6748
6748
 
6749
6749
  // src/active-workspace-root.ts
6750
6750
  var import_node_fs9 = require("node:fs");
@@ -6762,6 +6762,12 @@ function isPathUnderDirectory(childPath, parentPath, platform2 = process.platfor
6762
6762
  if (child2 === parent) return true;
6763
6763
  return child2.startsWith(`${parent}/`);
6764
6764
  }
6765
+ function isCursorAgentHost(env = process.env) {
6766
+ return env.CURSOR_AGENT === "1" || Boolean(env.CURSOR_EXTENSION_HOST_ROLE?.trim()) || Boolean(env.AGENT_TRANSCRIPTS?.trim());
6767
+ }
6768
+ function unresolvedWorkspaceRefusalMessage() {
6769
+ return "refusing to remove a worktree: Cursor agent host cannot resolve the active workspace root. Open the primary checkout in Cursor first (keep the workspace root on the primary; edit the worktree without move_agent_to_root), or set MMI_ACTIVE_WORKSPACE_ROOT to the primary path, then retry `mmi-cli worktree gc sweep-deferred` / `worktree gc --apply` / `worktree land --apply`.";
6770
+ }
6765
6771
  function normalizeActiveWorkspaceRoot(value, opts = {}) {
6766
6772
  const raw = value?.trim();
6767
6773
  if (!raw) return void 0;
@@ -6779,8 +6785,13 @@ function removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform
6779
6785
  function activeWorkspaceRefusalMessage(targetPath, activeWorkspaceRoot) {
6780
6786
  return `refusing to remove active Cursor workspace ${activeWorkspaceRoot} (target ${targetPath}). Open the primary checkout in Cursor first, then retry \`mmi-cli worktree gc sweep-deferred\` / \`worktree gc --apply\`.`;
6781
6787
  }
6782
- function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform) {
6783
- if (!activeWorkspaceRoot) return { action: "proceed" };
6788
+ function decideActiveWorkspaceGuard(targetPath, activeWorkspaceRoot, platform2 = process.platform, opts = {}) {
6789
+ if (!activeWorkspaceRoot) {
6790
+ if (opts.cursorAgentHost) {
6791
+ return { action: "refuse", reason: "unresolved-workspace", message: unresolvedWorkspaceRefusalMessage() };
6792
+ }
6793
+ return { action: "proceed" };
6794
+ }
6784
6795
  if (!removalTouchesActiveWorkspace(targetPath, activeWorkspaceRoot, platform2)) {
6785
6796
  return { action: "proceed" };
6786
6797
  }
@@ -6882,10 +6893,137 @@ function resolveActiveWorkspaceRoot(deps = {}) {
6882
6893
  return resolveCursorAgentWorkspaceRoot(deps);
6883
6894
  }
6884
6895
 
6896
+ // src/estate-hygiene.ts
6897
+ var import_node_path8 = require("node:path");
6898
+ function win32LongPath(p, platform2 = process.platform) {
6899
+ if (platform2 !== "win32") return p;
6900
+ if (p.startsWith("\\\\?\\")) return p;
6901
+ if (p.startsWith("\\\\")) return `\\\\?\\UNC\\${p.slice(2)}`;
6902
+ return `\\\\?\\${p}`;
6903
+ }
6904
+ function normalizeGithubRemote(url) {
6905
+ const raw = url.trim();
6906
+ if (!raw) return void 0;
6907
+ const ssh = raw.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i);
6908
+ if (ssh) return `${ssh[1]}/${ssh[2]}`.toLowerCase();
6909
+ const https = raw.match(/^https?:\/\/github\.com\/([^/]+)\/(.+?)(?:\.git)?$/i);
6910
+ if (https) return `${https[1]}/${https[2]}`.toLowerCase();
6911
+ return void 0;
6912
+ }
6913
+ function remotesOverlap(a, b) {
6914
+ const left = new Set(a.map(normalizeGithubRemote).filter((x) => Boolean(x)));
6915
+ if (!left.size) return false;
6916
+ return b.some((url) => {
6917
+ const key = normalizeGithubRemote(url);
6918
+ return Boolean(key && left.has(key));
6919
+ });
6920
+ }
6921
+ function worktreesRootOf(primaryCheckout) {
6922
+ return (0, import_node_path8.join)((0, import_node_path8.dirname)(primaryCheckout), "mmi-worktrees", (0, import_node_path8.basename)(primaryCheckout));
6923
+ }
6924
+ function helperWorktreeRoots(primaryCheckout) {
6925
+ return [
6926
+ (0, import_node_path8.join)(primaryCheckout, ".claude", "worktrees"),
6927
+ (0, import_node_path8.join)(primaryCheckout, ".worktrees")
6928
+ ];
6929
+ }
6930
+ function pathProvesRepoContainerOwnership(dir, repoContainer, platform2 = process.platform) {
6931
+ const norm = (p) => {
6932
+ const unified = p.replace(/\\/g, "/").replace(/\/+$/, "");
6933
+ return platform2 === "win32" || platform2 === "darwin" ? unified.toLowerCase() : unified;
6934
+ };
6935
+ return norm((0, import_node_path8.dirname)(dir)) === norm(repoContainer);
6936
+ }
6937
+ function worktreesRootFromLeaseRef(ref) {
6938
+ const unified = ref.replace(/\\/g, "/");
6939
+ const marker = "/mmi-worktrees/";
6940
+ const idx = unified.toLowerCase().lastIndexOf(marker);
6941
+ if (idx < 0) return void 0;
6942
+ const after = unified.slice(idx + marker.length);
6943
+ const repo = after.split("/").filter(Boolean)[0];
6944
+ if (!repo) return void 0;
6945
+ return unified.slice(0, idx + marker.length + repo.length);
6946
+ }
6947
+ function classifyEstateAudit(input) {
6948
+ const otherPrimaries = input.otherCheckouts.filter((c) => remotesOverlap(input.remoteUrls, c.remoteUrls)).map((c) => c.path);
6949
+ const thisRoot = input.thisWorktreesRoot.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
6950
+ const foreignLeaseRoots = [...new Set(
6951
+ (input.leaseRefs ?? []).map(worktreesRootFromLeaseRef).filter((root) => Boolean(root)).filter((root) => root.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() !== thisRoot)
6952
+ )];
6953
+ const reasons = [];
6954
+ if (otherPrimaries.length) {
6955
+ reasons.push(
6956
+ `another primary checkout of the same remotes exists: ${otherPrimaries.join(", ")}`
6957
+ );
6958
+ }
6959
+ if (foreignLeaseRoots.length) {
6960
+ reasons.push(
6961
+ `jerv lease ledger points at another worktrees root: ${foreignLeaseRoots.join(", ")}`
6962
+ );
6963
+ }
6964
+ return {
6965
+ auditedClone: input.auditedClone,
6966
+ otherPrimaries,
6967
+ foreignLeaseRoots,
6968
+ blocksGreen: reasons.length > 0,
6969
+ reasons
6970
+ };
6971
+ }
6972
+ function formatEstateAuditLines(audit) {
6973
+ const lines = [`audited clone: ${audit.auditedClone}`];
6974
+ if (!audit.blocksGreen) return lines;
6975
+ lines.push('refusing a clean "no leaks" report \u2014 this clone is not the whole estate:');
6976
+ for (const reason of audit.reasons) lines.push(` ${reason}`);
6977
+ lines.push(" fix: run doctor / worktree list --stale / worktree gc from the canonical primary (E:\\AI Projects\\Mutatis Mutandis\\\u2026), not a second clone");
6978
+ return lines;
6979
+ }
6980
+ function classifyOriginLeftovers(input) {
6981
+ const local = new Set(input.localBranches ?? []);
6982
+ const leftovers = [];
6983
+ for (const branch of input.remoteBranches) {
6984
+ const name = branch.replace(/^origin\//, "").trim();
6985
+ if (!name || name === "HEAD" || input.protectedBranches.has(name)) continue;
6986
+ if (input.openPrBranches.has(name)) {
6987
+ leftovers.push({
6988
+ branch: name,
6989
+ kind: "open-pr",
6990
+ autoReap: false,
6991
+ detail: `origin/${name} has an open PR \u2014 gc will not delete it`
6992
+ });
6993
+ continue;
6994
+ }
6995
+ if (input.mergedPrBranches.has(name)) {
6996
+ leftovers.push({
6997
+ branch: name,
6998
+ kind: "merged-missed",
6999
+ autoReap: true,
7000
+ detail: local.has(name) ? `origin/${name} is merged; this clone still has the head` : `origin/${name} is merged; this clone has no local branch (missed reap)`
7001
+ });
7002
+ continue;
7003
+ }
7004
+ if (input.closedUnmergedBranches.has(name)) {
7005
+ leftovers.push({
7006
+ branch: name,
7007
+ kind: "closed-not-merged",
7008
+ autoReap: Boolean(input.trainOnly),
7009
+ detail: `origin/${name} is closed-not-merged \u2014 gc will not auto-delete it${input.trainOnly ? " unless --train-only --apply" : ""}`
7010
+ });
7011
+ continue;
7012
+ }
7013
+ leftovers.push({
7014
+ branch: name,
7015
+ kind: local.has(name) ? "no-pr" : "other-clone",
7016
+ autoReap: Boolean(input.trainOnly),
7017
+ detail: local.has(name) ? `origin/${name} has no PR \u2014 gc will not auto-delete it` : `origin/${name} is an other-clone leftover with no PR this clone can see`
7018
+ });
7019
+ }
7020
+ return leftovers;
7021
+ }
7022
+
6885
7023
  // src/worktree-ownership.ts
6886
7024
  var import_node_fs10 = require("node:fs");
6887
7025
  var import_node_os4 = require("node:os");
6888
- var import_node_path8 = require("node:path");
7026
+ var import_node_path9 = require("node:path");
6889
7027
  var OWNERS_FILE = "worktree-owners.json";
6890
7028
  var EVENTS_FILE = "worktree-events.jsonl";
6891
7029
  var WORKTREE_ACTIVITY_WINDOW_MS = 30 * 6e4;
@@ -6990,7 +7128,7 @@ function decideWorktreeRemoval(input) {
6990
7128
  }
6991
7129
  function readFreshWorktreeLease(path2, now) {
6992
7130
  try {
6993
- const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path8.join)(path2, WORKTREE_LEASE_MARKER), "utf8"));
7131
+ const candidate = JSON.parse((0, import_node_fs10.readFileSync)((0, import_node_path9.join)(path2, WORKTREE_LEASE_MARKER), "utf8"));
6994
7132
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
6995
7133
  const lease = candidate;
6996
7134
  if (lease.kind !== "worktree" || lease.state !== "active" || typeof lease.agent !== "string" || !lease.agent.trim() || typeof lease.ref !== "string" || !sameWorktreePath(lease.ref, path2) || typeof lease.createdAt !== "string" || typeof lease.ttlHours !== "number" || !Number.isFinite(lease.ttlHours) || lease.ttlHours <= 0) return void 0;
@@ -7017,7 +7155,7 @@ function readOwners(primaryRoot) {
7017
7155
  function writeOwners(primaryRoot, entries) {
7018
7156
  try {
7019
7157
  const path2 = worktreeOwnersPath(primaryRoot);
7020
- (0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(path2), { recursive: true });
7158
+ (0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
7021
7159
  (0, import_node_fs10.writeFileSync)(path2, serializeWorktreeOwners(entries), "utf8");
7022
7160
  } catch {
7023
7161
  }
@@ -7053,7 +7191,7 @@ function dropWorktreeOwner(primaryRoot, path2, expectedCreatedAt) {
7053
7191
  function appendWorktreeEvent(primaryRoot, event) {
7054
7192
  try {
7055
7193
  const path2 = worktreeEventsPath(primaryRoot);
7056
- (0, import_node_fs10.mkdirSync)((0, import_node_path8.dirname)(path2), { recursive: true });
7194
+ (0, import_node_fs10.mkdirSync)((0, import_node_path9.dirname)(path2), { recursive: true });
7057
7195
  (0, import_node_fs10.appendFileSync)(path2, `${JSON.stringify({ at: event.at ?? (/* @__PURE__ */ new Date()).toISOString(), ...event })}
7058
7196
  `, "utf8");
7059
7197
  } catch {
@@ -7132,7 +7270,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
7132
7270
  const resolveRoot = deps.resolveArchiveRoot ?? repoRuntimeStatePath;
7133
7271
  if (!args.primaryRoot?.trim()) return { status: "skipped", reason: "missing-primary-root" };
7134
7272
  if (!args.worktreePath?.trim()) return { status: "skipped", reason: "missing-worktree-path" };
7135
- const source = (0, import_node_path9.join)(args.worktreePath, ".jerv");
7273
+ const source = (0, import_node_path10.join)(args.worktreePath, ".jerv");
7136
7274
  if (!exists(source)) return { status: "absent" };
7137
7275
  if (!isDirectory(source)) return { status: "skipped", reason: "jerv-not-a-directory" };
7138
7276
  const runScope = resolveJervArtifactRunScope(env);
@@ -7140,7 +7278,7 @@ function archiveWorktreeJervArtifacts(args, deps = {}) {
7140
7278
  const stamp = now().toISOString().replace(/[:.]/g, "-");
7141
7279
  const dest = resolveRoot(args.primaryRoot, "jerv-artifacts", runScope, branchSlug, stamp, ".jerv");
7142
7280
  try {
7143
- mkdirp((0, import_node_path9.dirname)(dest));
7281
+ mkdirp((0, import_node_path10.dirname)(dest));
7144
7282
  copyDir(source, dest);
7145
7283
  if (!exists(dest)) return { status: "failed", error: `archive write left no directory at ${dest}` };
7146
7284
  return { status: "archived", path: dest, runScope };
@@ -7294,7 +7432,9 @@ async function sweepDeferredWorktrees(store, deps, removalContext) {
7294
7432
  const owner = removalContext ? lookupWorktreeOwner(removalContext.primaryRoot, entry.path) : void 0;
7295
7433
  if (removalContext) {
7296
7434
  const activeRoot = removalContext.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
7297
- const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot);
7435
+ const activeGuard = decideActiveWorkspaceGuard(entry.path, activeRoot, process.platform, {
7436
+ cursorAgentHost: removalContext.cursorAgentHost ?? false
7437
+ });
7298
7438
  if (activeGuard.action === "refuse") {
7299
7439
  stillDeferred.push({ ...entry, reason: "active-workspace" });
7300
7440
  recordWorktreeRemoval(removalContext.primaryRoot, {
@@ -7405,7 +7545,7 @@ async function runWithSweepWatchdog(run, timeoutMs, onTimeout) {
7405
7545
  clearTimeout(timer);
7406
7546
  }
7407
7547
  }
7408
- var defaultSleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
7548
+ var defaultSleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
7409
7549
  async function removeWorktreeWithRecovery(wtPath, deps) {
7410
7550
  const maxAttempts = deps.maxAttempts ?? 3;
7411
7551
  const backoff = deps.backoffMs ?? [250, 1e3];
@@ -7639,23 +7779,23 @@ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoots, dep
7639
7779
  return { ok: false, reason: reasons.join("; ") || "no worktrees root to check against" };
7640
7780
  }
7641
7781
  function siblingMmiWorktreesRoot(repoRoot2) {
7642
- const parent = (0, import_node_path9.dirname)(repoRoot2);
7643
- if ((0, import_node_path9.basename)(parent).toLowerCase() === "mmi-worktrees") return parent;
7644
- const grandparent = (0, import_node_path9.dirname)(parent);
7645
- if ((0, import_node_path9.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
7646
- return (0, import_node_path9.join)(parent, "mmi-worktrees");
7782
+ const parent = (0, import_node_path10.dirname)(repoRoot2);
7783
+ if ((0, import_node_path10.basename)(parent).toLowerCase() === "mmi-worktrees") return parent;
7784
+ const grandparent = (0, import_node_path10.dirname)(parent);
7785
+ if ((0, import_node_path10.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
7786
+ return (0, import_node_path10.join)(parent, "mmi-worktrees");
7647
7787
  }
7648
7788
  function agentWorktreesRoot(repoRoot2) {
7649
- return (0, import_node_path9.join)(repoRoot2, ".claude", "worktrees");
7789
+ return (0, import_node_path10.join)(repoRoot2, ".claude", "worktrees");
7650
7790
  }
7651
7791
  function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
7652
- const projectsDir = (0, import_node_path9.dirname)(root);
7653
- const ownName = (0, import_node_path9.basename)(repoRoot2).toLowerCase();
7792
+ const projectsDir = (0, import_node_path10.dirname)(root);
7793
+ const ownName = (0, import_node_path10.basename)(repoRoot2).toLowerCase();
7654
7794
  const flat = [];
7655
7795
  let ownContainer = null;
7656
7796
  for (const dir of listDirs(root)) {
7657
- const name = (0, import_node_path9.basename)(dir);
7658
- if (isRepoCheckout((0, import_node_path9.join)(projectsDir, name))) {
7797
+ const name = (0, import_node_path10.basename)(dir);
7798
+ if (isRepoCheckout((0, import_node_path10.join)(projectsDir, name))) {
7659
7799
  if (name.toLowerCase() === ownName) ownContainer = dir;
7660
7800
  continue;
7661
7801
  }
@@ -7664,9 +7804,9 @@ function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
7664
7804
  return ownContainer ? [...flat, ...listDirs(ownContainer)] : flat;
7665
7805
  }
7666
7806
  function explicitRepoWorktreesRoot(root, repoRoot2, rootDirs) {
7667
- const repoName2 = (0, import_node_path9.basename)(repoRoot2);
7807
+ const repoName2 = (0, import_node_path10.basename)(repoRoot2);
7668
7808
  const repoDir = rootDirs.find((name) => name.toLowerCase() === repoName2.toLowerCase());
7669
- return repoDir ? (0, import_node_path9.join)(root, repoDir) : root;
7809
+ return repoDir ? (0, import_node_path10.join)(root, repoDir) : root;
7670
7810
  }
7671
7811
  function classifySiblingWorktreeDir(entry) {
7672
7812
  if (!entry.ownedByCurrentRepo) {
@@ -7914,7 +8054,33 @@ function buildGcPlan(inputs) {
7914
8054
  const state = closedState(prSet);
7915
8055
  return state ? { ref, branch, prState: state.state, prNumbers: state.numbers } : null;
7916
8056
  }).filter((r) => Boolean(r));
7917
- return { branches, trackingRefs, worktreeDirs, skippedWorktreeDirs, skipped };
8057
+ const originLeftovers = classifyOriginLeftovers({
8058
+ remoteBranches: inputs.originBranches ?? [],
8059
+ localBranches: inputs.localBranches,
8060
+ protectedBranches,
8061
+ openPrBranches: new Set(
8062
+ [...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "OPEN")).map(([b]) => b)
8063
+ ),
8064
+ mergedPrBranches: new Set(
8065
+ [...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
8066
+ ),
8067
+ closedUnmergedBranches: new Set(
8068
+ [...prs.entries()].filter(([, set]) => set.some((pr2) => pr2.state === "CLOSED") && !set.some((pr2) => pr2.state === "MERGED")).map(([b]) => b)
8069
+ ),
8070
+ trainOnly: inputs.trainOnly
8071
+ });
8072
+ const plannedBranches = new Set(branches.map((b) => b.branch));
8073
+ const reapOriginHeads = originLeftovers.filter((l) => l.autoReap && !plannedBranches.has(l.branch)).map((l) => {
8074
+ const prSet = prs.get(l.branch);
8075
+ const state = closedState(prSet);
8076
+ return {
8077
+ branch: l.branch,
8078
+ prState: state?.state ?? "MERGED",
8079
+ prNumbers: state?.numbers ?? [],
8080
+ ...state?.headOids.length ? { reviewedHeadOids: state.headOids } : {}
8081
+ };
8082
+ });
8083
+ return { branches, trackingRefs, worktreeDirs, skippedWorktreeDirs, skipped, originLeftovers, reapOriginHeads };
7918
8084
  }
7919
8085
  function parseRemotePruneDryRun(stdout) {
7920
8086
  const refs = [];
@@ -8229,7 +8395,9 @@ async function cleanupPrMergeLocalBranch(branch, options) {
8229
8395
  report.worktree = { path: wtPath, status: "not-attempted", reason: "main-worktree" };
8230
8396
  } else if (wtPath) {
8231
8397
  const activeRoot = options.removalContext?.activeWorkspaceRoot ?? resolveActiveWorkspaceRoot();
8232
- const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot);
8398
+ const activeGuard = decideActiveWorkspaceGuard(wtPath, activeRoot, process.platform, {
8399
+ cursorAgentHost: options.removalContext?.cursorAgentHost ?? false
8400
+ });
8233
8401
  if (activeGuard.action === "refuse") {
8234
8402
  if (options.deferredStore) {
8235
8403
  try {
@@ -8463,9 +8631,13 @@ async function cleanupPrMergeLocalBranch(branch, options) {
8463
8631
  if (wtPath) await git2(["worktree", "prune"]).catch(() => "");
8464
8632
  return report;
8465
8633
  }
8466
- function formatGcPlan(plan, apply) {
8634
+ function formatGcPlan(plan, apply, auditClone) {
8467
8635
  const lines = [`mmi-cli worktree gc: ${apply ? "apply" : "dry-run"}`];
8468
- if (!plan.branches.length && !plan.trackingRefs.length && !plan.worktreeDirs.length) lines.push("nothing to clean");
8636
+ if (auditClone) lines.push(`audited clone: ${auditClone}`);
8637
+ const hasOriginNames = Boolean(plan.originLeftovers?.length);
8638
+ if (!plan.branches.length && !plan.trackingRefs.length && !plan.worktreeDirs.length && !plan.reapOriginHeads?.length) {
8639
+ lines.push(hasOriginNames ? "nothing this clone will auto-delete" : "nothing to clean");
8640
+ }
8469
8641
  if (plan.branches.length) {
8470
8642
  lines.push("local branches:");
8471
8643
  for (const b of plan.branches) {
@@ -8507,6 +8679,12 @@ function formatGcPlan(plan, apply) {
8507
8679
  lines.push(` - ${s.path}: ${s.reason}${s.detail ? ` (${s.detail})` : ""}`);
8508
8680
  }
8509
8681
  }
8682
+ if (plan.originLeftovers?.length) {
8683
+ lines.push("origin leftovers gc will not silently delete (use --train-only for closed-not-merged / no-PR):");
8684
+ for (const leftover of plan.originLeftovers) {
8685
+ lines.push(` - ${leftover.branch}: ${leftover.kind}${leftover.autoReap ? " (will reap)" : ""} \u2014 ${leftover.detail}`);
8686
+ }
8687
+ }
8510
8688
  if (!apply && (plan.branches.length || plan.trackingRefs.length || plan.worktreeDirs.length)) lines.push("rerun with --apply to delete only the listed local, remote, tracking, and directory items");
8511
8689
  return lines.join("\n");
8512
8690
  }
@@ -8592,7 +8770,7 @@ async function gatherStaleWorktreeWarning(gitRun = defaultGitRun) {
8592
8770
 
8593
8771
  // src/released-version-cache.ts
8594
8772
  var import_node_fs12 = require("node:fs");
8595
- var import_node_path10 = require("node:path");
8773
+ var import_node_path11 = require("node:path");
8596
8774
 
8597
8775
  // src/version-lag.ts
8598
8776
  var VERSION_LABEL = "installed plugin/adapter cache freshness";
@@ -8688,7 +8866,7 @@ function versionAutoUpdateAction(report, releasedSource) {
8688
8866
  // src/released-version-cache.ts
8689
8867
  var RELEASED_VERSION_CACHE_MS = 24 * 36e5;
8690
8868
  function releasedVersionCachePath(runtimeRoot) {
8691
- return (0, import_node_path10.join)(runtimeRoot, "head-ts", ".released-version");
8869
+ return (0, import_node_path11.join)(runtimeRoot, "head-ts", ".released-version");
8692
8870
  }
8693
8871
  function readReleasedVersionCache(cachePath, now = Date.now(), read = import_node_fs12.readFileSync, currentVersion = resolveClientVersion()) {
8694
8872
  let parsed;
@@ -8708,7 +8886,7 @@ function readReleasedVersionCache(cachePath, now = Date.now(), read = import_nod
8708
8886
  }
8709
8887
  function writeReleasedVersionCache(cachePath, version, now = Date.now()) {
8710
8888
  try {
8711
- (0, import_node_fs12.mkdirSync)((0, import_node_path10.dirname)(cachePath), { recursive: true });
8889
+ (0, import_node_fs12.mkdirSync)((0, import_node_path11.dirname)(cachePath), { recursive: true });
8712
8890
  (0, import_node_fs12.writeFileSync)(cachePath, JSON.stringify({ version, at: now }), "utf8");
8713
8891
  } catch {
8714
8892
  }
@@ -8723,10 +8901,10 @@ function cachedReadNote(cachedAt, now = Date.now()) {
8723
8901
 
8724
8902
  // src/schedules-drift-cache.ts
8725
8903
  var import_node_fs13 = require("node:fs");
8726
- var import_node_path11 = require("node:path");
8904
+ var import_node_path12 = require("node:path");
8727
8905
  var SCHEDULES_DRIFT_CACHE_MS = 6 * 36e5;
8728
8906
  function schedulesDriftCachePath(runtimeRoot) {
8729
- return (0, import_node_path11.join)(runtimeRoot, "head-ts", ".schedules-drift");
8907
+ return (0, import_node_path12.join)(runtimeRoot, "head-ts", ".schedules-drift");
8730
8908
  }
8731
8909
  function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node_fs13.readFileSync) {
8732
8910
  let parsed;
@@ -8745,7 +8923,7 @@ function readSchedulesDriftCache(cachePath, now = Date.now(), read = import_node
8745
8923
  }
8746
8924
  function writeSchedulesDriftCache(cachePath, driftLines, now = Date.now()) {
8747
8925
  try {
8748
- (0, import_node_fs13.mkdirSync)((0, import_node_path11.dirname)(cachePath), { recursive: true });
8926
+ (0, import_node_fs13.mkdirSync)((0, import_node_path12.dirname)(cachePath), { recursive: true });
8749
8927
  (0, import_node_fs13.writeFileSync)(cachePath, JSON.stringify({ driftLines, at: now }), "utf8");
8750
8928
  } catch {
8751
8929
  }
@@ -8901,7 +9079,7 @@ function resolveCatalogRef(probe) {
8901
9079
  // src/plugin-guard-io.ts
8902
9080
  var import_node_fs14 = require("node:fs");
8903
9081
  var import_node_child_process6 = require("node:child_process");
8904
- var import_node_path12 = require("node:path");
9082
+ var import_node_path13 = require("node:path");
8905
9083
  var import_node_os5 = require("node:os");
8906
9084
  var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
8907
9085
 
@@ -9204,17 +9382,17 @@ function runHostBin(bin, args, opts) {
9204
9382
  return step ? execFileHard(file, argv, { ...shared, step }) : execFileP2(file, argv, shared);
9205
9383
  }
9206
9384
  function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os5.homedir)()) {
9207
- if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path12.join)(home, ".codex");
9208
- if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path12.join)(home, ".kimi-code");
9209
- if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path12.join)(home, ".config", "kilo");
9210
- if (surface === "cursor") return (0, import_node_path12.join)(home, ".cursor");
9385
+ if (surface === "codex") return env.CODEX_HOME?.trim() || (0, import_node_path13.join)(home, ".codex");
9386
+ if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path13.join)(home, ".kimi-code");
9387
+ if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path13.join)(home, ".config", "kilo");
9388
+ if (surface === "cursor") return (0, import_node_path13.join)(home, ".cursor");
9211
9389
  if (surface === "jervcode") {
9212
- return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path12.join)(home, ".jerv", "agent");
9390
+ return env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path13.join)(home, ".jerv", "agent");
9213
9391
  }
9214
- return (0, import_node_path12.join)(home, ".claude");
9392
+ return (0, import_node_path13.join)(home, ".claude");
9215
9393
  }
9216
9394
  var installedPluginsPath = (surface = detectSurface(process.env)) => {
9217
- return (0, import_node_path12.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
9395
+ return (0, import_node_path13.join)(surfaceConfigRoot(surface), "plugins", "installed_plugins.json");
9218
9396
  };
9219
9397
  function readInstalledPlugins(surface = detectSurface(process.env)) {
9220
9398
  try {
@@ -9227,15 +9405,15 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
9227
9405
  if (surface === "codex") {
9228
9406
  const root = surfaceConfigRoot(surface, env, home);
9229
9407
  return [
9230
- (0, import_node_path12.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
9231
- (0, import_node_path12.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
9408
+ (0, import_node_path13.join)(root, ".tmp", "marketplaces", CODEX_MARKETPLACE),
9409
+ (0, import_node_path13.join)(root, "plugins", "marketplaces", CODEX_MARKETPLACE)
9232
9410
  ];
9233
9411
  }
9234
9412
  if (surface === "kimi") return [];
9235
9413
  if (surface === "kilo") return [];
9236
9414
  if (surface === "cursor") return [];
9237
9415
  if (surface === "jervcode") return [];
9238
- return [(0, import_node_path12.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
9416
+ return [(0, import_node_path13.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
9239
9417
  }
9240
9418
  function marketplaceClonePresent(surface, home, exists = import_node_fs14.existsSync, env = process.env) {
9241
9419
  return marketplaceCloneCandidates(surface, home, env).some(exists);
@@ -9286,11 +9464,11 @@ function codexHookTrustState(status = codexPluginStatus()) {
9286
9464
  return { applicable: false, trusted: false, trustedCount: 0, requiredCount: 0 };
9287
9465
  }
9288
9466
  const root = surfaceConfigRoot("codex");
9289
- const hooksPath = (0, import_node_path12.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
9467
+ const hooksPath = (0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version, "hooks", "codex-hooks.json");
9290
9468
  const requiredCount = countCodexHookCommands(hooksPath);
9291
9469
  let config = "";
9292
9470
  try {
9293
- config = (0, import_node_fs14.readFileSync)((0, import_node_path12.join)(root, "config.toml"), "utf8");
9471
+ config = (0, import_node_fs14.readFileSync)((0, import_node_path13.join)(root, "config.toml"), "utf8");
9294
9472
  } catch {
9295
9473
  return { applicable: true, trusted: false, trustedCount: 0, requiredCount };
9296
9474
  }
@@ -9317,7 +9495,7 @@ var NPM_INSTALL_TIMEOUT_MS = 12e4;
9317
9495
  var CLI_VERSION_PROBE_TIMEOUT_MS = 3e4;
9318
9496
  function resolveNpmSpawn(args) {
9319
9497
  if (!isWin) return { file: "npm", args };
9320
- const cli = (0, import_node_path12.join)((0, import_node_path12.dirname)(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
9498
+ const cli = (0, import_node_path13.join)((0, import_node_path13.dirname)(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
9321
9499
  if ((0, import_node_fs14.existsSync)(cli)) return { file: process.execPath, args: [cli, ...args] };
9322
9500
  return { file: "cmd.exe", args: ["/c", "npm", ...args] };
9323
9501
  }
@@ -9346,9 +9524,9 @@ async function npmSelfUpdateCli(target, onStep, deps = {}) {
9346
9524
  }
9347
9525
  function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)(), read = (p) => (0, import_node_fs14.readFileSync)(p, "utf8"), exists = import_node_fs14.existsSync) {
9348
9526
  const candidates = ["kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc", "config.json"];
9349
- for (const dir of [configRoot, (0, import_node_path12.join)(home, ".kilo")]) {
9527
+ for (const dir of [configRoot, (0, import_node_path13.join)(home, ".kilo")]) {
9350
9528
  for (const file of candidates) {
9351
- const path2 = (0, import_node_path12.join)(dir, file);
9529
+ const path2 = (0, import_node_path13.join)(dir, file);
9352
9530
  if (!exists(path2)) continue;
9353
9531
  try {
9354
9532
  const stripped = read(path2).replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
@@ -9365,7 +9543,7 @@ function kiloConfigListsPlugin(configRoot, home = (0, import_node_os5.homedir)()
9365
9543
  return false;
9366
9544
  }
9367
9545
  function cursorLocalPluginRoot(env = process.env, home = (0, import_node_os5.homedir)()) {
9368
- return (0, import_node_path12.join)(surfaceConfigRoot("cursor", env, home), "plugins", "local", "mmi");
9546
+ return (0, import_node_path13.join)(surfaceConfigRoot("cursor", env, home), "plugins", "local", "mmi");
9369
9547
  }
9370
9548
  function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
9371
9549
  return [
@@ -9374,14 +9552,14 @@ function cursorPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
9374
9552
  "hooks/cursor-hooks.json",
9375
9553
  "scripts/hook-run.mjs",
9376
9554
  "scripts/hook-policy.mjs"
9377
- ].every((path2) => exists((0, import_node_path12.join)(root, ...path2.split("/"))));
9555
+ ].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
9378
9556
  }
9379
9557
  function kimiPluginTreeHealthy(root, exists = import_node_fs14.existsSync) {
9380
9558
  return [
9381
9559
  ".kimi-plugin/plugin.json",
9382
9560
  "skills/mmi/SKILL.md",
9383
9561
  "scripts/hook-run.mjs"
9384
- ].every((path2) => exists((0, import_node_path12.join)(root, ...path2.split("/"))));
9562
+ ].every((path2) => exists((0, import_node_path13.join)(root, ...path2.split("/"))));
9385
9563
  }
9386
9564
  var JERVCODE_WRAPPER_DIR = ".pi-plugin";
9387
9565
  function normalizePiEntry(value) {
@@ -9404,7 +9582,7 @@ function jervcodePackageFamily(entry) {
9404
9582
  function isMmiOwnedPiEntry(entry) {
9405
9583
  if (typeof entry !== "string") return false;
9406
9584
  try {
9407
- const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path12.join)(piEntryFsPath(entry), "package.json"), "utf8"));
9585
+ const pkg = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path13.join)(piEntryFsPath(entry), "package.json"), "utf8"));
9408
9586
  return pkg.name === "mmi";
9409
9587
  } catch {
9410
9588
  return false;
@@ -9436,8 +9614,8 @@ function readPiSettings(path2) {
9436
9614
  }
9437
9615
  }
9438
9616
  function jervcodeSettingsCandidates(env = process.env, home = (0, import_node_os5.homedir)()) {
9439
- const primary = (0, import_node_path12.join)(surfaceConfigRoot("jervcode", env, home), "settings.json");
9440
- const legacy = (0, import_node_path12.join)(home, ".pi", "agent", "settings.json");
9617
+ const primary = (0, import_node_path13.join)(surfaceConfigRoot("jervcode", env, home), "settings.json");
9618
+ const legacy = (0, import_node_path13.join)(home, ".pi", "agent", "settings.json");
9441
9619
  return legacy === primary ? [primary] : [primary, legacy];
9442
9620
  }
9443
9621
  function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir)()) {
@@ -9456,17 +9634,17 @@ function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os5.homedir
9456
9634
  function mmiPiWrapperHealthy(entry) {
9457
9635
  if (!entry) return false;
9458
9636
  const wrapper = piEntryFsPath(entry);
9459
- return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0, import_node_path12.join)((0, import_node_path12.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
9637
+ return isMmiOwnedPiEntry(entry) && (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_path13.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
9460
9638
  }
9461
9639
  function findMmiPiSourceClone(home = (0, import_node_os5.homedir)()) {
9462
- const cacheRoot = (0, import_node_path12.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
9640
+ const cacheRoot = (0, import_node_path13.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
9463
9641
  let best = null;
9464
9642
  try {
9465
9643
  for (const entry of (0, import_node_fs14.readdirSync)(cacheRoot, { withFileTypes: true })) {
9466
9644
  if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
9467
- if (!(0, import_node_fs14.existsSync)((0, import_node_path12.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
9645
+ if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
9468
9646
  if (!best || compareVersions(entry.name, best.version) > 0) {
9469
- best = { path: (0, import_node_path12.join)(cacheRoot, entry.name), version: entry.name };
9647
+ best = { path: (0, import_node_path13.join)(cacheRoot, entry.name), version: entry.name };
9470
9648
  }
9471
9649
  }
9472
9650
  } catch {
@@ -9520,7 +9698,7 @@ function healOneJervCodeSettingsFile(settingsPath2, packagePath, version, nextLa
9520
9698
  }
9521
9699
  current.packages = merged.next;
9522
9700
  try {
9523
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.dirname)(settingsPath2), { recursive: true });
9701
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.dirname)(settingsPath2), { recursive: true });
9524
9702
  const tmp = `${settingsPath2}.tmp-${process.pid}`;
9525
9703
  (0, import_node_fs14.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
9526
9704
  `, "utf8");
@@ -9551,7 +9729,7 @@ function healJervCodePackageRegistration(opts = {}) {
9551
9729
  const nextLaunch = inSeat ? " \u2014 takes effect on the next seat launch" : "";
9552
9730
  const home = opts.home ?? (0, import_node_os5.homedir)();
9553
9731
  const agentDir = surfaceConfigRoot("jervcode", env, home);
9554
- const legacyPi = (0, import_node_path12.join)(home, ".pi", "agent");
9732
+ const legacyPi = (0, import_node_path13.join)(home, ".pi", "agent");
9555
9733
  if (!(0, import_node_fs14.existsSync)(agentDir) && !(0, import_node_fs14.existsSync)(legacyPi)) {
9556
9734
  return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
9557
9735
  }
@@ -9560,8 +9738,8 @@ function healJervCodePackageRegistration(opts = {}) {
9560
9738
  return { available: true, ok: true, changed: false, version: null, detail: "skipped \u2014 no installed MMI clone carries the pi wrapper (install the Claude plugin first)" };
9561
9739
  }
9562
9740
  const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
9563
- const targets = [(0, import_node_path12.join)(agentDir, "settings.json")];
9564
- const legacySettings = (0, import_node_path12.join)(legacyPi, "settings.json");
9741
+ const targets = [(0, import_node_path13.join)(agentDir, "settings.json")];
9742
+ const legacySettings = (0, import_node_path13.join)(legacyPi, "settings.json");
9565
9743
  if ((0, import_node_fs14.existsSync)(legacyPi) && legacySettings !== targets[0]) targets.push(legacySettings);
9566
9744
  const details = [];
9567
9745
  let anyChanged = false;
@@ -9590,7 +9768,7 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
9590
9768
  return {
9591
9769
  isOrgRepo,
9592
9770
  installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // Kimi's managed plugin directory is its native install record; it has no Claude-style ledger.
9593
- surface === "kimi" && (0, import_node_fs14.existsSync)((0, import_node_path12.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
9771
+ surface === "kimi" && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
9594
9772
  surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
9595
9773
  surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs14.existsSync)(cursorLocalPluginRoot()),
9596
9774
  // Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
@@ -9599,9 +9777,9 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
9599
9777
  // Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
9600
9778
  // Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
9601
9779
  // version stamp, so the stamp's presence is the cache signal.
9602
- pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0, import_node_path12.join)((0, import_node_os5.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path12.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
9603
- codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0, import_node_path12.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
9604
- ) : (0, import_node_fs14.existsSync)((0, import_node_path12.join)(root, "plugins", "cache", "mutmutco", "mmi"))
9780
+ pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs14.existsSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path13.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
9781
+ codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
9782
+ ) : (0, import_node_fs14.existsSync)((0, import_node_path13.join)(root, "plugins", "cache", "mutmutco", "mmi"))
9605
9783
  };
9606
9784
  }
9607
9785
  async function runHostBinLogged(bin, args, opts) {
@@ -9621,9 +9799,9 @@ async function runPluginCli(bin, args, log) {
9621
9799
  function captureCodexHookLauncher() {
9622
9800
  const status = codexPluginStatus();
9623
9801
  if (!status.installed || !status.enabled || !status.version) return void 0;
9624
- const root = (0, import_node_path12.join)(surfaceConfigRoot("codex"), "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version);
9802
+ const root = (0, import_node_path13.join)(surfaceConfigRoot("codex"), "plugins", "cache", CODEX_MARKETPLACE, "mmi", status.version);
9625
9803
  const files = ["mmi-hook", "mmi-hook.exe"].flatMap((name) => {
9626
- const path2 = (0, import_node_path12.join)(root, "bin", name);
9804
+ const path2 = (0, import_node_path13.join)(root, "bin", name);
9627
9805
  try {
9628
9806
  return [{ name, content: (0, import_node_fs14.readFileSync)(path2) }];
9629
9807
  } catch {
@@ -9633,11 +9811,11 @@ function captureCodexHookLauncher() {
9633
9811
  return files.length === 2 ? { root, files } : void 0;
9634
9812
  }
9635
9813
  function restoreCodexHookLauncher(snapshot) {
9636
- if (!snapshot || (0, import_node_fs14.existsSync)((0, import_node_path12.join)(snapshot.root, "scripts", "hook-run.mjs"))) return false;
9637
- const bin = (0, import_node_path12.join)(snapshot.root, "bin");
9814
+ if (!snapshot || (0, import_node_fs14.existsSync)((0, import_node_path13.join)(snapshot.root, "scripts", "hook-run.mjs"))) return false;
9815
+ const bin = (0, import_node_path13.join)(snapshot.root, "bin");
9638
9816
  (0, import_node_fs14.mkdirSync)(bin, { recursive: true });
9639
9817
  for (const file of snapshot.files) {
9640
- const path2 = (0, import_node_path12.join)(bin, file.name);
9818
+ const path2 = (0, import_node_path13.join)(bin, file.name);
9641
9819
  (0, import_node_fs14.writeFileSync)(path2, file.content);
9642
9820
  if (file.name === "mmi-hook") (0, import_node_fs14.chmodSync)(path2, 493);
9643
9821
  }
@@ -9648,8 +9826,8 @@ function canonicalCursorRemote(remote) {
9648
9826
  }
9649
9827
  async function installCursorPluginCheckout(env = process.env) {
9650
9828
  const configRoot = surfaceConfigRoot("cursor", env);
9651
- const pluginsRoot = (0, import_node_path12.join)(configRoot, "plugins");
9652
- const target = (0, import_node_path12.join)(pluginsRoot, "local", "mmi");
9829
+ const pluginsRoot = (0, import_node_path13.join)(configRoot, "plugins");
9830
+ const target = (0, import_node_path13.join)(pluginsRoot, "local", "mmi");
9653
9831
  const source = env.MMI_CURSOR_PLUGIN_SOURCE?.trim();
9654
9832
  if ((0, import_node_fs14.existsSync)(target) && !source) {
9655
9833
  try {
@@ -9661,12 +9839,12 @@ async function installCursorPluginCheckout(env = process.env) {
9661
9839
  return { ok: false, detail: `refused to replace unmanaged Cursor plugin directory at ${target}` };
9662
9840
  }
9663
9841
  }
9664
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.join)(pluginsRoot, "local"), { recursive: true });
9665
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.join)(pluginsRoot, "staging"), { recursive: true });
9666
- (0, import_node_fs14.mkdirSync)((0, import_node_path12.join)(pluginsRoot, "quarantine"), { recursive: true });
9842
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "local"), { recursive: true });
9843
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "staging"), { recursive: true });
9844
+ (0, import_node_fs14.mkdirSync)((0, import_node_path13.join)(pluginsRoot, "quarantine"), { recursive: true });
9667
9845
  const suffix = `${Date.now()}-${process.pid}`;
9668
- const staged = (0, import_node_path12.join)(pluginsRoot, "staging", `mmi-${suffix}`);
9669
- const quarantined = (0, import_node_path12.join)(pluginsRoot, "quarantine", `mmi-${suffix}`);
9846
+ const staged = (0, import_node_path13.join)(pluginsRoot, "staging", `mmi-${suffix}`);
9847
+ const quarantined = (0, import_node_path13.join)(pluginsRoot, "quarantine", `mmi-${suffix}`);
9670
9848
  try {
9671
9849
  if (source) {
9672
9850
  (0, import_node_fs14.cpSync)(source, staged, {
@@ -9737,7 +9915,7 @@ async function runHealSteps(host, tableSteps, deps) {
9737
9915
  const refSupported = needsRefProbe ? await marketplaceAddRefSupported(host) : true;
9738
9916
  const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
9739
9917
  if (deps.banner) log(deps.banner(refSupported));
9740
- const pinsPath = (0, import_node_path12.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
9918
+ const pinsPath = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
9741
9919
  const pins = host === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
9742
9920
  let failure;
9743
9921
  try {
@@ -10048,7 +10226,7 @@ function describeUpdatePlan(hasBinary) {
10048
10226
 
10049
10227
  // src/hook-activity.ts
10050
10228
  var import_node_fs15 = require("node:fs");
10051
- var import_node_path13 = require("node:path");
10229
+ var import_node_path14 = require("node:path");
10052
10230
  var DEFAULT_SURFACE = "claude";
10053
10231
  function activityLogPath(cwd) {
10054
10232
  return repoRuntimeStatePath(cwd, "hooks", "activity.jsonl");
@@ -10061,7 +10239,7 @@ function appendHookActivity(cwd, entry) {
10061
10239
  surface: DEFAULT_SURFACE,
10062
10240
  ...entry
10063
10241
  };
10064
- (0, import_node_fs15.mkdirSync)((0, import_node_path13.dirname)(path2), { recursive: true });
10242
+ (0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
10065
10243
  (0, import_node_fs15.appendFileSync)(path2, `${JSON.stringify(line)}
10066
10244
  `, "utf8");
10067
10245
  } catch {
@@ -10070,12 +10248,12 @@ function appendHookActivity(cwd, entry) {
10070
10248
 
10071
10249
  // src/worktree.ts
10072
10250
  var import_node_fs16 = require("node:fs");
10073
- var import_node_path15 = require("node:path");
10251
+ var import_node_path16 = require("node:path");
10074
10252
 
10075
10253
  // src/file-lock.ts
10076
10254
  var import_promises2 = require("node:fs/promises");
10077
- var import_node_path14 = require("node:path");
10078
- var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
10255
+ var import_node_path15 = require("node:path");
10256
+ var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
10079
10257
  var IMMEDIATE_RETRY_BUDGET = 3;
10080
10258
  var FileLockBusyError = class extends Error {
10081
10259
  lockPath;
@@ -10159,7 +10337,7 @@ async function releaseFileLock(lockPath, guard) {
10159
10337
  }
10160
10338
  async function withFileLock(lockPath, opts, fn) {
10161
10339
  const resolved = resolveFileLockOpts(opts);
10162
- await (0, import_promises2.mkdir)((0, import_node_path14.dirname)(lockPath), { recursive: true }).catch(() => void 0);
10340
+ await (0, import_promises2.mkdir)((0, import_node_path15.dirname)(lockPath), { recursive: true }).catch(() => void 0);
10163
10341
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
10164
10342
  try {
10165
10343
  return await fn();
@@ -10214,7 +10392,7 @@ var realFsProbe = {
10214
10392
  }
10215
10393
  };
10216
10394
  function declaredProvision(fs2, abs) {
10217
- const raw = fs2.readFile?.((0, import_node_path15.join)(abs, PKG));
10395
+ const raw = fs2.readFile?.((0, import_node_path16.join)(abs, PKG));
10218
10396
  if (raw === void 0) return void 0;
10219
10397
  try {
10220
10398
  const scripts = JSON.parse(raw).scripts;
@@ -10226,13 +10404,13 @@ function declaredProvision(fs2, abs) {
10226
10404
  }
10227
10405
  function scanInstallDirs(root, fs2 = realFsProbe) {
10228
10406
  const factsFor = (dir) => {
10229
- const abs = dir ? (0, import_node_path15.join)(root, dir) : root;
10230
- const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0, import_node_path15.join)(abs, c.lockfile)));
10231
- const hasPackageJson = fs2.isFile((0, import_node_path15.join)(abs, PKG));
10407
+ const abs = dir ? (0, import_node_path16.join)(root, dir) : root;
10408
+ const match = LOCKFILE_INSTALLS.find((c) => fs2.isFile((0, import_node_path16.join)(abs, c.lockfile)));
10409
+ const hasPackageJson = fs2.isFile((0, import_node_path16.join)(abs, PKG));
10232
10410
  return {
10233
10411
  dir,
10234
10412
  hasPackageJson,
10235
- hasNodeModules: fs2.isDir((0, import_node_path15.join)(abs, NODE_MODULES)),
10413
+ hasNodeModules: fs2.isDir((0, import_node_path16.join)(abs, NODE_MODULES)),
10236
10414
  install: match?.command,
10237
10415
  provision: hasPackageJson ? declaredProvision(fs2, abs) : void 0
10238
10416
  };
@@ -10248,7 +10426,7 @@ function npmInstallTargets(dirs) {
10248
10426
  }));
10249
10427
  }
10250
10428
  function isLinkedWorktree(root, fs2 = realFsProbe) {
10251
- return fs2.isFile((0, import_node_path15.join)(root, ".git"));
10429
+ return fs2.isFile((0, import_node_path16.join)(root, ".git"));
10252
10430
  }
10253
10431
  function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
10254
10432
  if (!isLinkedWorktree(root, fs2)) return null;
@@ -10258,7 +10436,7 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
10258
10436
  return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
10259
10437
  }
10260
10438
  function defaultCopyFile(from, to) {
10261
- (0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(to), { recursive: true });
10439
+ (0, import_node_fs16.mkdirSync)((0, import_node_path16.dirname)(to), { recursive: true });
10262
10440
  (0, import_node_fs16.copyFileSync)(from, to);
10263
10441
  }
10264
10442
  async function runDeclaredProvision(target, cwd, runInstall) {
@@ -10289,7 +10467,7 @@ async function provisionWorktree(worktreeRoot, deps) {
10289
10467
  const targets = npmInstallTargets(allDirs);
10290
10468
  if (deps.validateInstall) {
10291
10469
  for (const dir of allDirs.filter((d) => d.hasPackageJson && (d.provision ?? d.install) && d.hasNodeModules)) {
10292
- const cwd = dir.dir ? (0, import_node_path15.join)(worktreeRoot, dir.dir) : worktreeRoot;
10470
+ const cwd = dir.dir ? (0, import_node_path16.join)(worktreeRoot, dir.dir) : worktreeRoot;
10293
10471
  if (!await deps.validateInstall(cwd)) {
10294
10472
  targets.push({
10295
10473
  dir: dir.dir,
@@ -10303,7 +10481,7 @@ async function provisionWorktree(worktreeRoot, deps) {
10303
10481
  const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules && !targetDirs.has(d.dir)).map((d) => d.dir);
10304
10482
  const installed = [];
10305
10483
  for (const target of targets) {
10306
- const cwd = target.dir ? (0, import_node_path15.join)(worktreeRoot, target.dir) : worktreeRoot;
10484
+ const cwd = target.dir ? (0, import_node_path16.join)(worktreeRoot, target.dir) : worktreeRoot;
10307
10485
  log(`installing deps: ${target.command} in ${target.dir || "."}`);
10308
10486
  if (target.declared) await runDeclaredProvision(target, cwd, deps.runInstall);
10309
10487
  else await deps.runInstall(target.command, cwd);
@@ -10313,7 +10491,7 @@ async function provisionWorktree(worktreeRoot, deps) {
10313
10491
  const copySkipped = [];
10314
10492
  const primary = await deps.primaryCheckout();
10315
10493
  for (const rel of LOCAL_ONLY_FILES) {
10316
- const dest = (0, import_node_path15.join)(worktreeRoot, rel);
10494
+ const dest = (0, import_node_path16.join)(worktreeRoot, rel);
10317
10495
  if (fs2.isFile(dest)) {
10318
10496
  copySkipped.push({ file: rel, reason: "already-present" });
10319
10497
  continue;
@@ -10322,11 +10500,11 @@ async function provisionWorktree(worktreeRoot, deps) {
10322
10500
  copySkipped.push({ file: rel, reason: "no-primary" });
10323
10501
  continue;
10324
10502
  }
10325
- if (!fs2.isFile((0, import_node_path15.join)(primary, rel))) {
10503
+ if (!fs2.isFile((0, import_node_path16.join)(primary, rel))) {
10326
10504
  copySkipped.push({ file: rel, reason: "absent-in-primary" });
10327
10505
  continue;
10328
10506
  }
10329
- copyFile((0, import_node_path15.join)(primary, rel), dest);
10507
+ copyFile((0, import_node_path16.join)(primary, rel), dest);
10330
10508
  copied.push(rel);
10331
10509
  log(`copied local config: ${rel}`);
10332
10510
  }
@@ -10340,12 +10518,12 @@ function capWorktreeDirName(name, max = 40) {
10340
10518
  }
10341
10519
  function defaultWorktreePath(repoRoot2, branch) {
10342
10520
  const safe = capWorktreeDirName(branch.replace(/[/\\]+/g, "-"));
10343
- return (0, import_node_path15.join)((0, import_node_path15.dirname)(repoRoot2), "mmi-worktrees", (0, import_node_path15.basename)(repoRoot2), safe);
10521
+ return (0, import_node_path16.join)((0, import_node_path16.dirname)(repoRoot2), "mmi-worktrees", (0, import_node_path16.basename)(repoRoot2), safe);
10344
10522
  }
10345
10523
  async function primaryCheckoutRootOf(git2) {
10346
10524
  try {
10347
10525
  const out = (await git2(["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
10348
- return out ? (0, import_node_path15.dirname)(out) : void 0;
10526
+ return out ? (0, import_node_path16.dirname)(out) : void 0;
10349
10527
  } catch {
10350
10528
  return void 0;
10351
10529
  }
@@ -10488,7 +10666,7 @@ function commandLadderHint() {
10488
10666
  }
10489
10667
 
10490
10668
  // src/index.ts
10491
- var import_node_path44 = require("node:path");
10669
+ var import_node_path46 = require("node:path");
10492
10670
 
10493
10671
  // src/merge-ci-policy.ts
10494
10672
  function resolveMergeCiPolicy(input) {
@@ -11162,12 +11340,12 @@ function planManagedGitignore(current) {
11162
11340
 
11163
11341
  // src/docs-index-command.ts
11164
11342
  var import_node_fs18 = require("node:fs");
11165
- var import_node_path17 = require("node:path");
11343
+ var import_node_path18 = require("node:path");
11166
11344
 
11167
11345
  // src/doc-refs-core.ts
11168
11346
  var import_node_child_process7 = require("node:child_process");
11169
11347
  var import_node_fs17 = require("node:fs");
11170
- var import_node_path16 = require("node:path");
11348
+ var import_node_path17 = require("node:path");
11171
11349
  var PIN_RE = /<!--\s*pinned by\s+([^:]+?)\s*:\s*"([^"]+)"[^"]*-->/;
11172
11350
  var PIN_MENTION_RE = /<!--\s*pinned by\b/;
11173
11351
  var FWD_RE = /<!--\s*forward-ref:\s*(\S+?)\s*-->/;
@@ -11226,7 +11404,7 @@ function checkPins(root, readFile9, docs2) {
11226
11404
  findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
11227
11405
  continue;
11228
11406
  }
11229
- const source = readFile9((0, import_node_path16.join)(root, pin.file));
11407
+ const source = readFile9((0, import_node_path17.join)(root, pin.file));
11230
11408
  if (source == null) {
11231
11409
  findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
11232
11410
  continue;
@@ -11288,11 +11466,11 @@ function checkRefs(root, deps, docs2) {
11288
11466
  for (const { ref } of extractRefs(markdown)) allFirstSegments.add(refFirstSegment(ref));
11289
11467
  }
11290
11468
  const tracked = allFirstSegments.size ? deps.trackedFirstSegments?.([...allFirstSegments]) ?? null : null;
11291
- const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path16.join)(root, first));
11469
+ const firstVerifiable = (first) => tracked ? tracked.has(first) : exists((0, import_node_path17.join)(root, first));
11292
11470
  const candidates = [];
11293
11471
  const direct = [];
11294
11472
  for (const [doc, markdown] of Object.entries(docs2)) {
11295
- const docDir = import_node_path16.posix.dirname(doc);
11473
+ const docDir = import_node_path17.posix.dirname(doc);
11296
11474
  const base = docDir === "." ? "" : docDir;
11297
11475
  const covered = /* @__PURE__ */ new Set();
11298
11476
  const markers = [];
@@ -11301,21 +11479,21 @@ function checkRefs(root, deps, docs2) {
11301
11479
  direct.push({ kind: "malformed-forward-ref", doc, line: fwd.line, detail: fwd.text });
11302
11480
  continue;
11303
11481
  }
11304
- const docRel = import_node_path16.posix.normalize(import_node_path16.posix.join(base, fwd.target));
11305
- const rootRel = import_node_path16.posix.normalize(fwd.target.replace(/^\/+/, ""));
11482
+ const docRel = import_node_path17.posix.normalize(import_node_path17.posix.join(base, fwd.target));
11483
+ const rootRel = import_node_path17.posix.normalize(fwd.target.replace(/^\/+/, ""));
11306
11484
  markers.push({ target: fwd.target, line: fwd.line, docRel, rootRel });
11307
11485
  covered.add(docRel);
11308
11486
  covered.add(rootRel);
11309
11487
  }
11310
11488
  const links = extractLinks(markdown).map(({ target, line }) => {
11311
- const resolved = import_node_path16.posix.normalize(import_node_path16.posix.join(base, target));
11312
- return { target, line, resolved, missing: !exists((0, import_node_path16.join)(root, resolved)) };
11489
+ const resolved = import_node_path17.posix.normalize(import_node_path17.posix.join(base, target));
11490
+ return { target, line, resolved, missing: !exists((0, import_node_path17.join)(root, resolved)) };
11313
11491
  });
11314
11492
  for (const marker of markers) {
11315
11493
  const coversMissing = links.some(
11316
11494
  (l) => l.missing && (l.resolved === marker.docRel || l.resolved === marker.rootRel)
11317
11495
  );
11318
- if (!coversMissing && (exists((0, import_node_path16.join)(root, marker.docRel)) || exists((0, import_node_path16.join)(root, marker.rootRel)))) {
11496
+ if (!coversMissing && (exists((0, import_node_path17.join)(root, marker.docRel)) || exists((0, import_node_path17.join)(root, marker.rootRel)))) {
11319
11497
  direct.push({
11320
11498
  kind: "stale-forward-ref",
11321
11499
  doc,
@@ -11326,7 +11504,7 @@ function checkRefs(root, deps, docs2) {
11326
11504
  }
11327
11505
  for (const { ref, line } of extractRefs(markdown)) {
11328
11506
  if (!firstVerifiable(refFirstSegment(ref))) continue;
11329
- if (!exists((0, import_node_path16.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
11507
+ if (!exists((0, import_node_path17.join)(root, ref))) candidates.push({ kind: "missing-path", doc, line, detail: ref });
11330
11508
  }
11331
11509
  for (const { target, line, resolved, missing } of links) {
11332
11510
  if (resolved.startsWith("..")) {
@@ -11378,18 +11556,18 @@ function readFileOrNull(path2) {
11378
11556
  }
11379
11557
  function walk(dir, root, out) {
11380
11558
  for (const entry of (0, import_node_fs17.readdirSync)(dir)) {
11381
- const full = (0, import_node_path16.join)(dir, entry);
11559
+ const full = (0, import_node_path17.join)(dir, entry);
11382
11560
  if ((0, import_node_fs17.statSync)(full).isDirectory()) walk(full, root, out);
11383
11561
  else if (entry.endsWith(".md")) out.push(full.slice(root.length + 1).replaceAll("\\", "/"));
11384
11562
  }
11385
11563
  return out;
11386
11564
  }
11387
11565
  function defaultListDocs(root) {
11388
- const docsDir = (0, import_node_path16.join)(root, "docs");
11566
+ const docsDir = (0, import_node_path17.join)(root, "docs");
11389
11567
  const docs2 = ((0, import_node_fs17.existsSync)(docsDir) ? walk(docsDir, root, []) : []).filter(
11390
11568
  (rel) => !SKIP_WALK.some((skip) => rel.startsWith(skip))
11391
11569
  );
11392
- return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0, import_node_path16.join)(root, rel))), ...docs2];
11570
+ return [...ROOT_DOCS.filter((rel) => (0, import_node_fs17.existsSync)((0, import_node_path17.join)(root, rel))), ...docs2];
11393
11571
  }
11394
11572
  var CHECK_IGNORE_MAX_BUFFER = 32 * 1024 * 1024;
11395
11573
  function defaultIsIgnored(root, relPaths, exec = import_node_child_process7.execFileSync) {
@@ -11449,7 +11627,7 @@ function runDocRefs(root, deps = {}) {
11449
11627
  const walked = listDocs(root);
11450
11628
  const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
11451
11629
  const docs2 = Object.fromEntries(
11452
- walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path16.join)(root, rel))]).filter(([, body]) => body != null)
11630
+ walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile9((0, import_node_path17.join)(root, rel))]).filter(([, body]) => body != null)
11453
11631
  );
11454
11632
  const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs2);
11455
11633
  const findings = [
@@ -11557,19 +11735,19 @@ function walkMarkdown(dir) {
11557
11735
  while (stack.length) {
11558
11736
  const current = stack.pop();
11559
11737
  for (const entry of (0, import_node_fs18.readdirSync)(current, { withFileTypes: true })) {
11560
- const full = (0, import_node_path17.join)(current, entry.name);
11738
+ const full = (0, import_node_path18.join)(current, entry.name);
11561
11739
  if (entry.isDirectory()) {
11562
11740
  stack.push(full);
11563
11741
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
11564
- out.push((0, import_node_path17.relative)(dir, full).split(import_node_path17.sep).join("/"));
11742
+ out.push((0, import_node_path18.relative)(dir, full).split(import_node_path18.sep).join("/"));
11565
11743
  }
11566
11744
  }
11567
11745
  }
11568
11746
  return out;
11569
11747
  }
11570
11748
  function createDocsIndexDeps(repoRoot2) {
11571
- const docsDir = (0, import_node_path17.join)(repoRoot2, "docs");
11572
- const indexPath = (0, import_node_path17.join)(repoRoot2, DOCS_INDEX_PATH);
11749
+ const docsDir = (0, import_node_path18.join)(repoRoot2, "docs");
11750
+ const indexPath = (0, import_node_path18.join)(repoRoot2, DOCS_INDEX_PATH);
11573
11751
  return {
11574
11752
  listDocs: () => {
11575
11753
  if (!(0, import_node_fs18.existsSync)(docsDir)) return [];
@@ -11578,7 +11756,7 @@ function createDocsIndexDeps(repoRoot2) {
11578
11756
  const ignored = defaultIsIgnored(repoRoot2, walked.map((rel) => `docs/${rel}`));
11579
11757
  return walked.filter((rel) => !ignored.has(`docs/${rel}`)).sort();
11580
11758
  },
11581
- readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0, import_node_path17.join)(docsDir, relPath), "utf8"),
11759
+ readDoc: (relPath) => (0, import_node_fs18.readFileSync)((0, import_node_path18.join)(docsDir, relPath), "utf8"),
11582
11760
  readIndex: () => (0, import_node_fs18.existsSync)(indexPath) ? (0, import_node_fs18.readFileSync)(indexPath, "utf8") : null,
11583
11761
  writeIndex: (content) => (0, import_node_fs18.writeFileSync)(indexPath, content, "utf8")
11584
11762
  };
@@ -12338,6 +12516,12 @@ function extractControlOutputFromLog(log) {
12338
12516
  const slice = end < 0 ? lines.slice(start + 1) : lines.slice(start + 1, end);
12339
12517
  return slice.map(logLinePayload).join("\n").trim();
12340
12518
  }
12519
+ function clampTaskOutput(output, maxChars = 16384) {
12520
+ if (output.length <= maxChars) return output;
12521
+ const kept = output.slice(output.length - maxChars);
12522
+ return `[task output truncated by mmi-cli \u2014 last ${maxChars} of ${output.length} chars]
12523
+ ${kept}`;
12524
+ }
12341
12525
  function parseStatusSnippet(stdout) {
12342
12526
  const t = stdout.toLowerCase();
12343
12527
  const m = t.match(/service[:=]\s*(running|stopped|missing|up|down|absent)/);
@@ -12367,7 +12551,7 @@ function parseVerifyBroker(stdout) {
12367
12551
  // src/train-apply.ts
12368
12552
  var import_node_fs21 = require("node:fs");
12369
12553
  var import_promises4 = require("node:fs/promises");
12370
- var import_node_path20 = require("node:path");
12554
+ var import_node_path21 = require("node:path");
12371
12555
 
12372
12556
  // src/bootstrap-org-ruleset.ts
12373
12557
  var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
@@ -12840,7 +13024,7 @@ function renderAccessReport(report) {
12840
13024
 
12841
13025
  // src/cli-doctor-shared.ts
12842
13026
  var import_node_fs19 = require("node:fs");
12843
- var import_node_path19 = require("node:path");
13027
+ var import_node_path20 = require("node:path");
12844
13028
  var import_node_fs20 = require("node:fs");
12845
13029
 
12846
13030
  // ../infra/registry-endpoints.mjs
@@ -12970,9 +13154,9 @@ function extractWorkflowCrons(yamlText) {
12970
13154
  function workflowEntry(repo, workflowPath, yamlText) {
12971
13155
  const crons = extractWorkflowCrons(yamlText);
12972
13156
  if (!crons.length) return null;
12973
- const basename6 = workflowPath.split("/").pop() ?? workflowPath;
13157
+ const basename8 = workflowPath.split("/").pop() ?? workflowPath;
12974
13158
  return {
12975
- name: `${repo}/${basename6.replace(/\.ya?ml$/, "")}`,
13159
+ name: `${repo}/${basename8.replace(/\.ya?ml$/, "")}`,
12976
13160
  cadence: crons.join(" + "),
12977
13161
  executor: "github-actions",
12978
13162
  llm: llmFromHeader(yamlText),
@@ -13382,8 +13566,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
13382
13566
  const crons = extractWorkflowCrons(yamlText);
13383
13567
  const header = parseScheduleHeader(yamlText);
13384
13568
  if (!crons.length && !header.schedule) return null;
13385
- const basename6 = workflowPath.split("/").pop() ?? workflowPath;
13386
- const expectedId = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
13569
+ const basename8 = workflowPath.split("/").pop() ?? workflowPath;
13570
+ const expectedId = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
13387
13571
  if (isHarbourUnmanaged(yamlText)) return null;
13388
13572
  const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
13389
13573
  if (missing.length) {
@@ -13628,6 +13812,9 @@ async function postSchedulesUnpark(id, deps) {
13628
13812
  async function tenantControl(payload, deps) {
13629
13813
  return postJson("/tenant-control", payload, deps, "POST", { noRetry: true });
13630
13814
  }
13815
+ async function tenantArtifactUpload(payload, deps) {
13816
+ return postJson("/tenant-control", { ...payload, action: "artifact-upload" }, deps, "POST", { noRetry: true });
13817
+ }
13631
13818
  async function tenantReconcile(payload, deps) {
13632
13819
  return postJson("/tenant-reconcile", payload, deps, "POST", { noRetry: true });
13633
13820
  }
@@ -13755,7 +13942,7 @@ var import_node_os7 = require("node:os");
13755
13942
  // src/gh-create.ts
13756
13943
  var import_promises3 = require("node:fs/promises");
13757
13944
  var import_node_os6 = require("node:os");
13758
- var import_node_path18 = require("node:path");
13945
+ var import_node_path19 = require("node:path");
13759
13946
  var import_node_crypto3 = require("node:crypto");
13760
13947
 
13761
13948
  // src/board-priority.ts
@@ -13838,8 +14025,8 @@ async function bodyArgsViaFile(args, deps = {}) {
13838
14025
  const remove2 = deps.remove ?? import_promises3.unlink;
13839
14026
  const ensureDir = deps.ensureDir ?? import_promises3.mkdir;
13840
14027
  const dir = deps.dir ?? (0, import_node_os6.tmpdir)();
13841
- const file = (0, import_node_path18.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.md`);
13842
- await ensureDir((0, import_node_path18.dirname)(file), { recursive: true }).catch(() => {
14028
+ const file = (0, import_node_path19.join)(dir, `mmi-gh-body-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}.md`);
14029
+ await ensureDir((0, import_node_path19.dirname)(file), { recursive: true }).catch(() => {
13843
14030
  });
13844
14031
  await write(file, args[i + 1], "utf8");
13845
14032
  return {
@@ -13930,7 +14117,7 @@ function upstreamFaultMessage(verb, stderr) {
13930
14117
  }
13931
14118
  async function ghCreate(args, deps = {}) {
13932
14119
  const exec = deps.exec ?? execFileP2;
13933
- const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
14120
+ const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
13934
14121
  const swapped = await bodyArgsViaFile(args);
13935
14122
  try {
13936
14123
  for (let attempt = 1; attempt <= GH_CREATE_UPSTREAM_RETRIES; attempt++) {
@@ -15136,8 +15323,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
15136
15323
  async function drainHttpPoolBeforeSpawn() {
15137
15324
  const drained = await Promise.race([
15138
15325
  closeHttpPool().then(() => true),
15139
- new Promise((resolve5) => {
15140
- setTimeout(() => resolve5(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
15326
+ new Promise((resolve6) => {
15327
+ setTimeout(() => resolve6(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
15141
15328
  })
15142
15329
  ]);
15143
15330
  if (!drained) destroyHttpPool();
@@ -15382,7 +15569,7 @@ async function localBranchHeads() {
15382
15569
  }
15383
15570
  async function currentRepoWorktreeGitRoot(repoRoot2) {
15384
15571
  const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
15385
- return gitCommonDir ? (0, import_node_path19.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
15572
+ return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot2, gitCommonDir, "worktrees") : "";
15386
15573
  }
15387
15574
  async function worktreeBranches() {
15388
15575
  const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
@@ -15402,7 +15589,7 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
15402
15589
  const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
15403
15590
  if (!match?.[1]) return void 0;
15404
15591
  const raw = match[1].trim();
15405
- return (0, import_node_path19.isAbsolute)(raw) ? raw : (0, import_node_path19.resolve)(worktreePath, raw);
15592
+ return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
15406
15593
  }
15407
15594
  function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
15408
15595
  if (!worktreeGitRoot) return false;
@@ -15411,9 +15598,9 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
15411
15598
  for (const ent of entries) {
15412
15599
  if (!ent.isDirectory()) continue;
15413
15600
  try {
15414
- const gitdirPath = (0, import_node_fs19.readFileSync)((0, import_node_path19.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
15415
- const resolvedGitdir = (0, import_node_path19.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path19.resolve)(worktreeGitRoot, ent.name, gitdirPath);
15416
- if (sameWorktreeMetadataPath((0, import_node_path19.dirname)(resolvedGitdir), worktreePath)) return true;
15601
+ const gitdirPath = (0, import_node_fs19.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
15602
+ const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
15603
+ if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
15417
15604
  } catch {
15418
15605
  }
15419
15606
  }
@@ -15432,7 +15619,7 @@ function pathExistsKnown(path2) {
15432
15619
  }
15433
15620
  }
15434
15621
  function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
15435
- const gitPath = (0, import_node_path19.join)(path2, ".git");
15622
+ const gitPath = (0, import_node_path20.join)(path2, ".git");
15436
15623
  let st;
15437
15624
  try {
15438
15625
  st = (0, import_node_fs20.lstatSync)(gitPath);
@@ -15486,24 +15673,25 @@ async function preservedBranches() {
15486
15673
  async function siblingWorktreeDirs(explicitRoot) {
15487
15674
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
15488
15675
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
15489
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path19.dirname)((0, import_node_path19.dirname)(worktreeGitRoot)) : repoRoot2;
15676
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path20.dirname)((0, import_node_path20.dirname)(worktreeGitRoot)) : repoRoot2;
15490
15677
  try {
15491
15678
  const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
15492
15679
  const agentDirs2 = listDirsIn(agentWorktreesRoot(primaryRepoRoot));
15493
- return [...dirs, ...agentDirs2].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
15680
+ const repoLocalWorktrees = listDirsIn((0, import_node_path20.join)(primaryRepoRoot, ".worktrees"));
15681
+ return [...dirs, ...agentDirs2, ...repoLocalWorktrees].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
15494
15682
  } catch {
15495
15683
  return [];
15496
15684
  }
15497
15685
  }
15498
15686
  function listDirsIn(dir) {
15499
15687
  try {
15500
- return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path19.join)(dir, ent.name));
15688
+ return (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true }).filter((ent) => ent.isDirectory()).map((ent) => (0, import_node_path20.join)(dir, ent.name));
15501
15689
  } catch {
15502
15690
  return [];
15503
15691
  }
15504
15692
  }
15505
15693
  function isRepoCheckoutDir(dir) {
15506
- return (0, import_node_fs20.existsSync)((0, import_node_path19.join)(dir, ".git"));
15694
+ return (0, import_node_fs20.existsSync)((0, import_node_path20.join)(dir, ".git"));
15507
15695
  }
15508
15696
  function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
15509
15697
  let rootDirs;
@@ -15515,7 +15703,7 @@ function resolveExplicitScanRoot(explicitRoot, repoRoot2) {
15515
15703
  return explicitRepoWorktreesRoot(explicitRoot, repoRoot2, rootDirs);
15516
15704
  }
15517
15705
  async function gcPlan(remote, limit, opts = {}) {
15518
- const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase] = await Promise.all([
15706
+ const [branches, heads, current, stale, worktrees, siblingDirs, preserved, mergedIntoBase, originListed] = await Promise.all([
15519
15707
  gitOut(["branch", "--format=%(refname:short)"]),
15520
15708
  localBranchHeads(),
15521
15709
  gitOut(["rev-parse", "--abbrev-ref", "HEAD"]),
@@ -15525,11 +15713,18 @@ async function gcPlan(remote, limit, opts = {}) {
15525
15713
  worktreeBranches(),
15526
15714
  siblingWorktreeDirs(opts.root),
15527
15715
  preservedBranches(),
15528
- branchesMergedIntoBase(remote)
15716
+ branchesMergedIntoBase(remote),
15717
+ gitOut(["branch", "-r", "--format=%(refname:short)"]).catch(() => "")
15529
15718
  ]);
15530
15719
  const localBranches = branches.split(/\r?\n/).map((b) => b.trim()).filter(Boolean);
15720
+ const originBranches = originListed.split(/\r?\n/).map((b) => b.trim()).filter((b) => b.startsWith(`${remote}/`) && b !== `${remote}/HEAD`);
15721
+ const originNames = originBranches.map((b) => b.slice(remote.length + 1));
15531
15722
  const { prs, failures } = await resolveBranchPrs(
15532
- [...localBranches, ...stale.map((ref) => branchForTrackingRef(ref, remote)).filter((b) => Boolean(b))].filter((b) => !isProtectedBranch(b)),
15723
+ [.../* @__PURE__ */ new Set([
15724
+ ...localBranches,
15725
+ ...stale.map((ref) => branchForTrackingRef(ref, remote)).filter((b) => Boolean(b)),
15726
+ ...originNames
15727
+ ])].filter((b) => !isProtectedBranch(b)),
15533
15728
  limit
15534
15729
  );
15535
15730
  return buildGcPlan({
@@ -15543,7 +15738,9 @@ async function gcPlan(remote, limit, opts = {}) {
15543
15738
  worktrees,
15544
15739
  remote,
15545
15740
  preservedBranches: preserved,
15546
- mergedIntoBase
15741
+ mergedIntoBase,
15742
+ originBranches,
15743
+ trainOnly: opts.trainOnly
15547
15744
  });
15548
15745
  }
15549
15746
  async function branchesMergedIntoBase(remote) {
@@ -15840,10 +16037,10 @@ var rollout_plan_default = {
15840
16037
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
15841
16038
  },
15842
16039
  baseline: {
15843
- version: "3.126.0",
15844
- tag: "v3.126.0",
15845
- commit: "1ccba0834c2a",
15846
- npm: "@mutmutco/cli@3.126.0"
16040
+ version: "3.128.0",
16041
+ tag: "v3.128.0",
16042
+ commit: "95241fa22be9",
16043
+ npm: "@mutmutco/cli@3.128.0"
15847
16044
  },
15848
16045
  exitCriterion: "fleet-n-of-n",
15849
16046
  hubOnlyShortcut: "forbidden",
@@ -15860,14 +16057,14 @@ var rollout_plan_default = {
15860
16057
  repo: "mutmutco/mmi-hub",
15861
16058
  role: "canary",
15862
16059
  schedule: "train",
15863
- v3Target: "v3.126.0"
16060
+ v3Target: "v3.128.0"
15864
16061
  }
15865
16062
  ],
15866
16063
  rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
15867
16064
  rollback: {
15868
16065
  independent: true,
15869
- mechanism: "npm dist-tag latest -> 3.126.0 and redeploy the Hub Lambda from tag v3.126.0 (1ccba0834c2a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15870
- v3Target: "v3.126.0 (@mutmutco/cli@3.126.0, tag commit 1ccba0834c2a \u2014 the preserved latest-v3 distribution, D6b)"
16066
+ mechanism: "npm dist-tag latest -> 3.128.0 and redeploy the Hub Lambda from tag v3.128.0 (95241fa22be9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16067
+ v3Target: "v3.128.0 (@mutmutco/cli@3.128.0, tag commit 95241fa22be9 \u2014 the preserved latest-v3 distribution, D6b)"
15871
16068
  }
15872
16069
  },
15873
16070
  {
@@ -18116,7 +18313,7 @@ function publishVisibilityFor(surfaceId) {
18116
18313
  }
18117
18314
  function npmPackArtifactName(packagePath) {
18118
18315
  try {
18119
- const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path20.join)(packagePath, "package.json"), "utf8"));
18316
+ const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path21.join)(packagePath, "package.json"), "utf8"));
18120
18317
  return pkg.name || void 0;
18121
18318
  } catch {
18122
18319
  return void 0;
@@ -18274,7 +18471,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
18274
18471
  async function predictMergeConflicts(deps, ours, theirs) {
18275
18472
  return runMergeTreePreflight(deps, ours, theirs);
18276
18473
  }
18277
- async function mergeWithToleratedResolution(deps, sourceRef, label, resolve5, extraTolerated = []) {
18474
+ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve6, extraTolerated = []) {
18278
18475
  try {
18279
18476
  await deps.run("git", ["merge", sourceRef, "--no-edit"]);
18280
18477
  return;
@@ -18288,7 +18485,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve5, ex
18288
18485
  unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
18289
18486
  );
18290
18487
  }
18291
- await deps.run("git", ["checkout", `--${resolve5}`, "--", ...unmerged]);
18488
+ await deps.run("git", ["checkout", `--${resolve6}`, "--", ...unmerged]);
18292
18489
  await deps.run("git", ["add", "--", ...unmerged]);
18293
18490
  await deps.run("git", ["commit", "--no-edit"]);
18294
18491
  }
@@ -18499,7 +18696,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
18499
18696
  var CORRELATE_PAGE_LIMIT = 50;
18500
18697
  var RUN_CONFIRM_ATTEMPTS = 3;
18501
18698
  var RUN_CONFIRM_DELAY_MS = 1e3;
18502
- var defaultSleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
18699
+ var defaultSleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
18503
18700
  function resolveSleep(deps) {
18504
18701
  return deps.sleep ?? defaultSleep2;
18505
18702
  }
@@ -19287,7 +19484,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
19287
19484
  return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
19288
19485
  }
19289
19486
  function readLocalGateWorkflows() {
19290
- const dir = (0, import_node_path20.join)(".github", "workflows");
19487
+ const dir = (0, import_node_path21.join)(".github", "workflows");
19291
19488
  let names;
19292
19489
  try {
19293
19490
  names = (0, import_node_fs21.readdirSync)(dir);
@@ -19297,7 +19494,7 @@ function readLocalGateWorkflows() {
19297
19494
  const files = [];
19298
19495
  for (const name of names.filter(isGateWorkflowPath)) {
19299
19496
  try {
19300
- files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0, import_node_path20.join)(dir, name), "utf8") });
19497
+ files.push({ path: `${dir}/${name}`.replace(/\\/g, "/"), body: (0, import_node_fs21.readFileSync)((0, import_node_path21.join)(dir, name), "utf8") });
19301
19498
  } catch {
19302
19499
  }
19303
19500
  }
@@ -20420,14 +20617,14 @@ async function runTenantReconcile(deps, options) {
20420
20617
  };
20421
20618
  }
20422
20619
  function tenantControlWatches(action) {
20423
- return action === "status" || action === "retire" || action === "verify-secrets" || action === "verify-broker" || action === "logs";
20620
+ return action === "status" || action === "retire" || action === "verify-secrets" || action === "verify-broker" || action === "logs" || action === "run-task";
20424
20621
  }
20425
20622
  async function runTenantControl(deps, options) {
20426
20623
  const { repo, stage, action } = options;
20427
20624
  const watch = options.watch ?? tenantControlWatches(action);
20428
20625
  const base = { command: "tenant-control", repo, stage, action };
20429
20626
  const since = (deps.now ?? Date.now)();
20430
- const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines });
20627
+ const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines, task: options.task, artifact: options.artifact });
20431
20628
  if (!d.ok) {
20432
20629
  const transport = d.category === "transport-failed";
20433
20630
  return {
@@ -20438,7 +20635,7 @@ async function runTenantControl(deps, options) {
20438
20635
  note: transport ? `runtime tenant control ${action} dispatch failed (transport) \u2014 safe to retry` : `runtime tenant control ${action} rejected: ${d.error ?? "request rejected by the Hub"}`
20439
20636
  };
20440
20637
  }
20441
- const correlated = await correlateControlRun(deps, since, [stage, action]);
20638
+ const correlated = await correlateControlRun(deps, since, [stage, action, ...action === "run-task" && options.task ? [options.task] : []]);
20442
20639
  if (correlated.read === "failed") {
20443
20640
  return {
20444
20641
  ...base,
@@ -20474,6 +20671,13 @@ async function runTenantControl(deps, options) {
20474
20671
  result.brokerRaw = output;
20475
20672
  }
20476
20673
  }
20674
+ if (watch && runId != null && action === "run-task" && (conclusion === "success" || conclusion === "failure")) {
20675
+ const fetched = await fetchControlRunLog(deps, runId);
20676
+ if (fetched.ok) {
20677
+ const output = extractControlOutputFromLog(fetched.log);
20678
+ if (output) result.taskOutput = clampTaskOutput(output);
20679
+ }
20680
+ }
20477
20681
  result.note = conclusion === "success" ? `tenant-control ${action} run succeeded` : conclusion === "failure" ? `tenant-control ${action} run failed \u2014 inspect the run` : runId == null ? `dispatched tenant-control.yml (${action}) \u2014 run not correlated; check the Actions tab` : `dispatched tenant-control.yml (${action}) \u2014 not watched`;
20478
20682
  return result;
20479
20683
  }
@@ -20485,6 +20689,10 @@ function renderTenantControl(r) {
20485
20689
  if (r.secrets?.length) {
20486
20690
  for (const s of r.secrets) lines.push(` ${s.key}: ${s.status}`);
20487
20691
  }
20692
+ if (r.taskOutput) {
20693
+ lines.push(" task output:");
20694
+ for (const l of r.taskOutput.split("\n")) lines.push(` ${l}`);
20695
+ }
20488
20696
  lines.push(` ${r.note}`);
20489
20697
  return lines.join("\n");
20490
20698
  }
@@ -21417,7 +21625,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
21417
21625
  if (first.mergeStatus !== "failed") return first;
21418
21626
  const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
21419
21627
  if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
21420
- const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
21628
+ const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
21421
21629
  await sleep3(PR_LAND_MERGE_RETRY_DELAY_MS);
21422
21630
  const retried = await deps.mergeAuto(prNumber, repo);
21423
21631
  if (retried.mergeStatus !== "failed") return retried;
@@ -21428,7 +21636,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 3e3;
21428
21636
  async function confirmAutoMergeEnqueued(deps, options) {
21429
21637
  const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
21430
21638
  const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
21431
- const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
21639
+ const sleep3 = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
21432
21640
  for (let attempt = 0; attempt < retries; attempt++) {
21433
21641
  if (await deps.readMerged().catch(() => false)) return "merged";
21434
21642
  const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
@@ -21445,7 +21653,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
21445
21653
  async function readGhPrStateWithRetry(fetchState, options) {
21446
21654
  const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
21447
21655
  const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
21448
- const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
21656
+ const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
21449
21657
  let lastError = "empty state";
21450
21658
  for (let attempt = 0; attempt < retries; attempt++) {
21451
21659
  try {
@@ -21523,7 +21731,7 @@ var import_node_fs23 = require("node:fs");
21523
21731
  // src/stage-runner.ts
21524
21732
  var import_node_child_process9 = require("node:child_process");
21525
21733
  var import_node_fs22 = require("node:fs");
21526
- var import_node_path21 = require("node:path");
21734
+ var import_node_path22 = require("node:path");
21527
21735
  var import_node_net = require("node:net");
21528
21736
  var import_node_util5 = require("node:util");
21529
21737
 
@@ -21552,7 +21760,7 @@ function healthPollIntervalMs() {
21552
21760
  return HEALTH_POLL_INTERVAL_MS;
21553
21761
  }
21554
21762
  function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
21555
- return new Promise((resolve5, reject) => {
21763
+ return new Promise((resolve6, reject) => {
21556
21764
  let settled = false;
21557
21765
  const finish = (fn) => {
21558
21766
  if (settled) return;
@@ -21562,7 +21770,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
21562
21770
  child2.removeAllListeners("exit");
21563
21771
  fn();
21564
21772
  };
21565
- const timer = setTimeout(() => finish(resolve5), graceMs);
21773
+ const timer = setTimeout(() => finish(resolve6), graceMs);
21566
21774
  child2.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
21567
21775
  child2.on("exit", (code, signal) => {
21568
21776
  const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
@@ -21662,11 +21870,11 @@ function appendForceRecreate(up) {
21662
21870
  return `${up.trimEnd()} --force-recreate`;
21663
21871
  }
21664
21872
  function stageStatePath(cwd = process.cwd()) {
21665
- return (0, import_node_path21.join)(cwd, "tmp", "stage", "state.json");
21873
+ return (0, import_node_path22.join)(cwd, "tmp", "stage", "state.json");
21666
21874
  }
21667
21875
  function stageGlobalStatePath(cwd = process.cwd(), gitCommonDir = ".git") {
21668
- const dir = (0, import_node_path21.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path21.resolve)(cwd, gitCommonDir);
21669
- return (0, import_node_path21.join)(dir, "mmi", "stage", "state.json");
21876
+ const dir = (0, import_node_path22.isAbsolute)(gitCommonDir) ? gitCommonDir : (0, import_node_path22.resolve)(cwd, gitCommonDir);
21877
+ return (0, import_node_path22.join)(dir, "mmi", "stage", "state.json");
21670
21878
  }
21671
21879
  function normPath3(path2) {
21672
21880
  return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -21772,10 +21980,10 @@ function pickStagePort(range, isFree) {
21772
21980
  throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
21773
21981
  }
21774
21982
  function isPortFree(port) {
21775
- return new Promise((resolve5) => {
21983
+ return new Promise((resolve6) => {
21776
21984
  const srv = (0, import_node_net.createServer)();
21777
- srv.once("error", () => resolve5(false));
21778
- srv.once("listening", () => srv.close(() => resolve5(true)));
21985
+ srv.once("error", () => resolve6(false));
21986
+ srv.once("listening", () => srv.close(() => resolve6(true)));
21779
21987
  srv.listen(port, "127.0.0.1");
21780
21988
  });
21781
21989
  }
@@ -21890,8 +22098,8 @@ function stageProcessEnv(stagePort, extraEnv) {
21890
22098
  }
21891
22099
  async function ensureStageRuntimeEnv(config, opts, cwd) {
21892
22100
  if (!config.ensureEnv) return;
21893
- const target = (0, import_node_path21.join)(cwd, config.ensureEnv.target);
21894
- const example = (0, import_node_path21.join)(cwd, config.ensureEnv.example);
22101
+ const target = (0, import_node_path22.join)(cwd, config.ensureEnv.target);
22102
+ const example = (0, import_node_path22.join)(cwd, config.ensureEnv.example);
21895
22103
  if (!(0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
21896
22104
  (0, import_node_fs22.copyFileSync)(example, target);
21897
22105
  } else if ((0, import_node_fs22.existsSync)(target) && (0, import_node_fs22.existsSync)(example)) {
@@ -21986,7 +22194,7 @@ async function killTree(pid) {
21986
22194
  } catch {
21987
22195
  }
21988
22196
  }
21989
- await new Promise((resolve5) => setTimeout(resolve5, 500));
22197
+ await new Promise((resolve6) => setTimeout(resolve6, 500));
21990
22198
  try {
21991
22199
  process.kill(-pid, "SIGKILL");
21992
22200
  } catch {
@@ -22007,7 +22215,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
22007
22215
  } catch (e) {
22008
22216
  last = e.message;
22009
22217
  }
22010
- await new Promise((resolve5) => setTimeout(resolve5, healthPollIntervalMs()));
22218
+ await new Promise((resolve6) => setTimeout(resolve6, healthPollIntervalMs()));
22011
22219
  }
22012
22220
  throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
22013
22221
  }
@@ -22256,13 +22464,13 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
22256
22464
  }
22257
22465
 
22258
22466
  // src/index.ts
22259
- var import_node_os19 = require("node:os");
22467
+ var import_node_os20 = require("node:os");
22260
22468
 
22261
22469
  // src/board.ts
22262
22470
  var import_node_child_process10 = require("node:child_process");
22263
22471
  var import_node_fs24 = require("node:fs");
22264
22472
  var import_node_os8 = require("node:os");
22265
- var import_node_path22 = require("node:path");
22473
+ var import_node_path23 = require("node:path");
22266
22474
  var import_node_util6 = require("node:util");
22267
22475
 
22268
22476
  // src/board-dependency.ts
@@ -23542,7 +23750,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
23542
23750
  await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
23543
23751
  return cliPriorityToFieldName(priority);
23544
23752
  }
23545
- var defaultRetrySleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
23753
+ var defaultRetrySleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
23546
23754
  async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
23547
23755
  const attempts = Math.max(1, opts.attempts ?? 5);
23548
23756
  const delayMs = opts.delayMs ?? 300;
@@ -24005,14 +24213,14 @@ function probeLocalClaimSession(marker, now = Date.now()) {
24005
24213
  claimSessionProbeCache.set(cacheKey, { checkedAt: now, state });
24006
24214
  return state;
24007
24215
  };
24008
- const root = (0, import_node_path22.join)((0, import_node_os8.homedir)(), ".claude", "projects");
24216
+ const root = (0, import_node_path23.join)((0, import_node_os8.homedir)(), ".claude", "projects");
24009
24217
  try {
24010
24218
  const wanted = `${marker.session}.jsonl`.toLowerCase();
24011
24219
  const pending = [root];
24012
24220
  while (pending.length) {
24013
24221
  const dir = pending.pop();
24014
24222
  for (const entry of (0, import_node_fs24.readdirSync)(dir, { withFileTypes: true })) {
24015
- const path2 = (0, import_node_path22.join)(dir, entry.name);
24223
+ const path2 = (0, import_node_path23.join)(dir, entry.name);
24016
24224
  if (entry.isDirectory()) pending.push(path2);
24017
24225
  else if (entry.isFile() && entry.name.toLowerCase() === wanted) {
24018
24226
  return remember(now - (0, import_node_fs24.statSync)(path2).mtimeMs <= CLAIM_SESSION_ACTIVITY_MS ? "live" : "dead");
@@ -24653,7 +24861,7 @@ function buildOption(opt) {
24653
24861
  if (opt.hidden) out.discovery = "all-only";
24654
24862
  return out;
24655
24863
  }
24656
- function buildCommand(cmd, path2) {
24864
+ function buildCommand(cmd, flatPath) {
24657
24865
  const metadata = commandMetadata(cmd) ?? {
24658
24866
  category: "core",
24659
24867
  discovery: "primary",
@@ -24661,29 +24869,31 @@ function buildCommand(cmd, path2) {
24661
24869
  module_owner: "unclassified",
24662
24870
  consumer: "unclassified"
24663
24871
  };
24664
- const house = houseForPath(path2);
24872
+ const house = houseForPath(flatPath);
24665
24873
  if (!house) {
24666
24874
  throw new Error(
24667
- `command-manifest: command '${path2}' has no house assignment \u2014 add it to cli/src/house-map.ts (the six-house taxonomy; every top-level command maps to exactly one house).`
24875
+ `command-manifest: command '${flatPath}' has no house assignment \u2014 add it to cli/src/house-map.ts (the six-house taxonomy; every top-level command maps to exactly one house).`
24668
24876
  );
24669
24877
  }
24670
24878
  const aliases = cmd.aliases();
24879
+ const canonical = canonicalPathFor(flatPath) ?? flatPath;
24671
24880
  const out = {
24672
24881
  name: cmd.name(),
24673
- path: path2,
24882
+ path: canonical,
24883
+ flat_path: flatPath,
24674
24884
  ...aliases.length ? { aliases: [...aliases] } : {},
24675
24885
  house,
24676
- canonical: canonicalPathFor(path2) ?? path2,
24886
+ canonical,
24677
24887
  arguments: cmd.registeredArguments.map(buildArgument),
24678
24888
  // A hand-parsed command registers no Commander options, so its declared set is merged in (#3682).
24679
24889
  options: [...cmd.options.map(buildOption), ...readDeclaredOptions(cmd)],
24680
24890
  subcommands: cmd.commands.map(
24681
- (child2) => buildCommand(child2, path2 ? `${path2} ${child2.name()}` : child2.name())
24891
+ (child2) => buildCommand(child2, flatPath ? `${flatPath} ${child2.name()}` : child2.name())
24682
24892
  ),
24683
24893
  ...metadata
24684
24894
  };
24685
24895
  if (cmd._allowUnknownOption) out.parses_own_argv = true;
24686
- if (path2 && house !== "core") out.flat_removed = true;
24896
+ if (flatPath && house !== "core") out.flat_removed = true;
24687
24897
  const description = cmd.description();
24688
24898
  if (description) out.description = description;
24689
24899
  const examples = readExamples(cmd);
@@ -24712,7 +24922,7 @@ function collectLeaves(node, acc) {
24712
24922
  function buildHouses(tree) {
24713
24923
  const collect = (node, parentHouse, root, out) => {
24714
24924
  if (node.house === root && parentHouse !== root) {
24715
- const entry = { path: node.path, canonical: node.canonical };
24925
+ const entry = { path: node.flat_path, canonical: node.canonical };
24716
24926
  if (node.description) entry.description = node.description;
24717
24927
  out.push(entry);
24718
24928
  }
@@ -24916,7 +25126,7 @@ function consolidateCommandNamespaces(program3) {
24916
25126
 
24917
25127
  // src/pi-plugin-registration.ts
24918
25128
  var import_node_fs25 = require("node:fs");
24919
- var import_node_path23 = require("node:path");
25129
+ var import_node_path24 = require("node:path");
24920
25130
  var import_proper_lockfile2 = __toESM(require_proper_lockfile(), 1);
24921
25131
 
24922
25132
  // src/plugin-cache-prune.ts
@@ -25179,17 +25389,17 @@ function newestExistingPiPlugin(home) {
25179
25389
  return void 0;
25180
25390
  }
25181
25391
  for (const version of names.filter(isVersionDirName).sort((a, b) => compareVersions(b, a))) {
25182
- const candidate = (0, import_node_path23.join)(cacheRoot, version, ".pi-plugin");
25392
+ const candidate = (0, import_node_path24.join)(cacheRoot, version, ".pi-plugin");
25183
25393
  if ((0, import_node_fs25.existsSync)(candidate)) return candidate;
25184
25394
  }
25185
25395
  return void 0;
25186
25396
  }
25187
25397
  function expectedPiPluginPath(home, env, installedVersion) {
25188
25398
  const root = env.CLAUDE_PLUGIN_ROOT?.trim();
25189
- if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0, import_node_path23.join)(root, ".pi-plugin");
25399
+ if (root && /[\\/]mutmutco[\\/]mmi[\\/]/.test(root)) return (0, import_node_path24.join)(root, ".pi-plugin");
25190
25400
  const version = installedVersion ?? runningPluginVersion(env);
25191
25401
  if (version) {
25192
- const pinned = (0, import_node_path23.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi", version, ".pi-plugin");
25402
+ const pinned = (0, import_node_path24.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi", version, ".pi-plugin");
25193
25403
  if ((0, import_node_fs25.existsSync)(pinned)) return pinned;
25194
25404
  }
25195
25405
  return newestExistingPiPlugin(home);
@@ -25197,15 +25407,15 @@ function expectedPiPluginPath(home, env, installedVersion) {
25197
25407
  function agentDirs(home, env = process.env) {
25198
25408
  const override = env.JERVCODE_CODING_AGENT_DIR?.trim() || env.PI_CODING_AGENT_DIR?.trim();
25199
25409
  if (override) return [override];
25200
- const jerv = (0, import_node_path23.join)(home, ".jerv", "agent");
25201
- const pi = (0, import_node_path23.join)(home, ".pi", "agent");
25410
+ const jerv = (0, import_node_path24.join)(home, ".jerv", "agent");
25411
+ const pi = (0, import_node_path24.join)(home, ".pi", "agent");
25202
25412
  if (!(0, import_node_fs25.existsSync)(jerv) && !(0, import_node_fs25.existsSync)(pi)) return [];
25203
25413
  const dirs = [jerv];
25204
25414
  if ((0, import_node_fs25.existsSync)(pi) && pi !== jerv) dirs.push(pi);
25205
25415
  return dirs;
25206
25416
  }
25207
25417
  function settingsPath(agentDir) {
25208
- return (0, import_node_path23.join)(agentDir, "settings.json");
25418
+ return (0, import_node_path24.join)(agentDir, "settings.json");
25209
25419
  }
25210
25420
  function readPiPluginState(home, env, installedVersion) {
25211
25421
  const dirs = agentDirs(home, env);
@@ -25239,7 +25449,7 @@ function acquirePiSettingsLock2(settingsFile) {
25239
25449
  return void 0;
25240
25450
  }
25241
25451
  function atomicWriteSettings(file, body) {
25242
- (0, import_node_fs25.mkdirSync)((0, import_node_path23.dirname)(file), { recursive: true });
25452
+ (0, import_node_fs25.mkdirSync)((0, import_node_path24.dirname)(file), { recursive: true });
25243
25453
  const tmp = `${file}.tmp-${process.pid}`;
25244
25454
  (0, import_node_fs25.writeFileSync)(tmp, body, "utf8");
25245
25455
  try {
@@ -25317,18 +25527,18 @@ function healPiPluginRegistration(home, env, installedVersion) {
25317
25527
  // src/claude-binary-doctor.ts
25318
25528
  var import_node_fs27 = require("node:fs");
25319
25529
  var import_node_os11 = require("node:os");
25320
- var import_node_path25 = require("node:path");
25530
+ var import_node_path26 = require("node:path");
25321
25531
 
25322
25532
  // src/jerv-cli-spawn.ts
25323
25533
  var import_node_fs26 = require("node:fs");
25324
25534
  var import_node_os10 = require("node:os");
25325
- var import_node_path24 = require("node:path");
25535
+ var import_node_path25 = require("node:path");
25326
25536
  var WIN_NAMES = ["jerv-cli.cmd", "jerv-cli.exe", "jerv-cli"];
25327
25537
  var POSIX_NAMES = ["jerv-cli"];
25328
- var JERV_CLI_ENTRY = (0, import_node_path24.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
25538
+ var JERV_CLI_ENTRY = (0, import_node_path25.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
25329
25539
  function pathEnvEntries(pathEnv, platform2 = process.platform) {
25330
25540
  if (platform2 !== "win32") {
25331
- return pathEnv.split(import_node_path24.delimiter).map((e) => e.trim()).filter(Boolean);
25541
+ return pathEnv.split(import_node_path25.delimiter).map((e) => e.trim()).filter(Boolean);
25332
25542
  }
25333
25543
  if (pathEnv.includes(";")) {
25334
25544
  return pathEnv.split(";").map((e) => e.trim()).filter(Boolean);
@@ -25361,10 +25571,10 @@ function jervCliCandidateDirs(env = process.env, home = (0, import_node_os10.hom
25361
25571
  push(normalizeSpawnPathEntry(entry, platform2));
25362
25572
  }
25363
25573
  if (platform2 === "win32") {
25364
- if (env.APPDATA) push((0, import_node_path24.join)(env.APPDATA, "npm"));
25365
- if (env.LOCALAPPDATA) push((0, import_node_path24.join)(env.LOCALAPPDATA, "npm"));
25574
+ if (env.APPDATA) push((0, import_node_path25.join)(env.APPDATA, "npm"));
25575
+ if (env.LOCALAPPDATA) push((0, import_node_path25.join)(env.LOCALAPPDATA, "npm"));
25366
25576
  } else {
25367
- push((0, import_node_path24.join)(home, ".local", "bin"));
25577
+ push((0, import_node_path25.join)(home, ".local", "bin"));
25368
25578
  }
25369
25579
  return out;
25370
25580
  }
@@ -25372,7 +25582,7 @@ function jervCliCandidatePaths(env = process.env, home = (0, import_node_os10.ho
25372
25582
  const names = platform2 === "win32" ? WIN_NAMES : POSIX_NAMES;
25373
25583
  const out = [];
25374
25584
  for (const dir of jervCliCandidateDirs(env, home, platform2)) {
25375
- for (const name of names) out.push((0, import_node_path24.join)(dir, name));
25585
+ for (const name of names) out.push((0, import_node_path25.join)(dir, name));
25376
25586
  }
25377
25587
  return out;
25378
25588
  }
@@ -25383,7 +25593,7 @@ function resolveJervCliPath(env = process.env, home = (0, import_node_os10.homed
25383
25593
  return void 0;
25384
25594
  }
25385
25595
  function resolveJervCliNodeEntry(shimPath, exists = import_node_fs26.existsSync) {
25386
- const entry = (0, import_node_path24.join)((0, import_node_path24.dirname)(shimPath), JERV_CLI_ENTRY);
25596
+ const entry = (0, import_node_path25.join)((0, import_node_path25.dirname)(shimPath), JERV_CLI_ENTRY);
25387
25597
  return exists(entry) ? entry : void 0;
25388
25598
  }
25389
25599
  function jervCliExecFileArgs(args, opts = {}) {
@@ -25478,10 +25688,10 @@ function globalNodeModulesRoots(host) {
25478
25688
  out.push(dir);
25479
25689
  };
25480
25690
  const prefix = env.npm_config_prefix?.trim();
25481
- if (prefix) push(platform2 === "win32" ? (0, import_node_path25.join)(prefix, "node_modules") : (0, import_node_path25.join)(prefix, "lib", "node_modules"));
25691
+ if (prefix) push(platform2 === "win32" ? (0, import_node_path26.join)(prefix, "node_modules") : (0, import_node_path26.join)(prefix, "lib", "node_modules"));
25482
25692
  for (const dir of jervCliCandidateDirs(env, host.home ?? (0, import_node_os11.homedir)(), platform2)) {
25483
- push((0, import_node_path25.join)(dir, "node_modules"));
25484
- push((0, import_node_path25.join)((0, import_node_path25.dirname)(dir), "lib", "node_modules"));
25693
+ push((0, import_node_path26.join)(dir, "node_modules"));
25694
+ push((0, import_node_path26.join)((0, import_node_path26.dirname)(dir), "lib", "node_modules"));
25485
25695
  }
25486
25696
  return out;
25487
25697
  }
@@ -25519,17 +25729,17 @@ function readClaudeBinaryState(host = {}) {
25519
25729
  const arch = host.arch ?? process.arch;
25520
25730
  const magic = EXECUTABLE_MAGIC[platform2];
25521
25731
  if (!magic) return void 0;
25522
- const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path25.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs27.existsSync)((0, import_node_path25.join)(dir, "package.json")));
25732
+ const packageRoot = globalNodeModulesRoots(host).map((root) => (0, import_node_path26.join)(root, ...PACKAGE.split("/"))).find((dir) => (0, import_node_fs27.existsSync)((0, import_node_path26.join)(dir, "package.json")));
25523
25733
  if (!packageRoot) return void 0;
25524
25734
  const keys = platformPackageKeys(platform2, arch);
25525
25735
  const fallbackPackage = `${PACKAGE}-${keys[0]}`;
25526
25736
  let manifest;
25527
25737
  try {
25528
- manifest = JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path25.join)(packageRoot, "package.json"), "utf8"));
25738
+ manifest = JSON.parse((0, import_node_fs27.readFileSync)((0, import_node_path26.join)(packageRoot, "package.json"), "utf8"));
25529
25739
  } catch (e) {
25530
25740
  return {
25531
25741
  state: "unreadable",
25532
- binPath: (0, import_node_path25.join)(packageRoot, "package.json"),
25742
+ binPath: (0, import_node_path26.join)(packageRoot, "package.json"),
25533
25743
  expectedMagic: magic.name,
25534
25744
  platformPackage: fallbackPackage,
25535
25745
  error: `package.json could not be read \u2014 ${e.message}`
@@ -25546,15 +25756,15 @@ function readClaudeBinaryState(host = {}) {
25546
25756
  error: "package.json declares no `bin` entry \u2014 the executed file cannot be resolved"
25547
25757
  };
25548
25758
  }
25549
- const binPath = (0, import_node_path25.join)(packageRoot, binRelative);
25550
- const binName = (0, import_node_path25.basename)(binRelative);
25759
+ const binPath = (0, import_node_path26.join)(packageRoot, binRelative);
25760
+ const binName = (0, import_node_path26.basename)(binRelative);
25551
25761
  const optional = manifest.optionalDependencies;
25552
25762
  const publishes = (name) => typeof optional === "object" && optional !== null && Object.prototype.hasOwnProperty.call(optional, name);
25553
25763
  const published = keys.map((key) => `${PACKAGE}-${key}`).filter(publishes);
25554
25764
  if (published.length === 0) return void 0;
25555
25765
  const binIn = (name) => [
25556
- (0, import_node_path25.join)(packageRoot, "node_modules", ...name.split("/"), binName),
25557
- (0, import_node_path25.join)((0, import_node_path25.dirname)((0, import_node_path25.dirname)(packageRoot)), ...name.split("/"), binName)
25766
+ (0, import_node_path26.join)(packageRoot, "node_modules", ...name.split("/"), binName),
25767
+ (0, import_node_path26.join)((0, import_node_path26.dirname)((0, import_node_path26.dirname)(packageRoot)), ...name.split("/"), binName)
25558
25768
  ];
25559
25769
  const found = published.map((name) => ({ name, path: binIn(name).find((file) => (0, import_node_fs27.existsSync)(file)) })).find((c) => c.path);
25560
25770
  const platformPackage = found?.name ?? published[0];
@@ -25875,6 +26085,57 @@ function renderVerifyBroker(input) {
25875
26085
  };
25876
26086
  }
25877
26087
 
26088
+ // src/tenant-artifact.ts
26089
+ var import_node_crypto4 = require("node:crypto");
26090
+ var import_node_fs28 = require("node:fs");
26091
+ var import_promises5 = require("node:fs/promises");
26092
+ var import_node_path27 = require("node:path");
26093
+ var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
26094
+ var MAX_BYTES = 5 * 1024 * 1024 * 1024;
26095
+ async function sha256File(path2) {
26096
+ const hash = (0, import_node_crypto4.createHash)("sha256");
26097
+ for await (const chunk of (0, import_node_fs28.createReadStream)(path2)) hash.update(chunk);
26098
+ return hash.digest("hex");
26099
+ }
26100
+ async function putTenantArtifact(repo, stage, inputPath, deps) {
26101
+ if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
26102
+ const path2 = (0, import_node_path27.resolve)(inputPath);
26103
+ const info = await (0, import_promises5.stat)(path2);
26104
+ if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
26105
+ if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES} bytes`);
26106
+ const sha256 = await sha256File(path2);
26107
+ const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256 }, deps);
26108
+ if (!prepared.ok) {
26109
+ const detail = prepared.body?.error ?? prepared.error ?? `HTTP ${prepared.status}`;
26110
+ throw new Error(`tenant artifact put: ${detail}`);
26111
+ }
26112
+ const body = prepared.body;
26113
+ if (!ARTIFACT_ID_RE.test(body.artifactId ?? "") || typeof body.uploadUrl !== "string" || !body.uploadUrl.startsWith("https://")) throw new Error("tenant artifact put: Hub returned an invalid upload grant");
26114
+ if (!body.headers || typeof body.headers !== "object" || Array.isArray(body.headers)) throw new Error("tenant artifact put: Hub returned invalid signed headers");
26115
+ const headers = Object.fromEntries(Object.entries(body.headers).map(([key, value]) => {
26116
+ if (typeof value !== "string" || /[\r\n]/.test(key + value)) throw new Error("tenant artifact put: Hub returned an unsafe signed header");
26117
+ return [key, value];
26118
+ }));
26119
+ headers["content-length"] = String(info.size);
26120
+ const stream = (0, import_node_fs28.createReadStream)(path2);
26121
+ let uploaded;
26122
+ try {
26123
+ uploaded = await fetch(body.uploadUrl, {
26124
+ method: "PUT",
26125
+ headers,
26126
+ body: stream,
26127
+ duplex: "half",
26128
+ signal: AbortSignal.timeout(60 * 60 * 1e3)
26129
+ });
26130
+ } catch (error) {
26131
+ stream.destroy();
26132
+ throw error;
26133
+ }
26134
+ if (!uploaded.ok) throw new Error(`tenant artifact put: object upload failed (HTTP ${uploaded.status})`);
26135
+ if (body.size !== info.size || body.sha256 !== sha256 || typeof body.expiresAt !== "string" || !Number.isFinite(Date.parse(body.expiresAt))) throw new Error("tenant artifact put: Hub returned inconsistent artifact metadata");
26136
+ return { artifactId: body.artifactId, repo, stage, size: info.size, sha256, expiresAt: body.expiresAt };
26137
+ }
26138
+
25878
26139
  // src/hotfix-coverage.ts
25879
26140
  var import_node_child_process12 = require("node:child_process");
25880
26141
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
@@ -26058,7 +26319,7 @@ function clean3(out) {
26058
26319
  return out.trim();
26059
26320
  }
26060
26321
  function sleeper(deps) {
26061
- return deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
26322
+ return deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
26062
26323
  }
26063
26324
  function normalizeHotfixVersion(input) {
26064
26325
  const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
@@ -26972,10 +27233,10 @@ async function announceRelease(deps, args) {
26972
27233
  }
26973
27234
 
26974
27235
  // src/repo-index.ts
26975
- var import_node_crypto4 = require("node:crypto");
27236
+ var import_node_crypto5 = require("node:crypto");
26976
27237
  var import_node_child_process13 = require("node:child_process");
26977
- var import_node_fs28 = require("node:fs");
26978
- var import_node_path26 = require("node:path");
27238
+ var import_node_fs29 = require("node:fs");
27239
+ var import_node_path28 = require("node:path");
26979
27240
  var REPO_INDEX_SCHEMA = 1;
26980
27241
  var HARD_DENY = [
26981
27242
  /(^|\/)\.env(\.|$)/i,
@@ -27100,11 +27361,11 @@ function loadReadmeHints(cwd, candidatePaths) {
27100
27361
  }
27101
27362
  for (const rel of readmes) {
27102
27363
  if (isHardDeniedPath(rel)) continue;
27103
- const abs = (0, import_node_path26.join)(cwd, ...rel.split("/"));
27104
- if (!(0, import_node_fs28.existsSync)(abs)) continue;
27364
+ const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
27365
+ if (!(0, import_node_fs29.existsSync)(abs)) continue;
27105
27366
  let text;
27106
27367
  try {
27107
- text = (0, import_node_fs28.readFileSync)(abs, "utf8");
27368
+ text = (0, import_node_fs29.readFileSync)(abs, "utf8");
27108
27369
  } catch {
27109
27370
  continue;
27110
27371
  }
@@ -27117,7 +27378,7 @@ function loadReadmeHints(cwd, candidatePaths) {
27117
27378
  return hints;
27118
27379
  }
27119
27380
  function toPosix(p) {
27120
- return p.split(import_node_path26.sep).join("/");
27381
+ return p.split(import_node_path28.sep).join("/");
27121
27382
  }
27122
27383
  function listCandidatePaths(cwd, exec = import_node_child_process13.execFileSync) {
27123
27384
  try {
@@ -27139,16 +27400,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
27139
27400
  for (const rel of candidates) {
27140
27401
  if (ignored.has(rel)) continue;
27141
27402
  if (isHardDeniedPath(rel)) continue;
27142
- const abs = (0, import_node_path26.join)(cwd, ...rel.split("/"));
27143
- if (!(0, import_node_fs28.existsSync)(abs)) continue;
27403
+ const abs = (0, import_node_path28.join)(cwd, ...rel.split("/"));
27404
+ if (!(0, import_node_fs29.existsSync)(abs)) continue;
27144
27405
  let text;
27145
27406
  try {
27146
- text = (0, import_node_fs28.readFileSync)(abs, "utf8");
27407
+ text = (0, import_node_fs29.readFileSync)(abs, "utf8");
27147
27408
  } catch {
27148
27409
  continue;
27149
27410
  }
27150
27411
  if (text.length > 15e5) continue;
27151
- const hash = (0, import_node_crypto4.createHash)("sha256").update(text).digest("hex").slice(0, 16);
27412
+ const hash = (0, import_node_crypto5.createHash)("sha256").update(text).digest("hex").slice(0, 16);
27152
27413
  const symbols = extractSymbols(text);
27153
27414
  const docBlurb = extractModuleBlurb(text);
27154
27415
  const top = rel.includes("/") ? rel.split("/")[0] : "";
@@ -27168,16 +27429,16 @@ function rebuildRepoIndex(cwd, repoSlug2) {
27168
27429
  entries
27169
27430
  };
27170
27431
  const store = repoIndexStorePath(cwd);
27171
- (0, import_node_fs28.mkdirSync)((0, import_node_path26.dirname)(store), { recursive: true });
27172
- (0, import_node_fs28.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
27432
+ (0, import_node_fs29.mkdirSync)((0, import_node_path28.dirname)(store), { recursive: true });
27433
+ (0, import_node_fs29.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
27173
27434
  `, "utf8");
27174
27435
  return projection;
27175
27436
  }
27176
27437
  function loadRepoIndex(cwd) {
27177
27438
  const store = repoIndexStorePath(cwd);
27178
- if (!(0, import_node_fs28.existsSync)(store)) return null;
27439
+ if (!(0, import_node_fs29.existsSync)(store)) return null;
27179
27440
  try {
27180
- const raw = JSON.parse((0, import_node_fs28.readFileSync)(store, "utf8"));
27441
+ const raw = JSON.parse((0, import_node_fs29.readFileSync)(store, "utf8"));
27181
27442
  if (raw?.schema !== REPO_INDEX_SCHEMA || !Array.isArray(raw.entries)) return null;
27182
27443
  return raw;
27183
27444
  } catch {
@@ -27249,7 +27510,7 @@ function inferRepoSlug(cwd, exec = import_node_child_process13.execFileSync) {
27249
27510
  if (m?.[1]) return m[1].toLowerCase();
27250
27511
  } catch {
27251
27512
  }
27252
- return ((0, import_node_path26.basename)(cwd) || "local").toLowerCase();
27513
+ return ((0, import_node_path28.basename)(cwd) || "local").toLowerCase();
27253
27514
  }
27254
27515
 
27255
27516
  // src/repo-index-cloud-client.ts
@@ -27389,9 +27650,9 @@ async function gcRepoIndexCloud(deps) {
27389
27650
  }
27390
27651
 
27391
27652
  // src/repo-index-sync.ts
27392
- var import_node_fs29 = require("node:fs");
27653
+ var import_node_fs30 = require("node:fs");
27393
27654
  var import_node_os12 = require("node:os");
27394
- var import_node_path27 = require("node:path");
27655
+ var import_node_path29 = require("node:path");
27395
27656
  var import_node_child_process14 = require("node:child_process");
27396
27657
  var MAX_EMBED_BACKFILL_ROUNDS = 40;
27397
27658
  function normalizeRepo(raw) {
@@ -27435,7 +27696,7 @@ async function syncEstateRepoIndex(opts) {
27435
27696
  const failed = [];
27436
27697
  const skipped = [];
27437
27698
  for (const repo of repos) {
27438
- const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path27.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-"));
27699
+ const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path29.join)((0, import_node_os12.tmpdir)(), "mmi-repo-index-"));
27439
27700
  try {
27440
27701
  shallowClone(repo, dir, opts.githubToken);
27441
27702
  const built = rebuildRepoIndex(dir, repo);
@@ -27485,7 +27746,7 @@ async function syncEstateRepoIndex(opts) {
27485
27746
  failed.push({ repo, error: e.message });
27486
27747
  } finally {
27487
27748
  try {
27488
- (0, import_node_fs29.rmSync)(dir, { recursive: true, force: true });
27749
+ (0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
27489
27750
  } catch {
27490
27751
  }
27491
27752
  }
@@ -27494,7 +27755,7 @@ async function syncEstateRepoIndex(opts) {
27494
27755
  }
27495
27756
 
27496
27757
  // src/repo-index-health.ts
27497
- var import_node_fs30 = require("node:fs");
27758
+ var import_node_fs31 = require("node:fs");
27498
27759
 
27499
27760
  // testdata/repo-index-golden-queries.json
27500
27761
  var repo_index_golden_queries_default = {
@@ -27536,7 +27797,7 @@ function assertGoldenSuite(raw, source) {
27536
27797
  function loadGoldenSuite(path2) {
27537
27798
  let text;
27538
27799
  try {
27539
- text = (0, import_node_fs30.readFileSync)(path2, "utf8");
27800
+ text = (0, import_node_fs31.readFileSync)(path2, "utf8");
27540
27801
  } catch (e) {
27541
27802
  throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
27542
27803
  }
@@ -27690,8 +27951,8 @@ async function runRepoIndexHealth(opts) {
27690
27951
 
27691
27952
  // src/spawn-policy-core.ts
27692
27953
  var import_node_child_process15 = require("node:child_process");
27693
- var import_node_fs31 = require("node:fs");
27694
- var import_node_path28 = require("node:path");
27954
+ var import_node_fs32 = require("node:fs");
27955
+ var import_node_path30 = require("node:path");
27695
27956
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
27696
27957
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
27697
27958
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -27777,7 +28038,7 @@ function runSpawnPolicy(root) {
27777
28038
  for (const file of files) {
27778
28039
  let raw;
27779
28040
  try {
27780
- raw = (0, import_node_fs31.readFileSync)((0, import_node_path28.join)(root, file), "utf8");
28041
+ raw = (0, import_node_fs32.readFileSync)((0, import_node_path30.join)(root, file), "utf8");
27781
28042
  } catch {
27782
28043
  continue;
27783
28044
  }
@@ -27795,8 +28056,8 @@ function runSpawnPolicy(root) {
27795
28056
 
27796
28057
  // src/test-policy-core.ts
27797
28058
  var import_node_child_process16 = require("node:child_process");
27798
- var import_node_fs32 = require("node:fs");
27799
- var import_node_path29 = require("node:path");
28059
+ var import_node_fs33 = require("node:fs");
28060
+ var import_node_path31 = require("node:path");
27800
28061
  var POLICY_FILE = "test-policy.json";
27801
28062
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
27802
28063
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -27849,7 +28110,7 @@ function isTestPath(path2) {
27849
28110
  return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
27850
28111
  }
27851
28112
  function loadPolicy(root, readFile9 = readFileOrNull2) {
27852
- const raw = readFile9((0, import_node_path29.join)(root, POLICY_FILE));
28113
+ const raw = readFile9((0, import_node_path31.join)(root, POLICY_FILE));
27853
28114
  if (raw == null) return { mandatory: [], declared: false };
27854
28115
  try {
27855
28116
  return { ...JSON.parse(raw), declared: true };
@@ -27859,7 +28120,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
27859
28120
  }
27860
28121
  function readFileOrNull2(path2) {
27861
28122
  try {
27862
- return (0, import_node_fs32.readFileSync)(path2, "utf8");
28123
+ return (0, import_node_fs33.readFileSync)(path2, "utf8");
27863
28124
  } catch {
27864
28125
  return null;
27865
28126
  }
@@ -27886,12 +28147,12 @@ function classify(changed, policy, present = () => false) {
27886
28147
  const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
27887
28148
  return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
27888
28149
  }
27889
- function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs32.existsSync)(path2)) {
27890
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path29.join)(root, p)));
28150
+ function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
28151
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path31.join)(root, p)));
27891
28152
  }
27892
- function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs32.existsSync)(path2)) {
28153
+ function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs33.existsSync)(path2)) {
27893
28154
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
27894
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path29.join)(root, p)));
28155
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path31.join)(root, p)));
27895
28156
  }
27896
28157
  function evaluate(changed, policy, present = () => false) {
27897
28158
  const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -28073,13 +28334,13 @@ function changedFilesSince(base, cwd) {
28073
28334
  }
28074
28335
  function runTestPolicy(root, deps = {}) {
28075
28336
  const policy = deps.policy ?? loadPolicy(root);
28076
- const exists = deps.exists ?? ((path2) => (0, import_node_fs32.existsSync)(path2));
28337
+ const exists = deps.exists ?? ((path2) => (0, import_node_fs33.existsSync)(path2));
28077
28338
  const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
28078
28339
  const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
28079
28340
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
28080
28341
  const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
28081
28342
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
28082
- const present = (path2) => exists((0, import_node_path29.join)(root, path2));
28343
+ const present = (path2) => exists((0, import_node_path31.join)(root, path2));
28083
28344
  const removedByThisDiff = removedPaths(changed);
28084
28345
  const staleFindings = [];
28085
28346
  const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
@@ -28117,8 +28378,8 @@ function runTestPolicy(root, deps = {}) {
28117
28378
  }
28118
28379
 
28119
28380
  // src/project-info-sync.ts
28120
- var import_node_fs33 = require("node:fs");
28121
- var import_node_path30 = require("node:path");
28381
+ var import_node_fs34 = require("node:fs");
28382
+ var import_node_path32 = require("node:path");
28122
28383
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
28123
28384
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
28124
28385
  projectV2 { id }
@@ -28163,14 +28424,14 @@ function sharedName(entries, fallback) {
28163
28424
  }
28164
28425
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
28165
28426
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
28166
- const readmePath = (0, import_node_path30.join)(repoRoot2, "README.md");
28167
- if (!(0, import_node_fs33.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
28427
+ const readmePath = (0, import_node_path32.join)(repoRoot2, "README.md");
28428
+ if (!(0, import_node_fs34.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
28168
28429
  const entries = entriesFor(project2, projects);
28169
28430
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
28170
28431
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
28171
28432
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
28172
28433
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
28173
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs33.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
28434
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs34.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
28174
28435
  const lines = [
28175
28436
  `# ${projectName}`,
28176
28437
  "",
@@ -28189,8 +28450,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
28189
28450
  const targetBase = `https://github.com/${targetRepo2}`;
28190
28451
  const targetBranch = branchFor(targetRepo2, projects);
28191
28452
  const orgDocs = [
28192
- (0, import_node_fs33.existsSync)((0, import_node_path30.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
28193
- (0, import_node_fs33.existsSync)((0, import_node_path30.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
28453
+ (0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
28454
+ (0, import_node_fs34.existsSync)((0, import_node_path32.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
28194
28455
  ].filter(Boolean);
28195
28456
  if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
28196
28457
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
@@ -28212,7 +28473,7 @@ async function syncProjectInfo(plan, client, apply) {
28212
28473
  }
28213
28474
 
28214
28475
  // src/project-set.ts
28215
- var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "ciExemptReason", "gate", "seedCanary"];
28476
+ var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "requiredBuildSecrets", "tenantTasks", "secrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "ciExemptReason", "gate", "seedCanary"];
28216
28477
  var UNSET_KEY_SET = new Set(UNSET_KEYS);
28217
28478
  var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
28218
28479
  var SECRET_CONSUMERS = ["runtime", "build", "lambda", "actions", "agent", "box"];
@@ -28537,6 +28798,43 @@ function parseGateVar(raw) {
28537
28798
  }
28538
28799
  return out;
28539
28800
  }
28801
+ function parseTenantTasksVar(raw) {
28802
+ let parsed;
28803
+ try {
28804
+ parsed = JSON.parse(raw);
28805
+ } catch {
28806
+ throw new Error("org project set: tenantTasks must be valid JSON");
28807
+ }
28808
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("org project set: tenantTasks must be a JSON object");
28809
+ const entries = Object.entries(parsed);
28810
+ if (entries.length > 20) throw new Error("org project set: tenantTasks supports at most 20 declarations");
28811
+ const out = {};
28812
+ const nameRe = /^[a-z0-9][a-z0-9-]{0,62}$/;
28813
+ const serviceRe = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$/;
28814
+ const validStages = /* @__PURE__ */ new Set(["dev", "rc", "main"]);
28815
+ for (const [name, rawTask] of entries) {
28816
+ if (!nameRe.test(name)) throw new Error(`org project set: tenantTasks name ${JSON.stringify(name)} must be lowercase kebab-case`);
28817
+ if (!rawTask || typeof rawTask !== "object" || Array.isArray(rawTask)) throw new Error(`org project set: tenantTasks.${name} must be an object`);
28818
+ const task = rawTask;
28819
+ if (Object.keys(task).some((key) => !["service", "command", "stages", "timeoutSeconds", "artifact"].includes(key))) throw new Error(`org project set: tenantTasks.${name} has an unknown field`);
28820
+ if (typeof task.service !== "string" || !serviceRe.test(task.service)) throw new Error(`org project set: tenantTasks.${name}.service has an unsafe shape`);
28821
+ if (!Array.isArray(task.command) || task.command.length < 1 || task.command.length > 32 || task.command.some((arg) => typeof arg !== "string" || arg.length < 1 || arg.length > 256 || /[\u0000\r\n]/.test(arg))) throw new Error(`org project set: tenantTasks.${name}.command must be 1-32 bounded argv strings`);
28822
+ if (!Array.isArray(task.stages) || task.stages.length < 1 || task.stages.length > 3 || task.stages.some((stage) => typeof stage !== "string" || !validStages.has(stage)) || new Set(task.stages).size !== task.stages.length) throw new Error(`org project set: tenantTasks.${name}.stages must be unique dev/rc/main values`);
28823
+ if (task.timeoutSeconds !== void 0 && (!Number.isInteger(task.timeoutSeconds) || task.timeoutSeconds < 1 || task.timeoutSeconds > 3600)) throw new Error(`org project set: tenantTasks.${name}.timeoutSeconds must be an integer 1..3600`);
28824
+ const artifact = task.artifact ?? "none";
28825
+ if (artifact !== "none" && artifact !== "required") throw new Error(`org project set: tenantTasks.${name}.artifact must be none or required`);
28826
+ const carriesPlaceholder = task.command.some((arg) => arg.includes("{artifact}"));
28827
+ if (artifact === "required" !== carriesPlaceholder) throw new Error(`org project set: tenantTasks.${name} must use {artifact} exactly when artifact is required`);
28828
+ out[name] = {
28829
+ service: task.service,
28830
+ command: task.command,
28831
+ stages: task.stages,
28832
+ ...task.timeoutSeconds !== void 0 ? { timeoutSeconds: task.timeoutSeconds } : {},
28833
+ ...artifact !== "none" ? { artifact } : {}
28834
+ };
28835
+ }
28836
+ return out;
28837
+ }
28540
28838
  var SETTABLE_VAR_KEYS = [
28541
28839
  "name",
28542
28840
  "division",
@@ -28556,6 +28854,7 @@ var SETTABLE_VAR_KEYS = [
28556
28854
  "requiredGcpApis",
28557
28855
  "requiredRuntimeSecrets",
28558
28856
  "requiredBuildSecrets",
28857
+ "tenantTasks",
28559
28858
  "edgeDomains",
28560
28859
  "statusFieldId",
28561
28860
  "statusOptions",
@@ -28584,6 +28883,7 @@ var SETTABLE_VAR_HINTS = {
28584
28883
  requiredGcpApis: "comma-string",
28585
28884
  requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
28586
28885
  requiredBuildSecrets: 'JSON flat array, e.g. ["NODE_AUTH_TOKEN=@github-packages-token"]',
28886
+ tenantTasks: 'JSON map {name:{service,command[],stages[],timeoutSeconds?,artifact?:"required"}}; use {artifact} in argv when required',
28587
28887
  secrets: "JSON catalog map keyed by KEY {key,purpose,group,owner,stages[],consumers[]} \u2014 merged per entry; clear with --unset secrets; prefer --secrets-file",
28588
28888
  edgeDomains: "JSON {dev,rc,main} domain map",
28589
28889
  statusOptions: "JSON name\u2192id map",
@@ -28656,6 +28956,8 @@ function buildProjectSetPatch(input) {
28656
28956
  patch[key] = parseRuntimeSecretsVar(raw);
28657
28957
  } else if (key === "requiredBuildSecrets") {
28658
28958
  patch[key] = parseBuildSecretsVar(raw);
28959
+ } else if (key === "tenantTasks") {
28960
+ patch[key] = parseTenantTasksVar(raw);
28659
28961
  } else if (key === "secrets") {
28660
28962
  patch[key] = parseSecretsCatalogVar(raw);
28661
28963
  } else if (key === "edgeDomains") {
@@ -28975,8 +29277,8 @@ function writeError(res) {
28975
29277
  }
28976
29278
 
28977
29279
  // src/secrets-commands.ts
28978
- var import_node_fs34 = require("node:fs");
28979
- var import_node_path31 = require("node:path");
29280
+ var import_node_fs35 = require("node:fs");
29281
+ var import_node_path33 = require("node:path");
28980
29282
  var import_node_os13 = require("node:os");
28981
29283
 
28982
29284
  // src/secrets-diff.ts
@@ -29079,18 +29381,18 @@ function collectMap(value, previous = []) {
29079
29381
  return [...previous, value];
29080
29382
  }
29081
29383
  async function decryptRailsCredentials(input) {
29082
- const appDir = (0, import_node_path31.resolve)(input.appDir ?? process.cwd());
29384
+ const appDir = (0, import_node_path33.resolve)(input.appDir ?? process.cwd());
29083
29385
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
29084
29386
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
29085
- const credentialsPath = (0, import_node_path31.resolve)(appDir, credentialsFile);
29086
- const masterKeyPath = (0, import_node_path31.resolve)(appDir, masterKeyFile);
29387
+ const credentialsPath = (0, import_node_path33.resolve)(appDir, credentialsFile);
29388
+ const masterKeyPath = (0, import_node_path33.resolve)(appDir, masterKeyFile);
29087
29389
  const env = {
29088
29390
  ...process.env,
29089
29391
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
29090
29392
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
29091
29393
  };
29092
- if ((0, import_node_fs34.existsSync)(masterKeyPath)) {
29093
- env.RAILS_MASTER_KEY = (0, import_node_fs34.readFileSync)(masterKeyPath, "utf8").trim();
29394
+ if ((0, import_node_fs35.existsSync)(masterKeyPath)) {
29395
+ env.RAILS_MASTER_KEY = (0, import_node_fs35.readFileSync)(masterKeyPath, "utf8").trim();
29094
29396
  }
29095
29397
  const script = [
29096
29398
  'require "json"',
@@ -29100,9 +29402,9 @@ async function decryptRailsCredentials(input) {
29100
29402
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
29101
29403
  "puts JSON.generate(config.config)"
29102
29404
  ].join("\n");
29103
- const scriptDir = (0, import_node_fs34.mkdtempSync)((0, import_node_path31.join)((0, import_node_os13.tmpdir)(), "mmi-rails-decrypt-"));
29104
- const scriptPath = (0, import_node_path31.join)(scriptDir, "decrypt.rb");
29105
- (0, import_node_fs34.writeFileSync)(scriptPath, script, "utf8");
29405
+ const scriptDir = (0, import_node_fs35.mkdtempSync)((0, import_node_path33.join)((0, import_node_os13.tmpdir)(), "mmi-rails-decrypt-"));
29406
+ const scriptPath = (0, import_node_path33.join)(scriptDir, "decrypt.rb");
29407
+ (0, import_node_fs35.writeFileSync)(scriptPath, script, "utf8");
29106
29408
  try {
29107
29409
  const args = ["exec", "ruby", scriptPath];
29108
29410
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -29114,7 +29416,7 @@ async function decryptRailsCredentials(input) {
29114
29416
  });
29115
29417
  return JSON.parse(stdout);
29116
29418
  } finally {
29117
- (0, import_node_fs34.rmSync)(scriptDir, { recursive: true, force: true });
29419
+ (0, import_node_fs35.rmSync)(scriptDir, { recursive: true, force: true });
29118
29420
  }
29119
29421
  }
29120
29422
  async function readSecretStdin() {
@@ -29204,7 +29506,7 @@ function registerSecretsCommands(program3) {
29204
29506
  let body;
29205
29507
  if (o.file) {
29206
29508
  try {
29207
- body = (0, import_node_fs34.readFileSync)((0, import_node_path31.resolve)(o.file), "utf8");
29509
+ body = (0, import_node_fs35.readFileSync)((0, import_node_path33.resolve)(o.file), "utf8");
29208
29510
  } catch (e) {
29209
29511
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
29210
29512
  }
@@ -29309,7 +29611,7 @@ function registerSecretsCommands(program3) {
29309
29611
  {
29310
29612
  ...d,
29311
29613
  decryptRailsCredentials,
29312
- removeFile: (path2) => (0, import_node_fs34.unlinkSync)((0, import_node_path31.resolve)(o.appDir ?? process.cwd(), path2))
29614
+ removeFile: (path2) => (0, import_node_fs35.unlinkSync)((0, import_node_path33.resolve)(o.appDir ?? process.cwd(), path2))
29313
29615
  },
29314
29616
  {
29315
29617
  repo: o.repo,
@@ -29354,7 +29656,7 @@ function registerSecretsCommands(program3) {
29354
29656
  }
29355
29657
 
29356
29658
  // src/app-actor.ts
29357
- var import_node_crypto5 = require("node:crypto");
29659
+ var import_node_crypto6 = require("node:crypto");
29358
29660
  var APP_ACTOR_ENV = "MMI_ACTOR";
29359
29661
  var APP_VAULT_REPO = "mutmutco/MMI-Hub";
29360
29662
  var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
@@ -29398,7 +29700,7 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
29398
29700
  exp: now + APP_JWT_TTL_S,
29399
29701
  iss: appId
29400
29702
  }));
29401
- const signer = (0, import_node_crypto5.createSign)("RSA-SHA256");
29703
+ const signer = (0, import_node_crypto6.createSign)("RSA-SHA256");
29402
29704
  signer.update(`${header}.${payload}`);
29403
29705
  return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
29404
29706
  }
@@ -29514,7 +29816,7 @@ function emitCliCallTelemetry(command) {
29514
29816
  }
29515
29817
 
29516
29818
  // src/box-commands.ts
29517
- var import_node_fs35 = require("node:fs");
29819
+ var import_node_fs36 = require("node:fs");
29518
29820
 
29519
29821
  // src/box.ts
29520
29822
  var BOX_KEYS = {
@@ -29593,7 +29895,7 @@ function formatBoxTable(boxes) {
29593
29895
  const addrW = w((b) => b.address || "(no public ipv4)", "ADDRESS");
29594
29896
  const projW = w((b) => b.project, "PROJECT");
29595
29897
  const statW = w((b) => b.status, "STATUS");
29596
- const row = (name, addr, proj, stat3, key) => `${name.padEnd(nameW)} ${addr.padEnd(addrW)} ${proj.padEnd(projW)} ${stat3.padEnd(statW)} ${key}`;
29898
+ const row = (name, addr, proj, stat4, key) => `${name.padEnd(nameW)} ${addr.padEnd(addrW)} ${proj.padEnd(projW)} ${stat4.padEnd(statW)} ${key}`;
29597
29899
  const lines = [row("BOX", "ADDRESS", "PROJECT", "STATUS", "SSH KEY (vault path)")];
29598
29900
  for (const b of boxes) {
29599
29901
  lines.push(row(b.name, b.address || "(no public ipv4)", b.project, b.status, b.sshKeyPath));
@@ -29717,7 +30019,7 @@ function registerBoxCommands(program3) {
29717
30019
  }
29718
30020
  if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
29719
30021
  else if (o.ssh && o.script) {
29720
- (0, import_node_fs35.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
30022
+ (0, import_node_fs36.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
29721
30023
  console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
29722
30024
  } else if (o.ssh) console.log(`${formatSshRecipe(found)}
29723
30025
  ${SSH_RECIPE_AGENT_NOTE}`);
@@ -29731,7 +30033,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
29731
30033
  }
29732
30034
 
29733
30035
  // src/schedules-commands.ts
29734
- var import_promises5 = require("node:fs/promises");
30036
+ var import_promises6 = require("node:fs/promises");
29735
30037
  var import_node_child_process17 = require("node:child_process");
29736
30038
  var import_node_util7 = require("node:util");
29737
30039
  var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process17.execFile);
@@ -29763,8 +30065,8 @@ async function repoWorkflowEntries(client, repo) {
29763
30065
  for (const wf of workflowsList) {
29764
30066
  if (typeof wf?.path !== "string" || !wf.path) continue;
29765
30067
  if (!wf.path.startsWith(".github/workflows/")) continue;
29766
- const basename6 = wf.path.split("/").pop() ?? wf.path;
29767
- const name = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
30068
+ const basename8 = wf.path.split("/").pop() ?? wf.path;
30069
+ const name = `${repo}/${basename8.replace(/\.ya?ml$/, "")}`;
29768
30070
  if (wf.state !== "active") {
29769
30071
  disabled.push(name);
29770
30072
  continue;
@@ -29846,7 +30148,7 @@ async function awsJson(args) {
29846
30148
  try {
29847
30149
  return await run();
29848
30150
  } catch {
29849
- await new Promise((resolve5) => setTimeout(resolve5, AWS_RETRY_DELAY_MS));
30151
+ await new Promise((resolve6) => setTimeout(resolve6, AWS_RETRY_DELAY_MS));
29850
30152
  return run();
29851
30153
  }
29852
30154
  }
@@ -29947,9 +30249,9 @@ function registerSchedulesCommands(program3) {
29947
30249
  await failGraceful("org schedules --doc: refusing to regenerate the doc from an incomplete read (see warnings above).");
29948
30250
  return;
29949
30251
  }
29950
- const docText = await (0, import_promises5.readFile)(o.doc, "utf8");
30252
+ const docText = await (0, import_promises6.readFile)(o.doc, "utf8");
29951
30253
  const spliced = spliceDoc(docText, renderDocSection(entries, now.toISOString()));
29952
- await (0, import_promises5.writeFile)(o.doc, spliced, "utf8");
30254
+ await (0, import_promises6.writeFile)(o.doc, spliced, "utf8");
29953
30255
  reportParked(parkedLines);
29954
30256
  reportDrift(drift);
29955
30257
  console.log(`org schedules: wrote ${entries.length} entries into ${o.doc}`);
@@ -30035,8 +30337,8 @@ function registerSchedulesCommands(program3) {
30035
30337
  }
30036
30338
 
30037
30339
  // src/schedules-lift-command.ts
30038
- var import_promises6 = require("node:fs/promises");
30039
- var import_node_path32 = require("node:path");
30340
+ var import_promises7 = require("node:fs/promises");
30341
+ var import_node_path34 = require("node:path");
30040
30342
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
30041
30343
  var SCHEDULE_REPO_RE = /^[A-Za-z0-9_.-]+$/;
30042
30344
  var SchedulesLiftUsageError = class extends Error {
@@ -30056,14 +30358,14 @@ var RegistryUnreachableError = class extends Error {
30056
30358
  async function readWorkflowFiles(dir) {
30057
30359
  let names;
30058
30360
  try {
30059
- names = await (0, import_promises6.readdir)(dir);
30361
+ names = await (0, import_promises7.readdir)(dir);
30060
30362
  } catch {
30061
30363
  return [];
30062
30364
  }
30063
30365
  const files = [];
30064
30366
  for (const name of names.sort()) {
30065
30367
  if (!/\.ya?ml$/.test(name)) continue;
30066
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path32.join)(dir, name), "utf8") });
30368
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises7.readFile)((0, import_node_path34.join)(dir, name), "utf8") });
30067
30369
  }
30068
30370
  return files;
30069
30371
  }
@@ -30211,12 +30513,12 @@ function registerEdgeCommands(program3) {
30211
30513
  }
30212
30514
 
30213
30515
  // src/bootstrap-commands.ts
30214
- var import_node_fs36 = require("node:fs");
30516
+ var import_node_fs37 = require("node:fs");
30215
30517
  var import_node_os14 = require("node:os");
30216
- var import_node_path33 = require("node:path");
30518
+ var import_node_path35 = require("node:path");
30217
30519
 
30218
30520
  // src/bootstrap-drift.ts
30219
- var import_node_crypto6 = require("node:crypto");
30521
+ var import_node_crypto7 = require("node:crypto");
30220
30522
  function byteComparableSeeds(manifest, cls) {
30221
30523
  return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
30222
30524
  }
@@ -30236,7 +30538,7 @@ function compareSeedBytes(hubContent, repoContent) {
30236
30538
  return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
30237
30539
  }
30238
30540
  function seedContentHash(content) {
30239
- return (0, import_node_crypto6.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
30541
+ return (0, import_node_crypto7.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
30240
30542
  }
30241
30543
  function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
30242
30544
  const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
@@ -31093,13 +31395,13 @@ function registerBootstrapCommands(program3) {
31093
31395
  client: defaultGitHubClient(),
31094
31396
  projectMeta: meta,
31095
31397
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
31096
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs36.existsSync)(path2) ? (0, import_node_fs36.readFileSync)(path2, "utf8") : null,
31398
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs37.existsSync)(path2) ? (0, import_node_fs37.readFileSync)(path2, "utf8") : null,
31097
31399
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
31098
31400
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
31099
31401
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
31100
31402
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
31101
31403
  // sanction, which is the pre-#3664 behaviour.
31102
- sanctionedAdmins: (0, import_node_fs36.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
31404
+ sanctionedAdmins: (0, import_node_fs37.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs37.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
31103
31405
  requiredGcpApis: (() => {
31104
31406
  const v = meta?.requiredGcpApis;
31105
31407
  if (Array.isArray(v)) return v;
@@ -31152,14 +31454,14 @@ function registerBootstrapCommands(program3) {
31152
31454
  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 () => {
31153
31455
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
31154
31456
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
31155
- if (!(0, import_node_fs36.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`);
31457
+ if (!(0, import_node_fs37.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`);
31156
31458
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
31157
31459
  if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
31158
- const manifest = loadBootstrapSeeds((0, import_node_fs36.readFileSync)(manifestPath, "utf8"));
31460
+ const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
31159
31461
  const hubContents = /* @__PURE__ */ new Map();
31160
31462
  for (const s of manifest.seeds) {
31161
31463
  if (s.ownership !== "org" || s.source !== "self") continue;
31162
- hubContents.set(s.target, (0, import_node_fs36.existsSync)(s.target) ? (0, import_node_fs36.readFileSync)(s.target, "utf8") : null);
31464
+ hubContents.set(s.target, (0, import_node_fs37.existsSync)(s.target) ? (0, import_node_fs37.readFileSync)(s.target, "utf8") : null);
31163
31465
  }
31164
31466
  let targets;
31165
31467
  let classOf = (_repo) => "deployable";
@@ -31238,10 +31540,10 @@ function registerBootstrapCommands(program3) {
31238
31540
  return fail(`bootstrap apply: ${e.message}`);
31239
31541
  }
31240
31542
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
31241
- if (!(0, import_node_fs36.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`);
31543
+ if (!(0, import_node_fs37.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`);
31242
31544
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
31243
31545
  if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
31244
- const manifest = loadBootstrapSeeds((0, import_node_fs36.readFileSync)(manifestPath, "utf8"));
31546
+ const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
31245
31547
  const baseBranch = o.class === "content" ? "main" : "development";
31246
31548
  const slug = parsedRepo.slug;
31247
31549
  const onlyTarget = o.only.trim();
@@ -31252,16 +31554,16 @@ function registerBootstrapCommands(program3) {
31252
31554
  ${known}`);
31253
31555
  }
31254
31556
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
31255
- const readFile9 = (p) => (0, import_node_fs36.existsSync)(p) ? (0, import_node_fs36.readFileSync)(p, "utf8") : null;
31557
+ const readFile9 = (p) => (0, import_node_fs37.existsSync)(p) ? (0, import_node_fs37.readFileSync)(p, "utf8") : null;
31256
31558
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
31257
31559
  const putSeed = async (target, content, ref, sha) => {
31258
- const tmp = (0, import_node_path33.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
31259
- (0, import_node_fs36.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
31560
+ const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
31561
+ (0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
31260
31562
  try {
31261
31563
  await gh(contentPutInputArgs(repo, target, tmp));
31262
31564
  } finally {
31263
31565
  try {
31264
- (0, import_node_fs36.unlinkSync)(tmp);
31566
+ (0, import_node_fs37.unlinkSync)(tmp);
31265
31567
  } catch {
31266
31568
  }
31267
31569
  }
@@ -31526,10 +31828,10 @@ LIVE apply to ${repo}:
31526
31828
  bootstrap.command("propagate").description("#4238: re-entrant canary\u2192wave tick \u2014 plan (or, with --execute, open) per-repo PRs fanning an org-owned seed out to the fleet").option("--target <path>", "the manifest target to propagate (an ownership:org + source:self seed, e.g. .github/workflows/agent-pr.yml)").option("--execute", "LIVE tick via gh (master-gated) \u2014 opens/reuses per-repo seed-propagate PRs; dry-run prints the plan only").option("--json", "machine-readable output").action(async () => {
31527
31829
  const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
31528
31830
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
31529
- if (!(0, import_node_fs36.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
31831
+ if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
31530
31832
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
31531
31833
  if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
31532
- const manifest = loadBootstrapSeeds((0, import_node_fs36.readFileSync)(manifestPath, "utf8"));
31834
+ const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
31533
31835
  const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
31534
31836
  if (!o.target) {
31535
31837
  return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
@@ -31538,8 +31840,8 @@ LIVE apply to ${repo}:
31538
31840
  const seed = propagatable.find((s) => s.target === o.target);
31539
31841
  if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no ownership:org + source:self seed in ${manifestPath}. Propagatable targets:
31540
31842
  ${propagatable.map((s) => s.target).join("\n ")}`);
31541
- if (!(0, import_node_fs36.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
31542
- const hubContent = (0, import_node_fs36.readFileSync)(seed.target, "utf8");
31843
+ if (!(0, import_node_fs37.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
31844
+ const hubContent = (0, import_node_fs37.readFileSync)(seed.target, "utf8");
31543
31845
  const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
31544
31846
  const cfg = await loadConfig();
31545
31847
  const projects = await fetchProjectsList(registryClientDeps(cfg));
@@ -31548,9 +31850,9 @@ LIVE apply to ${repo}:
31548
31850
  }
31549
31851
  const rosterRepos2 = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
31550
31852
  let independentCount = rosterRepos2.length;
31551
- if ((0, import_node_fs36.existsSync)("projects.json")) {
31853
+ if ((0, import_node_fs37.existsSync)("projects.json")) {
31552
31854
  try {
31553
- const local = JSON.parse((0, import_node_fs36.readFileSync)("projects.json", "utf8"));
31855
+ const local = JSON.parse((0, import_node_fs37.readFileSync)("projects.json", "utf8"));
31554
31856
  const localRepos = /* @__PURE__ */ new Set();
31555
31857
  for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
31556
31858
  const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
@@ -31666,13 +31968,13 @@ LIVE apply to ${repo}:
31666
31968
  } catch {
31667
31969
  existingSha = void 0;
31668
31970
  }
31669
- const tmp = (0, import_node_path33.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
31670
- (0, import_node_fs36.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
31971
+ const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
31972
+ (0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, hubContent, branch, existingSha)), "utf8");
31671
31973
  try {
31672
31974
  await gh(contentPutInputArgs(rec.repo, seed.target, tmp));
31673
31975
  } finally {
31674
31976
  try {
31675
- (0, import_node_fs36.unlinkSync)(tmp);
31977
+ (0, import_node_fs37.unlinkSync)(tmp);
31676
31978
  } catch {
31677
31979
  }
31678
31980
  }
@@ -31730,10 +32032,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
31730
32032
  return fail(`bootstrap rollback: ${e.message}`);
31731
32033
  }
31732
32034
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
31733
- if (!(0, import_node_fs36.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
32035
+ if (!(0, import_node_fs37.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
31734
32036
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
31735
32037
  if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
31736
- const manifest = loadBootstrapSeeds((0, import_node_fs36.readFileSync)(manifestPath, "utf8"));
32038
+ const manifest = loadBootstrapSeeds((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
31737
32039
  const propagatable = manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self");
31738
32040
  if (!o.target) {
31739
32041
  return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
@@ -31750,10 +32052,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
31750
32052
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
31751
32053
  let candidates;
31752
32054
  if (o.record) {
31753
- if (!(0, import_node_fs36.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
32055
+ if (!(0, import_node_fs37.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
31754
32056
  let parsed;
31755
32057
  try {
31756
- parsed = JSON.parse((0, import_node_fs36.readFileSync)(o.record, "utf8"));
32058
+ parsed = JSON.parse((0, import_node_fs37.readFileSync)(o.record, "utf8"));
31757
32059
  } catch (e) {
31758
32060
  return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
31759
32061
  }
@@ -31822,13 +32124,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
31822
32124
  } catch {
31823
32125
  existingSha = void 0;
31824
32126
  }
31825
- const tmp = (0, import_node_path33.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
31826
- (0, import_node_fs36.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
32127
+ const tmp = (0, import_node_path35.join)((0, import_node_os14.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
32128
+ (0, import_node_fs37.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
31827
32129
  try {
31828
32130
  await gh(contentPutInputArgs(repo, seed.target, tmp));
31829
32131
  } finally {
31830
32132
  try {
31831
- (0, import_node_fs36.unlinkSync)(tmp);
32133
+ (0, import_node_fs37.unlinkSync)(tmp);
31832
32134
  } catch {
31833
32135
  }
31834
32136
  }
@@ -31850,12 +32152,12 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
31850
32152
  }
31851
32153
 
31852
32154
  // src/stage-commands.ts
31853
- var import_node_fs38 = require("node:fs");
31854
- var import_node_path35 = require("node:path");
32155
+ var import_node_fs39 = require("node:fs");
32156
+ var import_node_path37 = require("node:path");
31855
32157
 
31856
32158
  // src/port-registry.ts
31857
- var import_node_fs37 = require("node:fs");
31858
- var import_node_path34 = require("node:path");
32159
+ var import_node_fs38 = require("node:fs");
32160
+ var import_node_path36 = require("node:path");
31859
32161
 
31860
32162
  // ../infra/port-geometry.mjs
31861
32163
  var PORT_BLOCK = 100;
@@ -31869,8 +32171,8 @@ function nextPortBlock(registry2) {
31869
32171
  return [base, base + PORT_SPAN];
31870
32172
  }
31871
32173
  function loadPortRegistry(path2) {
31872
- if (!(0, import_node_fs37.existsSync)(path2)) return {};
31873
- const raw = JSON.parse((0, import_node_fs37.readFileSync)(path2, "utf8"));
32174
+ if (!(0, import_node_fs38.existsSync)(path2)) return {};
32175
+ const raw = JSON.parse((0, import_node_fs38.readFileSync)(path2, "utf8"));
31874
32176
  const out = {};
31875
32177
  for (const [key, value] of Object.entries(raw)) {
31876
32178
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -31884,9 +32186,9 @@ function ensurePortRange(repo, path2) {
31884
32186
  const existing = registry2[repo];
31885
32187
  if (existing) return existing;
31886
32188
  const range = nextPortBlock(registry2);
31887
- const raw = (0, import_node_fs37.existsSync)(path2) ? JSON.parse((0, import_node_fs37.readFileSync)(path2, "utf8")) : {};
32189
+ const raw = (0, import_node_fs38.existsSync)(path2) ? JSON.parse((0, import_node_fs38.readFileSync)(path2, "utf8")) : {};
31888
32190
  raw[repo] = range;
31889
- (0, import_node_fs37.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
32191
+ (0, import_node_fs38.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
31890
32192
  return range;
31891
32193
  }
31892
32194
  function portCursorSeed(registry2) {
@@ -31908,22 +32210,22 @@ function existingPortRange(repo, registry2) {
31908
32210
  return registry2[repo] ?? null;
31909
32211
  }
31910
32212
  function portRangeInfraAt(root, source) {
31911
- const registryPath = (0, import_node_path34.join)(root, "infra", "port-ranges.json");
31912
- const ddbScriptPath = (0, import_node_path34.join)(root, "infra", "port-ddb.mjs");
31913
- if (!(0, import_node_fs37.existsSync)(registryPath) || !(0, import_node_fs37.existsSync)(ddbScriptPath)) return null;
32213
+ const registryPath = (0, import_node_path36.join)(root, "infra", "port-ranges.json");
32214
+ const ddbScriptPath = (0, import_node_path36.join)(root, "infra", "port-ddb.mjs");
32215
+ if (!(0, import_node_fs38.existsSync)(registryPath) || !(0, import_node_fs38.existsSync)(ddbScriptPath)) return null;
31914
32216
  return { root, source, registryPath, ddbScriptPath };
31915
32217
  }
31916
32218
  function resolvePortRangeInfra(cwd, packageDir) {
31917
32219
  const direct = portRangeInfraAt(cwd, "cwd");
31918
32220
  if (direct) return direct;
31919
- for (let dir = cwd; ; dir = (0, import_node_path34.dirname)(dir)) {
31920
- const sibling = portRangeInfraAt((0, import_node_path34.join)(dir, "MMI-Hub"), "sibling-hub");
32221
+ for (let dir = cwd; ; dir = (0, import_node_path36.dirname)(dir)) {
32222
+ const sibling = portRangeInfraAt((0, import_node_path36.join)(dir, "MMI-Hub"), "sibling-hub");
31921
32223
  if (sibling) return sibling;
31922
- const parent = (0, import_node_path34.dirname)(dir);
32224
+ const parent = (0, import_node_path36.dirname)(dir);
31923
32225
  if (parent === dir) break;
31924
32226
  }
31925
32227
  if (packageDir) {
31926
- const pkgRoot = (0, import_node_path34.join)(packageDir, "..", "..");
32228
+ const pkgRoot = (0, import_node_path36.join)(packageDir, "..", "..");
31927
32229
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
31928
32230
  if (pkgFrom) return pkgFrom;
31929
32231
  }
@@ -32117,8 +32419,8 @@ function registerStageCommands(program3) {
32117
32419
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
32118
32420
  return decideStage({
32119
32421
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
32120
- hasCompose: (0, import_node_fs38.existsSync)((0, import_node_path35.join)(process.cwd(), "docker-compose.yml")),
32121
- hasEnvExample: (0, import_node_fs38.existsSync)((0, import_node_path35.join)(process.cwd(), ".env.example"))
32422
+ hasCompose: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), "docker-compose.yml")),
32423
+ hasEnvExample: (0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), ".env.example"))
32122
32424
  });
32123
32425
  }
32124
32426
  async function fetchStageVaultEnvMerge() {
@@ -32610,9 +32912,9 @@ function registerBoardCommands(program3) {
32610
32912
  }
32611
32913
 
32612
32914
  // src/merge-cleanup.ts
32613
- var import_node_fs39 = require("node:fs");
32614
- var import_promises8 = require("node:fs/promises");
32615
- var import_node_path37 = require("node:path");
32915
+ var import_node_fs40 = require("node:fs");
32916
+ var import_promises9 = require("node:fs/promises");
32917
+ var import_node_path39 = require("node:path");
32616
32918
  var import_node_os15 = require("node:os");
32617
32919
  var import_node_child_process18 = require("node:child_process");
32618
32920
 
@@ -32699,23 +33001,23 @@ function boardAdvanceFailureMessage(result) {
32699
33001
  }
32700
33002
 
32701
33003
  // src/deferred-registry-store.ts
32702
- var import_promises7 = require("node:fs/promises");
32703
- var import_node_path36 = require("node:path");
32704
- var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
33004
+ var import_promises8 = require("node:fs/promises");
33005
+ var import_node_path38 = require("node:path");
33006
+ var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
32705
33007
  async function atomicWrite(target, contents) {
32706
33008
  const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
32707
- await (0, import_promises7.writeFile)(tmp, contents, "utf8");
33009
+ await (0, import_promises8.writeFile)(tmp, contents, "utf8");
32708
33010
  try {
32709
33011
  await renameWithRetry(tmp, target);
32710
33012
  } catch (e) {
32711
- await (0, import_promises7.unlink)(tmp).catch(() => void 0);
33013
+ await (0, import_promises8.unlink)(tmp).catch(() => void 0);
32712
33014
  throw e;
32713
33015
  }
32714
33016
  }
32715
33017
  async function renameWithRetry(from, to, attempts = 5, backoffMs = 20) {
32716
33018
  for (let i = 0; ; i++) {
32717
33019
  try {
32718
- await (0, import_promises7.rename)(from, to);
33020
+ await (0, import_promises8.rename)(from, to);
32719
33021
  return;
32720
33022
  } catch (e) {
32721
33023
  const code = e.code;
@@ -32727,7 +33029,7 @@ async function renameWithRetry(from, to, attempts = 5, backoffMs = 20) {
32727
33029
  async function readStrict(registryPath) {
32728
33030
  let text;
32729
33031
  try {
32730
- text = await (0, import_promises7.readFile)(registryPath, "utf8");
33032
+ text = await (0, import_promises8.readFile)(registryPath, "utf8");
32731
33033
  } catch (e) {
32732
33034
  if (e.code === "ENOENT") return [];
32733
33035
  throw e;
@@ -32744,19 +33046,19 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
32744
33046
  // Lenient read for the sweep's initial fetch: any error (missing/corrupt) yields an empty queue.
32745
33047
  read: async () => {
32746
33048
  try {
32747
- return parseDeferredWorktreesFile(await (0, import_promises7.readFile)(registryPath, "utf8"));
33049
+ return parseDeferredWorktreesFile(await (0, import_promises8.readFile)(registryPath, "utf8"));
32748
33050
  } catch {
32749
33051
  return [];
32750
33052
  }
32751
33053
  },
32752
33054
  // Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
32753
33055
  write: async (entries) => {
32754
- await (0, import_promises7.mkdir)((0, import_node_path36.dirname)(registryPath), { recursive: true });
33056
+ await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
32755
33057
  await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
32756
33058
  },
32757
33059
  // Serialized read-modify-write under the repo-wide lock (#2846).
32758
33060
  update: async (mutate) => {
32759
- await (0, import_promises7.mkdir)((0, import_node_path36.dirname)(registryPath), { recursive: true });
33061
+ await (0, import_promises8.mkdir)((0, import_node_path38.dirname)(registryPath), { recursive: true });
32760
33062
  const deadline = Date.now() + opts.maxWaitMs;
32761
33063
  for (; ; ) {
32762
33064
  const guard = await acquireLock(lockPath, opts, deadline);
@@ -32920,6 +33222,23 @@ ${err.stderr ?? ""}`;
32920
33222
  return { step, status: `failed: ${msg}` };
32921
33223
  }
32922
33224
  }
33225
+ async function bestEffortCloseMissingWorktreeLeases(leaseDir = (0, import_node_path39.join)((0, import_node_os15.homedir)(), ".jerv", "leases"), exists = import_node_fs40.existsSync) {
33226
+ let names = [];
33227
+ try {
33228
+ names = (0, import_node_fs40.readdirSync)(leaseDir);
33229
+ } catch {
33230
+ return;
33231
+ }
33232
+ for (const name of names) {
33233
+ if (!name.endsWith(".json")) continue;
33234
+ try {
33235
+ const rec = JSON.parse((0, import_node_fs40.readFileSync)((0, import_node_path39.join)(leaseDir, name), "utf8"));
33236
+ if (rec.kind !== "worktree" || rec.state === "closed" || typeof rec.ref !== "string" || !rec.ref.trim()) continue;
33237
+ if (!exists(rec.ref)) await bestEffortLeaseClose(rec.ref);
33238
+ } catch {
33239
+ }
33240
+ }
33241
+ }
32923
33242
  async function applyGcPlan(plan, remote, opts = {}) {
32924
33243
  const result = { removedBranches: [], removedRemoteBranches: [], removedTrackingRefs: [], removedWorktreeDirs: [], refused: [], failed: [], pruned: false };
32925
33244
  const beforeWorktrees = parseWorktreePorcelain(
@@ -32927,7 +33246,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
32927
33246
  );
32928
33247
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
32929
33248
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
32930
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path37.dirname)((0, import_node_path37.dirname)(worktreeGitRoot)) : repoRoot2;
33249
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path39.dirname)((0, import_node_path39.dirname)(worktreeGitRoot)) : repoRoot2;
32931
33250
  const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
32932
33251
  const owners = readWorktreeOwners(primaryRepoRoot);
32933
33252
  const removalNow = Date.now();
@@ -32936,7 +33255,9 @@ async function applyGcPlan(plan, remote, opts = {}) {
32936
33255
  const activeWorkspaceDeferred = [];
32937
33256
  const refusesRemoval = (path2, branch) => {
32938
33257
  if (!path2) return false;
32939
- const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot);
33258
+ const activeGuard = decideActiveWorkspaceGuard(path2, activeWorkspaceRoot, process.platform, {
33259
+ cursorAgentHost: isCursorAgentHost()
33260
+ });
32940
33261
  if (activeGuard.action === "refuse") {
32941
33262
  result.refused.push(activeGuard.message);
32942
33263
  const owner2 = findWorktreeOwner(owners, path2);
@@ -32984,7 +33305,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
32984
33305
  const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
32985
33306
  beforeWorktrees,
32986
33307
  startingPath: branch.worktreePath,
32987
- pathExists: (p) => (0, import_node_fs39.existsSync)(p),
33308
+ pathExists: (p) => (0, import_node_fs40.existsSync)(p),
32988
33309
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
32989
33310
  teardownWorktreeStage,
32990
33311
  deferredStore,
@@ -33015,14 +33336,15 @@ async function applyGcPlan(plan, remote, opts = {}) {
33015
33336
  const removeDeps = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
33016
33337
  const cleanupRoots = [
33017
33338
  opts.root ? resolveExplicitScanRoot(opts.root, primaryRepoRoot) : siblingMmiWorktreesRoot(primaryRepoRoot),
33018
- agentWorktreesRoot(primaryRepoRoot)
33339
+ agentWorktreesRoot(primaryRepoRoot),
33340
+ ...helperWorktreeRoots(primaryRepoRoot)
33019
33341
  ];
33020
33342
  for (const wt of worktreeDirsToRemove) {
33021
33343
  const owner = findWorktreeOwner(owners, wt.path);
33022
33344
  let removalAttempted = false;
33023
33345
  try {
33024
33346
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, cleanupRoots, {
33025
- realpath: (path2) => (0, import_node_fs39.realpathSync)(path2)
33347
+ realpath: (path2) => (0, import_node_fs40.realpathSync)(path2)
33026
33348
  });
33027
33349
  if (!cleanupTarget.ok) {
33028
33350
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -33067,12 +33389,27 @@ async function applyGcPlan(plan, remote, opts = {}) {
33067
33389
  }
33068
33390
  }
33069
33391
  }
33392
+ for (const head of plan.reapOriginHeads ?? []) {
33393
+ try {
33394
+ await execFileP2("git", ["push", remote, "--delete", head.branch], { timeout: GIT_TIMEOUT_MS });
33395
+ result.removedRemoteBranches.push(head.branch);
33396
+ } catch (e) {
33397
+ const detail = `${e.message}
33398
+ ${e.stderr ?? ""}`;
33399
+ if (/not found|does not exist|unable to delete|remote ref does not exist/i.test(detail)) {
33400
+ result.removedRemoteBranches.push(head.branch);
33401
+ } else {
33402
+ result.failed.push(`${head.branch}: origin delete failed (${e.message.split("\n")[0]})`);
33403
+ }
33404
+ }
33405
+ }
33070
33406
  try {
33071
33407
  await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS });
33072
33408
  result.pruned = true;
33073
33409
  } catch (e) {
33074
33410
  result.failed.push(`worktree prune: ${e.message.split("\n")[0]}`);
33075
33411
  }
33412
+ await bestEffortCloseMissingWorktreeLeases();
33076
33413
  return result;
33077
33414
  }
33078
33415
  async function pollGhPrChecks(prNumber, repoArgs) {
@@ -33109,13 +33446,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
33109
33446
  const commits = JSON.parse(raw).commits ?? [];
33110
33447
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
33111
33448
  if (!body) return void 0;
33112
- const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path37.join)((0, import_node_os15.tmpdir)(), "mmi-squash-body-"));
33113
- const path2 = (0, import_node_path37.join)(dir, "body.txt");
33114
- (0, import_node_fs39.writeFileSync)(path2, `${body}
33449
+ const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path39.join)((0, import_node_os15.tmpdir)(), "mmi-squash-body-"));
33450
+ const path2 = (0, import_node_path39.join)(dir, "body.txt");
33451
+ (0, import_node_fs40.writeFileSync)(path2, `${body}
33115
33452
  `, "utf8");
33116
33453
  return { path: path2, cleanup: () => {
33117
33454
  try {
33118
- (0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
33455
+ (0, import_node_fs40.rmSync)(dir, { recursive: true, force: true });
33119
33456
  } catch {
33120
33457
  }
33121
33458
  } };
@@ -33235,15 +33572,16 @@ async function createDeferredWorktreeStore() {
33235
33572
  }
33236
33573
  var realWorktreeDirRemover = {
33237
33574
  probe: (p) => {
33575
+ const target = win32LongPath(p);
33238
33576
  let st;
33239
33577
  try {
33240
- st = (0, import_node_fs39.lstatSync)(p);
33578
+ st = (0, import_node_fs40.lstatSync)(target);
33241
33579
  } catch {
33242
33580
  return null;
33243
33581
  }
33244
33582
  if (st.isSymbolicLink()) return "link";
33245
33583
  try {
33246
- (0, import_node_fs39.readlinkSync)(p);
33584
+ (0, import_node_fs40.readlinkSync)(target);
33247
33585
  return "link";
33248
33586
  } catch {
33249
33587
  }
@@ -33251,7 +33589,7 @@ var realWorktreeDirRemover = {
33251
33589
  },
33252
33590
  readdir: (p) => {
33253
33591
  try {
33254
- return (0, import_node_fs39.readdirSync)(p);
33592
+ return (0, import_node_fs40.readdirSync)(win32LongPath(p));
33255
33593
  } catch {
33256
33594
  return [];
33257
33595
  }
@@ -33259,13 +33597,16 @@ var realWorktreeDirRemover = {
33259
33597
  // A directory reparse point (junction / dir-symlink) is detached with rmdir (unlinks the mount point,
33260
33598
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
33261
33599
  detachLink: (p) => {
33600
+ const target = win32LongPath(p);
33262
33601
  try {
33263
- (0, import_node_fs39.rmdirSync)(p);
33602
+ (0, import_node_fs40.rmdirSync)(target);
33264
33603
  } catch {
33265
- (0, import_node_fs39.unlinkSync)(p);
33604
+ (0, import_node_fs40.unlinkSync)(target);
33266
33605
  }
33267
33606
  },
33268
- removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
33607
+ // #4904: Windows MAX_PATH aborts git worktree remove; the fallback must use \\?\ so the dir
33608
+ // (and its lease) do not survive as an unregistered leftover.
33609
+ removeTree: (p) => (0, import_promises9.rm)(win32LongPath(p), { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
33269
33610
  };
33270
33611
  async function resolvePrimaryCheckout(execGit) {
33271
33612
  try {
@@ -33277,13 +33618,13 @@ async function resolvePrimaryCheckout(execGit) {
33277
33618
  function worktreeRemoveDeps(execGit) {
33278
33619
  return {
33279
33620
  git: execGit,
33280
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
33621
+ sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
33281
33622
  // #3064: unlink any reparse point (esp. a `node_modules` junction to base) before the git-native
33282
33623
  // `worktree remove --force`, which would otherwise recurse through it and empty the base checkout.
33283
33624
  detachReparsePoints: (worktreePath) => detachReparsePoints(worktreePath, realWorktreeDirRemover),
33284
33625
  removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover),
33285
33626
  // #4850: verify the directory is actually gone before any caller reports completion.
33286
- pathExists: (worktreePath) => (0, import_node_fs39.existsSync)(worktreePath)
33627
+ pathExists: (worktreePath) => (0, import_node_fs40.existsSync)(worktreePath)
33287
33628
  };
33288
33629
  }
33289
33630
  async function worktreeHasStageState(worktreePath) {
@@ -33297,9 +33638,9 @@ async function worktreeHasStageState(worktreePath) {
33297
33638
  }
33298
33639
  }
33299
33640
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
33300
- if (!(0, import_node_fs39.existsSync)(statePath)) return false;
33641
+ if (!(0, import_node_fs40.existsSync)(statePath)) return false;
33301
33642
  try {
33302
- const state = JSON.parse((0, import_node_fs39.readFileSync)(statePath, "utf8"));
33643
+ const state = JSON.parse((0, import_node_fs40.readFileSync)(statePath, "utf8"));
33303
33644
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
33304
33645
  return Boolean(recordedCwd && isPathUnderDirectory2(recordedCwd, worktreePath));
33305
33646
  } catch {
@@ -33456,7 +33797,7 @@ var PR_SNAPSHOT_READ_DELAY_MS = 2e3;
33456
33797
  async function readRestPrSnapshotWithRetry(prNumber, repo, gh = defaultGhApi, options) {
33457
33798
  const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
33458
33799
  const delayMs = options?.delayMs ?? PR_SNAPSHOT_READ_DELAY_MS;
33459
- const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
33800
+ const sleep3 = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
33460
33801
  let lastError = "no attempt completed";
33461
33802
  for (let attempt = 0; attempt < retries; attempt++) {
33462
33803
  try {
@@ -33734,14 +34075,15 @@ async function checkDocsIndexAtHead(opts, deps) {
33734
34075
  }
33735
34076
 
33736
34077
  // src/worktree-lifecycle-commands.ts
33737
- var import_node_fs41 = require("node:fs");
33738
- var import_promises9 = require("node:fs/promises");
33739
- var import_node_path39 = require("node:path");
34078
+ var import_node_fs42 = require("node:fs");
34079
+ var import_promises10 = require("node:fs/promises");
34080
+ var import_node_os16 = require("node:os");
34081
+ var import_node_path41 = require("node:path");
33740
34082
 
33741
34083
  // src/worktree-install-cache.ts
33742
- var import_node_crypto7 = require("node:crypto");
33743
- var import_node_fs40 = require("node:fs");
33744
- var import_node_path38 = require("node:path");
34084
+ var import_node_crypto8 = require("node:crypto");
34085
+ var import_node_fs41 = require("node:fs");
34086
+ var import_node_path40 = require("node:path");
33745
34087
  var CACHE_DIR = "worktree-install-cache";
33746
34088
  var MANIFEST = "manifest.json";
33747
34089
  var NODE_MODULES2 = "node_modules";
@@ -33754,24 +34096,24 @@ var LOCKFILE_NAMES = [
33754
34096
  "package-lock.json"
33755
34097
  ];
33756
34098
  var realWorktreeInstallCacheFs = {
33757
- exists: import_node_fs40.existsSync,
33758
- readFile: (path2) => (0, import_node_fs40.readFileSync)(path2, "utf8"),
33759
- lstat: (path2) => (0, import_node_fs40.lstatSync)(path2),
33760
- copyDir: (from, to) => (0, import_node_fs40.cpSync)(from, to, { recursive: true, force: true }),
34099
+ exists: import_node_fs41.existsSync,
34100
+ readFile: (path2) => (0, import_node_fs41.readFileSync)(path2, "utf8"),
34101
+ lstat: (path2) => (0, import_node_fs41.lstatSync)(path2),
34102
+ copyDir: (from, to) => (0, import_node_fs41.cpSync)(from, to, { recursive: true, force: true }),
33761
34103
  mkdirp: (path2) => {
33762
- (0, import_node_fs40.mkdirSync)(path2, { recursive: true });
34104
+ (0, import_node_fs41.mkdirSync)(path2, { recursive: true });
33763
34105
  },
33764
- writeFile: (path2, contents) => (0, import_node_fs40.writeFileSync)(path2, contents, "utf8"),
34106
+ writeFile: (path2, contents) => (0, import_node_fs41.writeFileSync)(path2, contents, "utf8"),
33765
34107
  rm: (path2) => {
33766
- (0, import_node_fs40.rmSync)(path2, { recursive: true, force: true });
34108
+ (0, import_node_fs41.rmSync)(path2, { recursive: true, force: true });
33767
34109
  }
33768
34110
  };
33769
34111
  function hashLockfileBytes(contents) {
33770
- return (0, import_node_crypto7.createHash)("sha256").update(contents).digest("hex");
34112
+ return (0, import_node_crypto8.createHash)("sha256").update(contents).digest("hex");
33771
34113
  }
33772
34114
  function resolveWorktreeInstallLockfile(packageDir, fs2 = realWorktreeInstallCacheFs) {
33773
34115
  for (const name of LOCKFILE_NAMES) {
33774
- const path2 = (0, import_node_path38.join)(packageDir, name);
34116
+ const path2 = (0, import_node_path40.join)(packageDir, name);
33775
34117
  if (!fs2.exists(path2)) continue;
33776
34118
  try {
33777
34119
  const hash = hashLockfileBytes(fs2.readFile(path2));
@@ -33786,8 +34128,8 @@ function worktreeInstallCacheEntry(primaryRoot, lockfileHash) {
33786
34128
  const root = repoRuntimeStatePath(primaryRoot, CACHE_DIR, lockfileHash);
33787
34129
  return {
33788
34130
  root,
33789
- manifestPath: (0, import_node_path38.join)(root, MANIFEST),
33790
- nodeModulesPath: (0, import_node_path38.join)(root, NODE_MODULES2)
34131
+ manifestPath: (0, import_node_path40.join)(root, MANIFEST),
34132
+ nodeModulesPath: (0, import_node_path40.join)(root, NODE_MODULES2)
33791
34133
  };
33792
34134
  }
33793
34135
  function readWorktreeInstallCacheManifest(manifestPath, fs2 = realWorktreeInstallCacheFs) {
@@ -33827,7 +34169,7 @@ function invalidateWorktreeInstallCacheEntry(entry, fs2 = realWorktreeInstallCac
33827
34169
  }
33828
34170
  }
33829
34171
  function removeMaterializedTree(packageDir, fs2) {
33830
- const dest = (0, import_node_path38.join)(packageDir, NODE_MODULES2);
34172
+ const dest = (0, import_node_path40.join)(packageDir, NODE_MODULES2);
33831
34173
  if (!fs2.exists(dest)) return;
33832
34174
  fs2.rm(dest);
33833
34175
  if (fs2.exists(dest)) {
@@ -33835,7 +34177,7 @@ function removeMaterializedTree(packageDir, fs2) {
33835
34177
  }
33836
34178
  }
33837
34179
  function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeInstallCacheFs) {
33838
- const dest = (0, import_node_path38.join)(destPackageDir, NODE_MODULES2);
34180
+ const dest = (0, import_node_path40.join)(destPackageDir, NODE_MODULES2);
33839
34181
  try {
33840
34182
  if (fs2.exists(dest)) fs2.rm(dest);
33841
34183
  fs2.mkdirp(destPackageDir);
@@ -33850,7 +34192,7 @@ function materializeCachedNodeModules(entry, destPackageDir, fs2 = realWorktreeI
33850
34192
  }
33851
34193
  }
33852
34194
  async function storeWorktreeInstallCacheEntry(primaryRoot, lockfile3, command, sourcePackageDir, fs2 = realWorktreeInstallCacheFs, now = Date.now()) {
33853
- const source = (0, import_node_path38.join)(sourcePackageDir, NODE_MODULES2);
34195
+ const source = (0, import_node_path40.join)(sourcePackageDir, NODE_MODULES2);
33854
34196
  if (!isMaterializableNodeModulesDir(source, fs2)) return;
33855
34197
  const entry = worktreeInstallCacheEntry(primaryRoot, lockfile3.hash);
33856
34198
  const manifest = {
@@ -34105,12 +34447,36 @@ function classifyStaleLeaks(input) {
34105
34447
  remediation: "mmi-cli worktree gc --apply"
34106
34448
  });
34107
34449
  }
34450
+ for (const leftover of input.originLeftovers ?? []) {
34451
+ leaks.push({
34452
+ kind: "origin-leftover",
34453
+ ref: leftover.branch,
34454
+ detail: leftover.detail,
34455
+ remediation: leftover.autoReap ? "mmi-cli worktree gc --apply (conservative merged-head reap)" : leftover.kind === "open-pr" ? "leave open \u2014 gc will not delete an open PR head" : "mmi-cli worktree gc --apply --train-only (explicit; will not silently delete every non-train branch)"
34456
+ });
34457
+ }
34458
+ for (const helper of input.helperWorktrees ?? []) {
34459
+ leaks.push({
34460
+ kind: "helper-worktree",
34461
+ ref: helper.path,
34462
+ detail: helper.detail,
34463
+ remediation: helper.reapable ? "mmi-cli worktree gc --apply" : "inspect / git -C <primary> worktree remove --force after the helper is abandoned"
34464
+ });
34465
+ }
34466
+ for (const ref of input.missingLeaseRefs ?? []) {
34467
+ leaks.push({
34468
+ kind: "missing-lease",
34469
+ ref,
34470
+ detail: `jerv worktree lease still active but path is gone`,
34471
+ remediation: "mmi-cli worktree gc --apply (closes the lease; JPT #5548 also reaps on sweep)"
34472
+ });
34473
+ }
34108
34474
  return leaks;
34109
34475
  }
34110
34476
  var defaultOrphanDirScanDeps = {
34111
34477
  listDirs: (root) => {
34112
34478
  try {
34113
- return (0, import_node_fs41.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path39.join)(root, e.name));
34479
+ return (0, import_node_fs42.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path41.join)(root, e.name));
34114
34480
  } catch {
34115
34481
  return [];
34116
34482
  }
@@ -34125,8 +34491,12 @@ function scanOrphanDirs(worktreesRoot, worktreeGitRoot, deps = defaultOrphanDirS
34125
34491
  }
34126
34492
  return candidates;
34127
34493
  }
34128
- function formatStaleLeaks(leaks, prLookupFailures = []) {
34129
- const lines = leaks.length ? [`worktree list --stale: ${leaks.length} leak(s)`] : ["worktree list --stale: no leaks found"];
34494
+ function formatStaleLeaks(leaks, prLookupFailures = [], audit) {
34495
+ const blocked = Boolean(audit?.blocksGreen);
34496
+ const lines = leaks.length ? [`worktree list --stale: ${leaks.length} leak(s)`] : blocked ? ["worktree list --stale: incomplete \u2014 refusing a clean report"] : ["worktree list --stale: no leaks found"];
34497
+ if (audit) {
34498
+ for (const line of formatEstateAuditLines(audit)) lines.push(` ${line}`);
34499
+ }
34130
34500
  for (const leak of leaks) {
34131
34501
  lines.push(` [${leak.kind}] ${leak.ref} \u2014 ${leak.detail}`);
34132
34502
  lines.push(` fix: ${leak.remediation}`);
@@ -34276,13 +34646,13 @@ function registerWorktreeCommands(program3) {
34276
34646
  const detached = headBorn && !symbolicBranch;
34277
34647
  const branch = symbolicBranch || (detached ? "HEAD" : "");
34278
34648
  if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
34279
- const gitFile = (0, import_node_path39.join)(wtPath, ".git");
34280
- const isLinked = (0, import_node_fs41.existsSync)(gitFile) && (0, import_node_fs41.statSync)(gitFile).isFile();
34649
+ const gitFile = (0, import_node_path41.join)(wtPath, ".git");
34650
+ const isLinked = (0, import_node_fs42.existsSync)(gitFile) && (0, import_node_fs42.statSync)(gitFile).isFile();
34281
34651
  if (apply && !isLinked) {
34282
34652
  return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
34283
34653
  }
34284
34654
  const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
34285
- const primaryCheckout = commonDir ? (0, import_node_path39.dirname)(commonDir) : wtPath;
34655
+ const primaryCheckout = commonDir ? (0, import_node_path41.dirname)(commonDir) : wtPath;
34286
34656
  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);
34287
34657
  const orphan = classifyOrphanedWorktree({
34288
34658
  branch,
@@ -34320,13 +34690,13 @@ function registerWorktreeCommands(program3) {
34320
34690
  }
34321
34691
  const landStatus = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "unreadable" }))).stdout || "";
34322
34692
  const gitInWorktree = async (args) => (await execFileP2("git", ["-C", wtPath, ...args], { timeout: GIT_TIMEOUT_MS })).stdout;
34323
- const orphanDirt = headBorn ? void 0 : await classifyOrphanWorktreeDirt(landStatus, { git: gitInWorktree, readTextFile: (p) => (0, import_promises9.readFile)(p, "utf8") });
34693
+ const orphanDirt = headBorn ? void 0 : await classifyOrphanWorktreeDirt(landStatus, { git: gitInWorktree, readTextFile: (p) => (0, import_promises10.readFile)(p, "utf8") });
34324
34694
  const landDirty = headBorn ? landStatus.trim().length > 0 : orphanDirt.dirt !== "clean";
34325
34695
  if (landDirty) {
34326
34696
  return fail(`worktree land: refusing to land '${branch}' \u2014 uncommitted changes in ${wtPath}; commit, stash, or remove the worktree manually`, { code: ERROR_CODES.ERR_BAD_ENUM });
34327
34697
  }
34328
34698
  const orphanTip = orphan.orphan ? orphanDirt?.tip ?? await readOrphanWorktreeTip(
34329
- { git: gitInWorktree, readTextFile: (p) => (0, import_promises9.readFile)(p, "utf8") },
34699
+ { git: gitInWorktree, readTextFile: (p) => (0, import_promises10.readFile)(p, "utf8") },
34330
34700
  orphan.kind
34331
34701
  ) : void 0;
34332
34702
  const lastCommit = orphanTip ? {
@@ -34346,7 +34716,9 @@ function registerWorktreeCommands(program3) {
34346
34716
  const reportedMergeState = treeOnly ? "not-checked" : mergeVerdict.state;
34347
34717
  const landOwner = lookupWorktreeOwner(primaryCheckout, wtPath);
34348
34718
  const activeWorkspaceRoot = resolveActiveWorkspaceRoot();
34349
- const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot);
34719
+ const activeGuard = decideActiveWorkspaceGuard(wtPath, activeWorkspaceRoot, process.platform, {
34720
+ cursorAgentHost: isCursorAgentHost()
34721
+ });
34350
34722
  if (activeGuard.action === "refuse") {
34351
34723
  const deferredStore = await createDeferredWorktreeStore();
34352
34724
  if (deferredStore) {
@@ -34422,6 +34794,9 @@ function registerWorktreeCommands(program3) {
34422
34794
  step: "remove worktree",
34423
34795
  status: removeOutcome.status === "removed" ? removeOutcome.recovery ? `done (${removeOutcome.recovery})` : "done" : removeOutcome.remainsOnDisk ? `failed: ${removeOutcome.error ?? "lock held"} \u2014 the directory "${wtPath}" is still on disk; cd every shell out of it (a cwd inside holds it open on Windows), then delete that directory` : `failed: ${removeOutcome.error ?? "lock held"} \u2014 run: git -C "${primaryCheckout}" worktree remove --force "${wtPath}"`
34424
34796
  });
34797
+ if (!removeOutcome.remainsOnDisk) {
34798
+ report.push(await bestEffortLeaseClose(wtPath));
34799
+ }
34425
34800
  if (!shouldContinueLandCleanup(removeOutcome.status)) {
34426
34801
  const result2 = {
34427
34802
  dryRun: false,
@@ -34506,8 +34881,17 @@ function registerWorktreeCommands(program3) {
34506
34881
  if (o.stale) {
34507
34882
  const leaks = classifyStaleLeaks(ctx);
34508
34883
  const failures = ctx.prLookupFailures ?? [];
34509
- if (o.json) return console.log(JSON.stringify({ stale: leaks, count: leaks.length, prLookupFailures: failures, complete: failures.length === 0 }, null, 2));
34510
- return console.log(formatStaleLeaks(leaks, failures));
34884
+ const complete = failures.length === 0 && !ctx.audit?.blocksGreen;
34885
+ if (o.json) {
34886
+ return console.log(JSON.stringify({
34887
+ stale: leaks,
34888
+ count: leaks.length,
34889
+ prLookupFailures: failures,
34890
+ complete,
34891
+ audit: ctx.audit
34892
+ }, null, 2));
34893
+ }
34894
+ return console.log(formatStaleLeaks(leaks, failures, ctx.audit));
34511
34895
  }
34512
34896
  if (o.json) return console.log(JSON.stringify({ worktrees: ctx.worktrees }, null, 2));
34513
34897
  if (!ctx.worktrees.length) return console.log("worktree list: no worktrees");
@@ -34531,10 +34915,18 @@ async function gatherWorktreeContext() {
34531
34915
  const branchOut = (await execFileP2("git", ["branch", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
34532
34916
  const localBranches = branchOut.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
34533
34917
  const currentBranch2 = (await execFileP2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || void 0;
34534
- const { prs, failures: prLookupFailures } = await resolveBranchPrs(localBranches.filter((b) => !PROTECTED_BRANCHES2.has(b)), STALE_PR_LOOKUP_LIMIT);
34918
+ const remoteBranchOut = (await execFileP2("git", ["branch", "-r", "--format=%(refname:short)"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
34919
+ const remoteBranches = remoteBranchOut.split(/\r?\n/).map((l) => l.trim()).filter((b) => b.startsWith("origin/") && b !== "origin/HEAD");
34920
+ const remoteNames = remoteBranches.map((b) => b.replace(/^origin\//, ""));
34921
+ const prTargets = [.../* @__PURE__ */ new Set([
34922
+ ...localBranches.filter((b) => !PROTECTED_BRANCHES2.has(b)),
34923
+ ...remoteNames.filter((b) => !PROTECTED_BRANCHES2.has(b))
34924
+ ])];
34925
+ const { prs, failures: prLookupFailures } = await resolveBranchPrs(prTargets, STALE_PR_LOOKUP_LIMIT);
34535
34926
  const openPrBranches = /* @__PURE__ */ new Set();
34536
34927
  const closedBranches = /* @__PURE__ */ new Set();
34537
34928
  const closedUnmergedBranches = /* @__PURE__ */ new Set();
34929
+ const mergedPrBranches = /* @__PURE__ */ new Set();
34538
34930
  const byBranch = /* @__PURE__ */ new Map();
34539
34931
  for (const pr2 of prs) {
34540
34932
  const arr = byBranch.get(pr2.headRefName) ?? [];
@@ -34545,6 +34937,7 @@ async function gatherWorktreeContext() {
34545
34937
  if (states.some((s) => s === "OPEN")) openPrBranches.add(br);
34546
34938
  else if (states.some((s) => s === "MERGED" || s === "CLOSED")) {
34547
34939
  closedBranches.add(br);
34940
+ if (states.some((s) => s === "MERGED")) mergedPrBranches.add(br);
34548
34941
  if (!states.some((s) => s === "MERGED") && states.some((s) => s === "CLOSED")) {
34549
34942
  closedUnmergedBranches.add(br);
34550
34943
  }
@@ -34556,15 +34949,53 @@ async function gatherWorktreeContext() {
34556
34949
  if (s) stages.push({ path: wt.path, port: s.port });
34557
34950
  }
34558
34951
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
34559
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path39.dirname)((0, import_node_path39.dirname)(worktreeGitRoot)) : repoRoot2;
34952
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path41.dirname)((0, import_node_path41.dirname)(worktreeGitRoot)) : repoRoot2;
34560
34953
  const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
34561
34954
  let orphanDirs = [];
34562
- if ((0, import_node_fs41.existsSync)(wtRoot)) {
34955
+ if ((0, import_node_fs42.existsSync)(wtRoot)) {
34563
34956
  orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
34564
34957
  ...defaultOrphanDirScanDeps,
34565
34958
  listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
34566
34959
  });
34567
34960
  }
34961
+ const repoContainer = worktreesRootOf(primaryRepoRoot);
34962
+ if ((0, import_node_fs42.existsSync)(repoContainer)) {
34963
+ for (const dir of defaultOrphanDirScanDeps.listDirs(repoContainer)) {
34964
+ if (orphanDirs.some((o) => o.path === dir)) continue;
34965
+ const inspected = inspectSiblingWorktreeDir(dir, worktreeGitRoot);
34966
+ const classified = classifySiblingWorktreeDir(inspected);
34967
+ if (classified.cleanup) {
34968
+ orphanDirs.push({ path: dir, reason: classified.cleanup.reason });
34969
+ continue;
34970
+ }
34971
+ if (inspected.gitType === "missing" && pathProvesRepoContainerOwnership(dir, repoContainer)) {
34972
+ orphanDirs.push({ path: dir, reason: "orphaned-folder" });
34973
+ }
34974
+ }
34975
+ }
34976
+ const originLeftovers = classifyOriginLeftovers({
34977
+ remoteBranches,
34978
+ localBranches,
34979
+ protectedBranches: PROTECTED_BRANCHES2,
34980
+ openPrBranches,
34981
+ mergedPrBranches,
34982
+ closedUnmergedBranches
34983
+ });
34984
+ const helperWorktrees = scanHelperWorktrees(primaryRepoRoot, worktreeGitRoot);
34985
+ const leaseRefs = readJervWorktreeLeaseRefs();
34986
+ const missingLeaseRefs = leaseRefs.filter((ref) => !(0, import_node_fs42.existsSync)(ref));
34987
+ const remoteUrls = await gitRemoteUrls();
34988
+ const otherCheckouts = discoverOtherPrimaries(primaryRepoRoot).map((path2) => ({
34989
+ path: path2,
34990
+ remoteUrls: gitConfigRemoteUrls(path2)
34991
+ }));
34992
+ const audit = classifyEstateAudit({
34993
+ auditedClone: primaryRepoRoot,
34994
+ remoteUrls,
34995
+ otherCheckouts,
34996
+ leaseRefs,
34997
+ thisWorktreesRoot: repoContainer
34998
+ });
34568
34999
  return {
34569
35000
  worktrees,
34570
35001
  localBranches,
@@ -34574,9 +35005,96 @@ async function gatherWorktreeContext() {
34574
35005
  closedUnmergedBranches,
34575
35006
  stages,
34576
35007
  orphanDirs,
34577
- prLookupFailures
35008
+ prLookupFailures,
35009
+ originLeftovers,
35010
+ helperWorktrees,
35011
+ missingLeaseRefs,
35012
+ audit
34578
35013
  };
34579
35014
  }
35015
+ function gitConfigRemoteUrls(checkout) {
35016
+ const gitPath = (0, import_node_path41.join)(checkout, ".git");
35017
+ let configPath = (0, import_node_path41.join)(checkout, ".git", "config");
35018
+ try {
35019
+ const st = (0, import_node_fs42.statSync)(gitPath);
35020
+ if (st.isFile()) return [];
35021
+ } catch {
35022
+ return [];
35023
+ }
35024
+ try {
35025
+ const text = (0, import_node_fs42.readFileSync)(configPath, "utf8");
35026
+ return [...text.matchAll(/^\s*url\s*=\s*(.+)$/gm)].map((m) => m[1].trim());
35027
+ } catch {
35028
+ return [];
35029
+ }
35030
+ }
35031
+ async function gitRemoteUrls() {
35032
+ const out = (await execFileP2("git", ["remote", "-v"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
35033
+ return [...new Set(
35034
+ out.split(/\r?\n/).map((line) => line.trim().split(/\s+/)[1]).filter((url) => Boolean(url))
35035
+ )];
35036
+ }
35037
+ function discoverOtherPrimaries(primaryRepoRoot) {
35038
+ const found = /* @__PURE__ */ new Set();
35039
+ const parent = (0, import_node_path41.dirname)(primaryRepoRoot);
35040
+ try {
35041
+ for (const name of (0, import_node_fs42.readdirSync)(parent)) {
35042
+ const path2 = (0, import_node_path41.join)(parent, name);
35043
+ if (path2 === primaryRepoRoot) continue;
35044
+ if ((0, import_node_fs42.existsSync)((0, import_node_path41.join)(path2, ".git"))) found.add(path2);
35045
+ }
35046
+ } catch {
35047
+ }
35048
+ const mirror = (0, import_node_path41.join)((0, import_node_os16.homedir)(), "Projects", (0, import_node_path41.basename)(primaryRepoRoot));
35049
+ if (mirror !== primaryRepoRoot && (0, import_node_fs42.existsSync)((0, import_node_path41.join)(mirror, ".git"))) found.add(mirror);
35050
+ return [...found];
35051
+ }
35052
+ function readJervWorktreeLeaseRefs(leaseDir = (0, import_node_path41.join)((0, import_node_os16.homedir)(), ".jerv", "leases")) {
35053
+ try {
35054
+ const refs = [];
35055
+ for (const name of (0, import_node_fs42.readdirSync)(leaseDir)) {
35056
+ if (!name.endsWith(".json")) continue;
35057
+ try {
35058
+ const rec = JSON.parse((0, import_node_fs42.readFileSync)((0, import_node_path41.join)(leaseDir, name), "utf8"));
35059
+ if (rec.kind === "worktree" && rec.state !== "closed" && typeof rec.ref === "string" && rec.ref.trim()) {
35060
+ refs.push(rec.ref);
35061
+ }
35062
+ } catch {
35063
+ }
35064
+ }
35065
+ return refs;
35066
+ } catch {
35067
+ return [];
35068
+ }
35069
+ }
35070
+ function scanHelperWorktrees(thisPrimary, thisWorktreeGitRoot) {
35071
+ const primaries = [thisPrimary, ...discoverOtherPrimaries(thisPrimary)];
35072
+ const out = [];
35073
+ for (const primary of primaries) {
35074
+ const gitRoot = primary === thisPrimary ? thisWorktreeGitRoot : (0, import_node_path41.join)(primary, ".git", "worktrees");
35075
+ for (const root of helperWorktreeRoots(primary)) {
35076
+ if (!(0, import_node_fs42.existsSync)(root)) continue;
35077
+ for (const dir of defaultOrphanDirScanDeps.listDirs(root)) {
35078
+ const inspected = inspectSiblingWorktreeDir(dir, gitRoot);
35079
+ const classified = classifySiblingWorktreeDir(inspected);
35080
+ if (classified.cleanup) {
35081
+ out.push({
35082
+ path: dir,
35083
+ detail: `${classified.cleanup.reason} helper worktree under ${root}`,
35084
+ reapable: true
35085
+ });
35086
+ } else {
35087
+ out.push({
35088
+ path: dir,
35089
+ detail: `helper worktree outside ../mmi-worktrees (${classified.skip?.reason ?? inspected.gitType}) at ${dir}`,
35090
+ reapable: false
35091
+ });
35092
+ }
35093
+ }
35094
+ }
35095
+ }
35096
+ return out;
35097
+ }
34580
35098
  async function bestEffortGit(args, cwd, step, timeoutMs = GIT_TIMEOUT_MS) {
34581
35099
  try {
34582
35100
  const fullArgs = cwd ? ["-C", cwd, ...args] : args;
@@ -34595,8 +35113,8 @@ ${err.stderr ?? ""}`;
34595
35113
  }
34596
35114
 
34597
35115
  // src/issue-commands.ts
34598
- var import_node_fs42 = require("node:fs");
34599
- var import_node_crypto8 = require("node:crypto");
35116
+ var import_node_fs43 = require("node:fs");
35117
+ var import_node_crypto9 = require("node:crypto");
34600
35118
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
34601
35119
  var ReparentConflictError = class extends Error {
34602
35120
  constructor(message, payload) {
@@ -34613,7 +35131,7 @@ async function editIssue(client, options, deps = {}) {
34613
35131
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
34614
35132
  const patch = {};
34615
35133
  let bodyChanged = false;
34616
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs42.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
35134
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs43.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
34617
35135
  if (options.titleFile !== void 0) {
34618
35136
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
34619
35137
  } else if (options.title !== void 0) {
@@ -34887,7 +35405,7 @@ function rowIdempotencyKey(batchKey, spec) {
34887
35405
  const identity = `${spec.type}
34888
35406
  ${spec.title.trim()}
34889
35407
  ${spec.body ?? ""}`;
34890
- const hash = (0, import_node_crypto8.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
35408
+ const hash = (0, import_node_crypto9.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
34891
35409
  return `${batchKey}:${hash}`;
34892
35410
  }
34893
35411
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -35218,7 +35736,7 @@ function extendCreateCommand(issue2, batchAttach) {
35218
35736
  if (opts.batch) {
35219
35737
  let specs;
35220
35738
  try {
35221
- const raw = (0, import_node_fs42.readFileSync)(opts.batch, "utf8");
35739
+ const raw = (0, import_node_fs43.readFileSync)(opts.batch, "utf8");
35222
35740
  specs = JSON.parse(raw);
35223
35741
  if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
35224
35742
  } catch (e) {
@@ -35293,8 +35811,8 @@ ${lines}`, {
35293
35811
  }
35294
35812
 
35295
35813
  // src/train-commands.ts
35296
- var import_node_fs43 = require("node:fs");
35297
- var import_node_path40 = require("node:path");
35814
+ var import_node_fs44 = require("node:fs");
35815
+ var import_node_path42 = require("node:path");
35298
35816
  var RELEASE_BUMP_INTENTS = ["major", "minor", "patch"];
35299
35817
  function resolveReleaseBumpIntent(raw) {
35300
35818
  const intent = typeof raw === "string" ? raw.trim() : "";
@@ -35305,7 +35823,7 @@ function resolveReleaseBumpIntent(raw) {
35305
35823
  }
35306
35824
  function readRepoVersion() {
35307
35825
  try {
35308
- return JSON.parse((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
35826
+ return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
35309
35827
  } catch {
35310
35828
  return void 0;
35311
35829
  }
@@ -35462,9 +35980,9 @@ function registerDeployCommands(program3) {
35462
35980
  }
35463
35981
 
35464
35982
  // src/discovery-commands.ts
35465
- var import_node_fs44 = require("node:fs");
35466
- var import_node_os16 = require("node:os");
35467
- var import_node_path41 = require("node:path");
35983
+ var import_node_fs45 = require("node:fs");
35984
+ var import_node_os17 = require("node:os");
35985
+ var import_node_path43 = require("node:path");
35468
35986
  var GC_GH_TIMEOUT_MS3 = 2e4;
35469
35987
  async function collectStatus() {
35470
35988
  const repo = await resolveRepo();
@@ -35652,10 +36170,10 @@ async function collectOnboardStatus(opts = {}) {
35652
36170
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
35653
36171
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
35654
36172
  }
35655
- const home = (0, import_node_os16.homedir)();
36173
+ const home = (0, import_node_os17.homedir)();
35656
36174
  const plugin = onboardPluginGate({
35657
- readKnown: () => readFileSyncSafe((0, import_node_path41.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs44.readFileSync),
35658
- readSettings: () => readFileSyncSafe((0, import_node_path41.join)(home, ".claude", "settings.json"), import_node_fs44.readFileSync)
36175
+ readKnown: () => readFileSyncSafe((0, import_node_path43.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs45.readFileSync),
36176
+ readSettings: () => readFileSyncSafe((0, import_node_path43.join)(home, ".claude", "settings.json"), import_node_fs45.readFileSync)
35659
36177
  });
35660
36178
  return { track, board, registry: registry2, secrets, plugin, estateCli, doors: opts.doors ?? [], nextCommand };
35661
36179
  }
@@ -35815,7 +36333,7 @@ function formatExplainCommand(cmd, rootName) {
35815
36333
  return lines.join("\n").trimEnd();
35816
36334
  }
35817
36335
  function formatExplainGroup(cmd, rootName) {
35818
- const canonical = (node) => canonicalPathFor(node.path) ?? node.path;
36336
+ const canonical = (node) => node.path;
35819
36337
  const lines = [
35820
36338
  `${rootName} ${canonical(cmd)}${cmd.description ? ` \u2014 ${cmd.description}` : ""}`,
35821
36339
  `Category: ${cmd.category} \xB7 Discovery: ${cmd.discovery}`,
@@ -35841,7 +36359,7 @@ function formatExplainLoop(playbook) {
35841
36359
  }
35842
36360
  function findCommandInManifest(manifest, commandPath3) {
35843
36361
  const visit = (command) => {
35844
- if (command.path === commandPath3 || canonicalPathFor(command.path) === commandPath3) return command;
36362
+ if (command.path === commandPath3 || command.flat_path === commandPath3) return command;
35845
36363
  for (const child2 of command.subcommands) {
35846
36364
  const found = visit(child2);
35847
36365
  if (found) return found;
@@ -35877,7 +36395,7 @@ function registerExplainCommand(program3) {
35877
36395
  }
35878
36396
 
35879
36397
  // src/pr-commands.ts
35880
- var import_promises10 = require("node:fs/promises");
36398
+ var import_promises11 = require("node:fs/promises");
35881
36399
  var GC_GH_TIMEOUT_MS4 = 2e4;
35882
36400
  var CHECKS_WATCH_POLL_MS = 15e3;
35883
36401
  var CHECKS_WATCH_TIMEOUT_MS = 10 * 6e4;
@@ -36046,10 +36564,10 @@ function registerPrLifecycleCommands(program3) {
36046
36564
  let body;
36047
36565
  try {
36048
36566
  if (o.title || o.titleFile) {
36049
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises10.readFile, readStdin });
36567
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
36050
36568
  }
36051
36569
  if (o.body || o.bodyFile) {
36052
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises10.readFile, readStdin });
36570
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
36053
36571
  }
36054
36572
  } catch (e) {
36055
36573
  return fail(`pr edit: ${e.message}`);
@@ -36079,7 +36597,7 @@ function registerPrLifecycleCommands(program3) {
36079
36597
  }
36080
36598
  let body;
36081
36599
  try {
36082
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises10.readFile, readStdin });
36600
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
36083
36601
  } catch (e) {
36084
36602
  return fail(`pr comment: ${e.message}`);
36085
36603
  }
@@ -36790,19 +37308,19 @@ function registerOrgHealthQuery(program3, deps = defaultOrgHealthQueryDeps()) {
36790
37308
  }
36791
37309
 
36792
37310
  // src/plugin-release-catchup.ts
36793
- var import_node_fs45 = require("node:fs");
36794
- var import_node_path42 = require("node:path");
36795
- var import_node_os17 = require("node:os");
37311
+ var import_node_fs46 = require("node:fs");
37312
+ var import_node_path44 = require("node:path");
37313
+ var import_node_os18 = require("node:os");
36796
37314
  var RELEASE_CATCHUP_TTL_MS = 24 * 60 * 60 * 1e3;
36797
37315
  var RELEASE_CATCHUP_DISABLE_ENV = "MMI_NO_RELEASE_CATCHUP";
36798
37316
  function releaseCatchupStatePath(env = process.env) {
36799
37317
  if (env.MMI_RELEASE_CATCHUP_STATE) return env.MMI_RELEASE_CATCHUP_STATE;
36800
37318
  if (process.platform === "win32") {
36801
- const base2 = env.LOCALAPPDATA || (0, import_node_path42.join)((0, import_node_os17.homedir)(), "AppData", "Local");
36802
- return (0, import_node_path42.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
37319
+ const base2 = env.LOCALAPPDATA || (0, import_node_path44.join)((0, import_node_os18.homedir)(), "AppData", "Local");
37320
+ return (0, import_node_path44.join)(base2, "MMI Future", "mmi-cli", "release-catchup.json");
36803
37321
  }
36804
- const base = env.XDG_STATE_HOME || (0, import_node_path42.join)((0, import_node_os17.homedir)(), ".local", "state");
36805
- return (0, import_node_path42.join)(base, "mmi-cli", "release-catchup.json");
37322
+ const base = env.XDG_STATE_HOME || (0, import_node_path44.join)((0, import_node_os18.homedir)(), ".local", "state");
37323
+ return (0, import_node_path44.join)(base, "mmi-cli", "release-catchup.json");
36806
37324
  }
36807
37325
  function releaseCatchupDue(state, now, force = false) {
36808
37326
  if (force) return true;
@@ -36812,7 +37330,7 @@ function releaseCatchupDue(state, now, force = false) {
36812
37330
  function newestCachedPluginVersion(home) {
36813
37331
  let names;
36814
37332
  try {
36815
- names = (0, import_node_fs45.readdirSync)(pluginCacheRoot(home));
37333
+ names = (0, import_node_fs46.readdirSync)(pluginCacheRoot(home));
36816
37334
  } catch {
36817
37335
  return void 0;
36818
37336
  }
@@ -36820,15 +37338,15 @@ function newestCachedPluginVersion(home) {
36820
37338
  }
36821
37339
  function marketplaceClonePath(home) {
36822
37340
  try {
36823
- const parsed = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
37341
+ const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "known_marketplaces.json"), "utf8"));
36824
37342
  if (parsed.mutmutco?.installLocation) return parsed.mutmutco.installLocation;
36825
37343
  } catch {
36826
37344
  }
36827
- return (0, import_node_path42.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
37345
+ return (0, import_node_path44.join)(home, ".claude", "plugins", "marketplaces", "mutmutco");
36828
37346
  }
36829
37347
  function readCatalogVersion(home) {
36830
37348
  try {
36831
- const parsed = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
37349
+ const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(marketplaceClonePath(home), ".claude-plugin", "marketplace.json"), "utf8"));
36832
37350
  return parsed.plugins?.find((p) => p.name === "mmi")?.version;
36833
37351
  } catch {
36834
37352
  return void 0;
@@ -36836,7 +37354,7 @@ function readCatalogVersion(home) {
36836
37354
  }
36837
37355
  function readMmiInstallRecord(home) {
36838
37356
  try {
36839
- const parsed = JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
37357
+ const parsed = JSON.parse((0, import_node_fs46.readFileSync)((0, import_node_path44.join)(home, ".claude", "plugins", "installed_plugins.json"), "utf8"));
36840
37358
  const record = parsed.plugins?.["mmi@mutmutco"]?.[0];
36841
37359
  return record?.version ? { version: record.version, gitCommitSha: record.gitCommitSha } : void 0;
36842
37360
  } catch {
@@ -36865,7 +37383,7 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
36865
37383
  cliUpdated = true;
36866
37384
  }
36867
37385
  const cliUpdateDetail = cliUpdated ? `CLI ${runningCli} \u2192 ${latest}; the next mmi-cli invocation runs ${latest}` : `CLI ${runningCli} is current against released ${latest}`;
36868
- if (!(0, import_node_fs45.existsSync)(pluginCacheRoot(home))) {
37386
+ if (!(0, import_node_fs46.existsSync)(pluginCacheRoot(home))) {
36869
37387
  deps.writeState(statePath, { checkedAt: deps.now(), latest });
36870
37388
  return {
36871
37389
  ok: true,
@@ -36898,8 +37416,8 @@ async function runReleaseCatchup(home, env, deps, opts = {}) {
36898
37416
  return { ok: false, detail: `${cliUpdateDetail}; plugin install record could not be cleared (still ${prior.version})` };
36899
37417
  }
36900
37418
  const installed = await deps.runClaude(["plugin", "install", "mmi@mutmutco"], "claude plugin install mmi@mutmutco");
36901
- const payload = (0, import_node_path42.join)(pluginCacheRoot(home), latest, ".pi-plugin");
36902
- if (!installed || !(0, import_node_fs45.existsSync)(payload)) {
37419
+ const payload = (0, import_node_path44.join)(pluginCacheRoot(home), latest, ".pi-plugin");
37420
+ if (!installed || !(0, import_node_fs46.existsSync)(payload)) {
36903
37421
  const why = installed ? `install of ${latest} did not produce a verifiable .pi-plugin payload` : `install of ${latest} failed`;
36904
37422
  if (!prior) return { ok: false, detail: `${cliUpdateDetail}; ${why}; no prior record to restore` };
36905
37423
  const rollback = await restorePriorRecord(home, prior, deps);
@@ -37934,10 +38452,10 @@ function checkRepoWorktrees(probe) {
37934
38452
  ok: !(probe.isOrgRepo && probe.hasRepoLocalWorktrees),
37935
38453
  id: "repo-worktrees",
37936
38454
  label: "repo worktrees",
37937
- fix: "repo-local `.worktrees/` is not the canonical path \u2014 use `mmi-cli worktree create <branch>` (sibling `../worktrees/`), then remove the in-repo `.worktrees/`",
38455
+ fix: "repo-local `.worktrees/` / `.claude/worktrees` are not the canonical path \u2014 use `mmi-cli worktree create <branch>` (sibling `../mmi-worktrees/<Repo>/`), then `mmi-cli worktree gc --apply` for abandoned helper trees",
37938
38456
  verbose: [
37939
38457
  `org repo: ${probe.isOrgRepo ? "yes" : "no"}`,
37940
- `repo-local .worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
38458
+ `repo-local .worktrees/ or .claude/worktrees/: ${probe.hasRepoLocalWorktrees ? "present" : "absent"}`
37941
38459
  ]
37942
38460
  };
37943
38461
  }
@@ -39124,17 +39642,17 @@ function parseOriginRepo(remoteUrl) {
39124
39642
  }
39125
39643
  function ghHostsConfigPath(env, platform2) {
39126
39644
  const sep3 = platform2 === "win32" ? "\\" : "/";
39127
- const join40 = (...parts) => parts.join(sep3);
39645
+ const join41 = (...parts) => parts.join(sep3);
39128
39646
  const explicit = env.GH_CONFIG_DIR?.trim();
39129
- if (explicit) return join40(explicit, "hosts.yml");
39647
+ if (explicit) return join41(explicit, "hosts.yml");
39130
39648
  if (platform2 === "win32") {
39131
39649
  const appData = (env.AppData ?? env.APPDATA)?.trim();
39132
- return appData ? join40(appData, "GitHub CLI", "hosts.yml") : void 0;
39650
+ return appData ? join41(appData, "GitHub CLI", "hosts.yml") : void 0;
39133
39651
  }
39134
39652
  const xdg = env.XDG_CONFIG_HOME?.trim();
39135
- if (xdg) return join40(xdg, "gh", "hosts.yml");
39653
+ if (xdg) return join41(xdg, "gh", "hosts.yml");
39136
39654
  const home = env.HOME?.trim();
39137
- return home ? join40(home, ".config", "gh", "hosts.yml") : void 0;
39655
+ return home ? join41(home, ".config", "gh", "hosts.yml") : void 0;
39138
39656
  }
39139
39657
  function parseGhHostsAccounts(yaml, host = "github.com") {
39140
39658
  let hostIndent = null;
@@ -39184,9 +39702,9 @@ function ghAccountCaveat(announcedLogin, accounts) {
39184
39702
  }
39185
39703
 
39186
39704
  // src/doctor-io.ts
39187
- var import_node_fs46 = require("node:fs");
39188
- var import_node_os18 = require("node:os");
39189
- var import_node_path43 = require("node:path");
39705
+ var import_node_fs47 = require("node:fs");
39706
+ var import_node_os19 = require("node:os");
39707
+ var import_node_path45 = require("node:path");
39190
39708
  var import_node_child_process19 = require("node:child_process");
39191
39709
  var import_node_util8 = require("node:util");
39192
39710
  var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process19.execFile);
@@ -39194,7 +39712,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
39194
39712
  function installedClaudePluginVersion() {
39195
39713
  try {
39196
39714
  const file = JSON.parse(
39197
- (0, import_node_fs46.readFileSync)((0, import_node_path43.join)((0, import_node_os18.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
39715
+ (0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
39198
39716
  );
39199
39717
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
39200
39718
  if (versions.length === 0) return void 0;
@@ -39205,7 +39723,7 @@ function installedClaudePluginVersion() {
39205
39723
  }
39206
39724
  function manifestVersion(path2) {
39207
39725
  try {
39208
- const manifest = JSON.parse((0, import_node_fs46.readFileSync)(path2, "utf8"));
39726
+ const manifest = JSON.parse((0, import_node_fs47.readFileSync)(path2, "utf8"));
39209
39727
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
39210
39728
  } catch {
39211
39729
  return void 0;
@@ -39215,22 +39733,22 @@ function installedSurfacePluginVersion(surface) {
39215
39733
  const token = surfaceToken(surface);
39216
39734
  if (token === "kilo") {
39217
39735
  try {
39218
- const stamp = (0, import_node_fs46.readFileSync)((0, import_node_path43.join)((0, import_node_os18.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
39736
+ const stamp = (0, import_node_fs47.readFileSync)((0, import_node_path45.join)((0, import_node_os19.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
39219
39737
  return stamp || void 0;
39220
39738
  } catch {
39221
39739
  return void 0;
39222
39740
  }
39223
39741
  }
39224
39742
  if (token === "cursor") {
39225
- return manifestVersion((0, import_node_path43.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
39743
+ return manifestVersion((0, import_node_path45.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
39226
39744
  }
39227
39745
  if (token === "jervcode") {
39228
39746
  const entry = mmiPiWrapperEntry();
39229
39747
  if (!entry) return void 0;
39230
- return manifestVersion((0, import_node_path43.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
39748
+ return manifestVersion((0, import_node_path45.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
39231
39749
  }
39232
39750
  if (token === "kimi") {
39233
- return manifestVersion((0, import_node_path43.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
39751
+ return manifestVersion((0, import_node_path45.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
39234
39752
  }
39235
39753
  if (token === "claude") return installedClaudePluginVersion();
39236
39754
  if (token !== "codex") return void 0;
@@ -39268,13 +39786,13 @@ function worktreeRootSync() {
39268
39786
  }
39269
39787
  var gitignorePath = () => {
39270
39788
  const root = worktreeRootSync();
39271
- return root === null ? null : (0, import_node_path43.join)(root, ".gitignore");
39789
+ return root === null ? null : (0, import_node_path45.join)(root, ".gitignore");
39272
39790
  };
39273
39791
  function readGitignore() {
39274
39792
  const path2 = gitignorePath();
39275
39793
  if (path2 === null) return null;
39276
39794
  try {
39277
- return (0, import_node_fs46.readFileSync)(path2, "utf8");
39795
+ return (0, import_node_fs47.readFileSync)(path2, "utf8");
39278
39796
  } catch {
39279
39797
  return null;
39280
39798
  }
@@ -39283,7 +39801,7 @@ function writeGitignore(content) {
39283
39801
  const path2 = gitignorePath();
39284
39802
  if (path2 === null) return false;
39285
39803
  try {
39286
- (0, import_node_fs46.writeFileSync)(path2, content, "utf8");
39804
+ (0, import_node_fs47.writeFileSync)(path2, content, "utf8");
39287
39805
  return true;
39288
39806
  } catch {
39289
39807
  return false;
@@ -39302,7 +39820,7 @@ async function repoRoot() {
39302
39820
  }
39303
39821
  function hasRepoLocalWorktrees() {
39304
39822
  const root = worktreeRootSync();
39305
- return root !== null && (0, import_node_fs46.existsSync)((0, import_node_path43.join)(root, ".worktrees"));
39823
+ return root !== null && ((0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".worktrees")) || (0, import_node_fs47.existsSync)((0, import_node_path45.join)(root, ".claude", "worktrees")));
39306
39824
  }
39307
39825
 
39308
39826
  // src/cross-repo-filing-issue.ts
@@ -39398,7 +39916,7 @@ function binaryOnPath(bin) {
39398
39916
  for (const dir of pathEnvEntries(process.env.PATH ?? "")) {
39399
39917
  for (const ext of exts) {
39400
39918
  try {
39401
- if ((0, import_node_fs47.existsSync)((0, import_node_path44.join)(dir, `${bin}${ext}`))) return true;
39919
+ if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(dir, `${bin}${ext}`))) return true;
39402
39920
  } catch {
39403
39921
  }
39404
39922
  }
@@ -39420,8 +39938,8 @@ ${r.stderr ?? ""}`).catch(() => "");
39420
39938
  function ghMultiAccountCaveat(announcedLogin) {
39421
39939
  try {
39422
39940
  const hostsPath = ghHostsConfigPath(process.env, process.platform);
39423
- if (!hostsPath || !(0, import_node_fs47.existsSync)(hostsPath)) return void 0;
39424
- return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs47.readFileSync)(hostsPath, "utf8")));
39941
+ if (!hostsPath || !(0, import_node_fs48.existsSync)(hostsPath)) return void 0;
39942
+ return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs48.readFileSync)(hostsPath, "utf8")));
39425
39943
  } catch {
39426
39944
  return void 0;
39427
39945
  }
@@ -39429,12 +39947,12 @@ function ghMultiAccountCaveat(announcedLogin) {
39429
39947
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
39430
39948
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
39431
39949
  function envHealLockPath(home) {
39432
- return (0, import_node_path44.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
39950
+ return (0, import_node_path46.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
39433
39951
  }
39434
39952
  async function withEnvHealLock(what, run) {
39435
39953
  try {
39436
39954
  return await withFileLock(
39437
- envHealLockPath((0, import_node_os19.homedir)()),
39955
+ envHealLockPath((0, import_node_os20.homedir)()),
39438
39956
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
39439
39957
  run
39440
39958
  );
@@ -39531,7 +40049,7 @@ function mmiDoctorDeps(opts = {}) {
39531
40049
  const configRoot = surfaceConfigRoot(surface);
39532
40050
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
39533
40051
  const plan = buildPluginCachePlan(
39534
- (0, import_node_os19.homedir)(),
40052
+ (0, import_node_os20.homedir)(),
39535
40053
  running,
39536
40054
  pluginCacheFsDeps(configRoot, () => 0),
39537
40055
  { configRoot, includeStaging: surface !== "codex" }
@@ -39555,14 +40073,14 @@ function mmiDoctorDeps(opts = {}) {
39555
40073
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
39556
40074
  const installed = installedActivePluginVersion(surface);
39557
40075
  const plan = buildPluginCachePlan(
39558
- (0, import_node_os19.homedir)(),
40076
+ (0, import_node_os20.homedir)(),
39559
40077
  running,
39560
40078
  pluginCacheFsDeps(configRoot, () => 0),
39561
40079
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
39562
40080
  );
39563
40081
  const result = applyPluginCachePlan(
39564
40082
  plan,
39565
- (p) => (0, import_node_fs47.rmSync)(p, { recursive: true }),
40083
+ (p) => (0, import_node_fs48.rmSync)(p, { recursive: true }),
39566
40084
  stagingApplyFsGuard(configRoot)
39567
40085
  );
39568
40086
  return {
@@ -39590,12 +40108,12 @@ function mmiDoctorDeps(opts = {}) {
39590
40108
  piPluginState: () => {
39591
40109
  const env = { ...process.env };
39592
40110
  delete env.CLAUDE_PLUGIN_ROOT;
39593
- return readPiPluginState((0, import_node_os19.homedir)(), env);
40111
+ return readPiPluginState((0, import_node_os20.homedir)(), env);
39594
40112
  },
39595
40113
  healPiPlugin: () => {
39596
40114
  const env = { ...process.env };
39597
40115
  delete env.CLAUDE_PLUGIN_ROOT;
39598
- return healPiPluginRegistration((0, import_node_os19.homedir)(), env);
40116
+ return healPiPluginRegistration((0, import_node_os20.homedir)(), env);
39599
40117
  },
39600
40118
  // #4743: the global `claude` binary left as a ~500-byte placeholder by a self-update that died
39601
40119
  // EBUSY mid-install. A local read (package.json + the first bytes of two files) — no npm spawn:
@@ -39634,7 +40152,7 @@ function mmiDoctorDeps(opts = {}) {
39634
40152
  disableAutoUpdate: () => {
39635
40153
  if (detectSurface(process.env) === "codex") return void 0;
39636
40154
  return disableOrgMarketplaceBackgroundUpdates(
39637
- (0, import_node_path44.join)((0, import_node_os19.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE),
40155
+ (0, import_node_path46.join)((0, import_node_os20.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE),
39638
40156
  [MMI_MARKETPLACE_NAME]
39639
40157
  );
39640
40158
  }
@@ -39648,7 +40166,7 @@ function mmiDoctorDeps(opts = {}) {
39648
40166
  // adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
39649
40167
  // get a permanent — demanding an artifact it never asked for.
39650
40168
  docsIndexState: (root) => {
39651
- if (!(0, import_node_fs47.existsSync)((0, import_node_path44.join)(root, DOCS_INDEX_PATH))) return void 0;
40169
+ if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return void 0;
39652
40170
  const real = createDocsIndexDeps(root);
39653
40171
  let docs2;
39654
40172
  const listDocs = () => docs2 ??= real.listDocs();
@@ -39657,7 +40175,7 @@ function mmiDoctorDeps(opts = {}) {
39657
40175
  },
39658
40176
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
39659
40177
  healDocsIndex: (root) => {
39660
- if (!(0, import_node_fs47.existsSync)((0, import_node_path44.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
40178
+ if (!(0, import_node_fs48.existsSync)((0, import_node_path46.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
39661
40179
  const real = createDocsIndexDeps(root);
39662
40180
  let docs2;
39663
40181
  const listDocs = () => docs2 ??= real.listDocs();
@@ -39695,8 +40213,8 @@ function mmiDoctorDeps(opts = {}) {
39695
40213
  });
39696
40214
  const raced = await Promise.race([
39697
40215
  work.then((r) => ({ ...r, timedOut: false })),
39698
- new Promise((resolve5) => {
39699
- ceiling = setTimeout(() => resolve5({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
40216
+ new Promise((resolve6) => {
40217
+ ceiling = setTimeout(() => resolve6({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
39700
40218
  })
39701
40219
  ]);
39702
40220
  if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
@@ -39729,8 +40247,8 @@ function mmiDoctorDeps(opts = {}) {
39729
40247
  incomplete: nb.incomplete,
39730
40248
  timedOut: false
39731
40249
  })),
39732
- new Promise((resolve5) => {
39733
- ceiling = setTimeout(() => resolve5({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
40250
+ new Promise((resolve6) => {
40251
+ ceiling = setTimeout(() => resolve6({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
39734
40252
  })
39735
40253
  ]);
39736
40254
  if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
@@ -40023,19 +40541,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
40023
40541
  });
40024
40542
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
40025
40543
  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) => {
40026
- const path2 = (0, import_node_path44.join)(process.cwd(), ".gitignore");
40027
- const current = (0, import_node_fs47.existsSync)(path2) ? (0, import_node_fs47.readFileSync)(path2, "utf8") : null;
40544
+ const path2 = (0, import_node_path46.join)(process.cwd(), ".gitignore");
40545
+ const current = (0, import_node_fs48.existsSync)(path2) ? (0, import_node_fs48.readFileSync)(path2, "utf8") : null;
40028
40546
  const plan = planManagedGitignore(current);
40029
40547
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
40030
40548
  if (opts.json) {
40031
- if (opts.write && plan.changed) (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
40549
+ if (opts.write && plan.changed) (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
40032
40550
  console.log(JSON.stringify(plan, null, 2));
40033
40551
  if (!opts.write && plan.changed) process.exitCode = 1;
40034
40552
  return;
40035
40553
  }
40036
40554
  if (opts.write) {
40037
40555
  if (plan.changed) {
40038
- (0, import_node_fs47.writeFileSync)(path2, plan.content, "utf8");
40556
+ (0, import_node_fs48.writeFileSync)(path2, plan.content, "utf8");
40039
40557
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
40040
40558
  } else {
40041
40559
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -40177,7 +40695,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
40177
40695
  process.exit(process.exitCode ?? 0);
40178
40696
  });
40179
40697
  });
40180
- gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
40698
+ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").option("--train-only", "explicit keep-only-train path: also reap closed-not-merged / no-PR origin leftovers; never deletes open PR heads (#4903)").action(async (o) => {
40181
40699
  if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
40182
40700
  if (o.scratch) {
40183
40701
  try {
@@ -40194,18 +40712,19 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
40194
40712
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
40195
40713
  let root;
40196
40714
  if (o.root !== void 0) {
40197
- root = (0, import_node_path44.resolve)(o.root);
40198
- if (!(0, import_node_fs47.existsSync)(root) || !(0, import_node_fs47.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
40715
+ root = (0, import_node_path46.resolve)(o.root);
40716
+ if (!(0, import_node_fs48.existsSync)(root) || !(0, import_node_fs48.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
40199
40717
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
40200
40718
  if (isPathUnderDirectory2(gcRepoRoot, root)) {
40201
40719
  return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
40202
40720
  }
40203
40721
  }
40204
40722
  try {
40205
- const plan = await gcPlan(o.remote, limit, { root });
40723
+ const plan = await gcPlan(o.remote, limit, { root, trainOnly: o.trainOnly });
40724
+ const auditedClone = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
40206
40725
  if (root && !o.json) console.log(`worktree gc: scanning the explicitly named root ${root} for worktrees proven to belong to this repo
40207
40726
  `);
40208
- if (o.apply && !o.json) console.log(formatGcPlan(plan, true));
40727
+ if (o.apply && !o.json) console.log(formatGcPlan(plan, true, auditedClone));
40209
40728
  let applyResult;
40210
40729
  if (o.apply) {
40211
40730
  const deferredStore = await createDeferredWorktreeStore();
@@ -40224,7 +40743,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
40224
40743
  if (o.json) {
40225
40744
  console.log(JSON.stringify({ dryRun: !o.apply, remote: o.remote, ...root ? { root } : {}, plan, applyResult }, null, 2));
40226
40745
  } else if (!o.apply) {
40227
- console.log(formatGcPlan(plan, false));
40746
+ console.log(formatGcPlan(plan, false, auditedClone));
40228
40747
  } else {
40229
40748
  if (applyResult) console.log(`
40230
40749
  ${renderGcApplyResult(applyResult, plan.skipped)}`);
@@ -40243,7 +40762,7 @@ function runWorktreeInstall(command, cwd, quiet, opts) {
40243
40762
  quiet ? "ignore" : "inherit",
40244
40763
  "pipe"
40245
40764
  ];
40246
- return new Promise((resolve5, reject) => {
40765
+ return new Promise((resolve6, reject) => {
40247
40766
  const child2 = opts?.shell ? (0, import_node_child_process20.spawn)(command, { cwd, stdio, windowsHide: true, shell: true }) : (() => {
40248
40767
  const [bin, ...args] = command.split(" ");
40249
40768
  const file = isWin2 ? "cmd.exe" : bin;
@@ -40270,7 +40789,7 @@ function runWorktreeInstall(command, cwd, quiet, opts) {
40270
40789
  });
40271
40790
  child2.on("exit", (code) => {
40272
40791
  clearTimeout(timer);
40273
- if (code === 0) return resolve5();
40792
+ if (code === 0) return resolve6();
40274
40793
  const tail = stderrTail.trim();
40275
40794
  reject(new Error(`${command} exited ${code} in ${cwd}${tail ? `
40276
40795
  ${tail}` : ""}`));
@@ -40288,11 +40807,12 @@ async function currentWorktreeRemovalContext(command, force) {
40288
40807
  actor: describeActor({ env: process.env, surface: detectSurface(process.env), cwd }),
40289
40808
  command,
40290
40809
  ...force ? { force: true } : {},
40291
- ...activeWorkspaceRoot ? { activeWorkspaceRoot } : {}
40810
+ ...activeWorkspaceRoot ? { activeWorkspaceRoot } : {},
40811
+ cursorAgentHost: isCursorAgentHost()
40292
40812
  };
40293
40813
  }
40294
40814
  async function unprovenWorktreeReason(wtPath, repoRoot2) {
40295
- if (!(0, import_node_fs47.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
40815
+ if (!(0, import_node_fs48.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
40296
40816
  const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
40297
40817
  const registered = parseWorktreePorcelainEntries(porcelain);
40298
40818
  if (!registered.length) {
@@ -40322,26 +40842,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
40322
40842
  function acquireWorktreeSetupLock(worktreeRoot) {
40323
40843
  const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
40324
40844
  const take = () => {
40325
- const fd = (0, import_node_fs47.openSync)(lockPath, "wx");
40845
+ const fd = (0, import_node_fs48.openSync)(lockPath, "wx");
40326
40846
  try {
40327
- (0, import_node_fs47.writeSync)(fd, String(Date.now()));
40847
+ (0, import_node_fs48.writeSync)(fd, String(Date.now()));
40328
40848
  } finally {
40329
- (0, import_node_fs47.closeSync)(fd);
40849
+ (0, import_node_fs48.closeSync)(fd);
40330
40850
  }
40331
40851
  return () => {
40332
40852
  try {
40333
- (0, import_node_fs47.rmSync)(lockPath, { force: true });
40853
+ (0, import_node_fs48.rmSync)(lockPath, { force: true });
40334
40854
  } catch {
40335
40855
  }
40336
40856
  };
40337
40857
  };
40338
40858
  try {
40339
- (0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(lockPath), { recursive: true });
40859
+ (0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(lockPath), { recursive: true });
40340
40860
  return take();
40341
40861
  } catch {
40342
40862
  try {
40343
- if (Date.now() - (0, import_node_fs47.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
40344
- (0, import_node_fs47.rmSync)(lockPath, { force: true });
40863
+ if (Date.now() - (0, import_node_fs48.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
40864
+ (0, import_node_fs48.rmSync)(lockPath, { force: true });
40345
40865
  return take();
40346
40866
  }
40347
40867
  } catch {
@@ -40457,7 +40977,7 @@ withExamples(mutating(
40457
40977
  }
40458
40978
  if (!resumed) {
40459
40979
  step = `git worktree add ${wtPath}`;
40460
- const wtPathPreExisted = (0, import_node_fs47.existsSync)(wtPath);
40980
+ const wtPathPreExisted = (0, import_node_fs48.existsSync)(wtPath);
40461
40981
  const partialRemove = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
40462
40982
  await withWorktreeAddLock(repoRoot2, () => addWorktreeRobust(wtPath, branch, base, {
40463
40983
  // #4834: `-c core.longpaths=true` rides the add command itself — a Windows worktree path
@@ -40473,13 +40993,13 @@ withExamples(mutating(
40473
40993
  },
40474
40994
  deleteBranch: (b) => execFileP2("git", ["branch", "-D", b], { timeout: GIT_TIMEOUT_MS }).then(() => void 0),
40475
40995
  cleanupPartial: async () => {
40476
- if (wtPathPreExisted || !(0, import_node_fs47.existsSync)(wtPath)) return;
40996
+ if (wtPathPreExisted || !(0, import_node_fs48.existsSync)(wtPath)) return;
40477
40997
  partialRemove.detachReparsePoints(wtPath);
40478
40998
  await execFileP2("git", ["worktree", "remove", "--force", wtPath], { timeout: GIT_TIMEOUT_MS }).catch(() => partialRemove.removeWorktreeDir(wtPath).then(() => void 0));
40479
40999
  await execFileP2("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS }).catch(() => {
40480
41000
  });
40481
41001
  },
40482
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
41002
+ sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
40483
41003
  log: (m) => {
40484
41004
  if (!o.json) console.error(` ${m}`);
40485
41005
  }
@@ -40931,9 +41451,7 @@ docs.command("refs").description("deterministic doc reference gate: every backti
40931
41451
  try {
40932
41452
  const root = await repoRoot();
40933
41453
  const commandPaths = new Set(
40934
- buildCommandManifest(program2).index.map(
40935
- (entry) => entry.house && entry.house !== "core" ? `${entry.house} ${entry.path}` : entry.path
40936
- )
41454
+ buildCommandManifest(program2).index.map((entry) => entry.path)
40937
41455
  );
40938
41456
  const result = runDocRefs(root, { commandPaths });
40939
41457
  if (o.json) {
@@ -41016,7 +41534,7 @@ async function reportWrite(label, res) {
41016
41534
  return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
41017
41535
  }
41018
41536
  var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
41019
- tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; project-admin own dev/rc, master main").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
41537
+ tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; project-admin own dev/rc, master main").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
41020
41538
  try {
41021
41539
  let lines;
41022
41540
  if (o.lines !== void 0) {
@@ -41024,7 +41542,12 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
41024
41542
  lines = Number(o.lines);
41025
41543
  if (!Number.isInteger(lines) || lines < 1 || lines > 2e3) return fail("runtime tenant control: --lines must be an integer between 1 and 2000");
41026
41544
  }
41027
- const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines });
41545
+ if (action === "run-task") {
41546
+ if (!o.task) return fail("runtime tenant control: run-task requires --task <declared-name>");
41547
+ } else if (o.task || o.artifact) {
41548
+ return fail("runtime tenant control: --task/--artifact are valid only for run-task");
41549
+ }
41550
+ const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines, task: o.task, artifact: o.artifact });
41028
41551
  if (!o.json && action === "verify-secrets" && result.secrets) {
41029
41552
  const body = { ok: result.conclusion === "success", secrets: result.secrets, ssmStatus: result.conclusion === "success" ? "Success" : "Failed", raw: result.secretsRaw };
41030
41553
  const { lines: lines2, failure } = renderVerifySecrets(body);
@@ -41046,6 +41569,15 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
41046
41569
  return failGraceful(`runtime tenant control: ${e.message}`);
41047
41570
  }
41048
41571
  });
41572
+ var tenantArtifact = tenant.command("artifact").description("private stage-bound inputs for declared tenant tasks");
41573
+ tenantArtifact.command("put <owner/repo> <stage> <path>").description("upload a private file through a short-lived Hub grant; prints only an opaque artifact id").option("--json", "machine-readable output").action(async (repo, stage, path2, o) => {
41574
+ try {
41575
+ const receipt = await putTenantArtifact(repo, stage, path2, registryClientDeps(await loadConfig()));
41576
+ printLine(o.json ? JSON.stringify(receipt, null, 2) : `tenant artifact ${receipt.artifactId} ready for ${repo} ${stage} until ${receipt.expiresAt}`);
41577
+ } catch (e) {
41578
+ return failGraceful(e.message);
41579
+ }
41580
+ });
41049
41581
  tenant.command("reconcile <owner/repo> <stage>").description("re-render this tenant stage's generated box assets from registry truth; project-admin own dev/rc, master main; watches by default").option("--watch", "wait for the tenant-reconcile.yml run (default)").option("--no-watch", "return after dispatch; do not redeploy until the reconcile run succeeds").option("--json", "machine-readable output").action(async (repo, stage, o) => {
41050
41582
  try {
41051
41583
  const result = await runTenantReconcile(trainApplyDeps(), { repo, stage, watch: o.watch });
@@ -41242,7 +41774,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
41242
41774
  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`);
41243
41775
  if (o.secretsFile) {
41244
41776
  try {
41245
- vars.push(`secrets=${(0, import_node_fs47.readFileSync)(o.secretsFile, "utf8")}`);
41777
+ vars.push(`secrets=${(0, import_node_fs48.readFileSync)(o.secretsFile, "utf8")}`);
41246
41778
  } catch (e) {
41247
41779
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
41248
41780
  }
@@ -41568,7 +42100,7 @@ withExamples(mutating(
41568
42100
  try {
41569
42101
  title = await resolveIssueTitle(
41570
42102
  { title: opts.title, titleFile: opts.titleFile },
41571
- { readFile: import_promises11.readFile, readStdin }
42103
+ { readFile: import_promises12.readFile, readStdin }
41572
42104
  );
41573
42105
  } catch (e) {
41574
42106
  return fail(
@@ -41610,8 +42142,8 @@ withExamples(mutating(
41610
42142
  let surfaceFlagLabel;
41611
42143
  try {
41612
42144
  issueType = resolveCreateType(o.type, "issue create", o.label);
41613
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
41614
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
42145
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
42146
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
41615
42147
  if (o.idempotencyKey) body = appendIdempotencyMarker(body, o.idempotencyKey);
41616
42148
  priority = resolveCreatePriority(o.priority, "issue create");
41617
42149
  extraLabels = [...o.label ?? []];
@@ -41792,7 +42324,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
41792
42324
  }
41793
42325
  let body;
41794
42326
  try {
41795
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
42327
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
41796
42328
  } catch (e) {
41797
42329
  return fail(`issue comment: ${e.message}`);
41798
42330
  }
@@ -41852,8 +42384,8 @@ program2.command("report").description("file a friction report on the Hub board
41852
42384
  let title;
41853
42385
  const sourceRepo = o.repo ?? await resolveRepo(void 0);
41854
42386
  try {
41855
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
41856
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
42387
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
42388
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
41857
42389
  priority = resolveCreatePriority(o.priority, "report");
41858
42390
  if (!ISSUE_TYPES.includes(o.type)) {
41859
42391
  throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
@@ -41907,8 +42439,8 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
41907
42439
  let args;
41908
42440
  try {
41909
42441
  skill = assertSkillName(o.skill);
41910
- rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
41911
- const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
42442
+ rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
42443
+ const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
41912
42444
  title = buildSkillLessonTitle(skill, rawTitle);
41913
42445
  priority = resolveCreatePriority(o.priority, "skill-lesson");
41914
42446
  body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
@@ -41967,8 +42499,8 @@ withExamples(pr.command("create").description("create a PR and print {number,url
41967
42499
  let body;
41968
42500
  let title;
41969
42501
  try {
41970
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises11.readFile, readStdin });
41971
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
42502
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises12.readFile, readStdin });
42503
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises12.readFile, readStdin });
41972
42504
  } catch (e) {
41973
42505
  return fail(`pr create: ${e.message}`, e instanceof TextArgError ? { code: e.code, offending_flag: e.offendingFlag } : void 0);
41974
42506
  }
@@ -42010,11 +42542,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
42010
42542
  }
42011
42543
  });
42012
42544
  async function listCiWorkflowPaths(cwd = process.cwd()) {
42013
- const wfDir = (0, import_node_path44.join)(cwd, ".github", "workflows");
42014
- if (!(0, import_node_fs47.existsSync)(wfDir)) return [];
42015
- return (0, import_node_fs47.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
42545
+ const wfDir = (0, import_node_path46.join)(cwd, ".github", "workflows");
42546
+ if (!(0, import_node_fs48.existsSync)(wfDir)) return [];
42547
+ return (0, import_node_fs48.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
42016
42548
  try {
42017
- return workflowReportsPrChecks((0, import_node_fs47.readFileSync)((0, import_node_path44.join)(wfDir, name), "utf8"));
42549
+ return workflowReportsPrChecks((0, import_node_fs48.readFileSync)((0, import_node_path46.join)(wfDir, name), "utf8"));
42018
42550
  } catch {
42019
42551
  return true;
42020
42552
  }
@@ -42066,16 +42598,16 @@ function ciAuditDeps() {
42066
42598
  // gate re-seed step is skipped gracefully rather than failing mid-run.
42067
42599
  readSeedFile: (path2) => {
42068
42600
  if (!root) return null;
42069
- const fullPath = (0, import_node_path44.join)(root, path2);
42070
- return (0, import_node_fs47.existsSync)(fullPath) ? (0, import_node_fs47.readFileSync)(fullPath, "utf8") : null;
42601
+ const fullPath = (0, import_node_path46.join)(root, path2);
42602
+ return (0, import_node_fs48.existsSync)(fullPath) ? (0, import_node_fs48.readFileSync)(fullPath, "utf8") : null;
42071
42603
  }
42072
42604
  };
42073
42605
  }
42074
42606
  function hubRoot() {
42075
- const fromPkg = (0, import_node_path44.join)(__dirname, "..", "..");
42607
+ const fromPkg = (0, import_node_path46.join)(__dirname, "..", "..");
42076
42608
  const marker = "skills/bootstrap/seeds/manifest.json";
42077
- if ((0, import_node_fs47.existsSync)((0, import_node_path44.join)(fromPkg, marker))) return fromPkg;
42078
- if ((0, import_node_fs47.existsSync)((0, import_node_path44.join)(process.cwd(), marker))) return process.cwd();
42609
+ if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(fromPkg, marker))) return fromPkg;
42610
+ if ((0, import_node_fs48.existsSync)((0, import_node_path46.join)(process.cwd(), marker))) return process.cwd();
42079
42611
  return null;
42080
42612
  }
42081
42613
  async function waitLoopCorePool(label) {
@@ -42124,7 +42656,7 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
42124
42656
  // reading as "your tests failed". One call per failing run, only at the verdict.
42125
42657
  diagnoseFailure: () => waitLoopDiagnosis("pr checks-wait", number, repo),
42126
42658
  baseBranch,
42127
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
42659
+ sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
42128
42660
  log: (message) => console.warn(message),
42129
42661
  timeoutMs,
42130
42662
  // Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
@@ -42204,7 +42736,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
42204
42736
  // then read its own wall-clock kill as a broken diff.
42205
42737
  diagnoseFailure: () => waitLoopDiagnosis("pr land", prNumber, repo),
42206
42738
  baseBranch: "development",
42207
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
42739
+ sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
42208
42740
  log: (message) => console.warn(message),
42209
42741
  // `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
42210
42742
  // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
@@ -42247,7 +42779,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
42247
42779
  } else {
42248
42780
  lastFailure = void 0;
42249
42781
  }
42250
- await new Promise((resolve5) => setTimeout(resolve5, PR_LAND_POLL_MS));
42782
+ await new Promise((resolve6) => setTimeout(resolve6, PR_LAND_POLL_MS));
42251
42783
  }
42252
42784
  if (lastFailure) {
42253
42785
  throw new Error(
@@ -42347,7 +42879,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42347
42879
  pollRateLimit: () => waitLoopCorePool("pr merge --wait"),
42348
42880
  diagnoseFailure: () => waitLoopDiagnosis("pr merge --wait", number, repo),
42349
42881
  baseBranch,
42350
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
42882
+ sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
42351
42883
  log: (message) => console.warn(message),
42352
42884
  progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
42353
42885
  });
@@ -42383,7 +42915,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42383
42915
  }
42384
42916
  if (!repoForPostCleanup) throw e;
42385
42917
  console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
42386
- const commitMessage = bodyFile ? (0, import_node_fs47.readFileSync)(bodyFile, "utf8") : void 0;
42918
+ const commitMessage = bodyFile ? (0, import_node_fs48.readFileSync)(bodyFile, "utf8") : void 0;
42387
42919
  await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
42388
42920
  body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
42389
42921
  timeoutMs: GH_MUTATION_TIMEOUT_MS
@@ -42479,7 +43011,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42479
43011
  localCleanup = await cleanupPrMergeLocalBranch(headRef, {
42480
43012
  beforeWorktrees,
42481
43013
  startingPath,
42482
- pathExists: (p) => (0, import_node_fs47.existsSync)(p),
43014
+ pathExists: (p) => (0, import_node_fs48.existsSync)(p),
42483
43015
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
42484
43016
  teardownWorktreeStage,
42485
43017
  deferredStore,
@@ -42580,8 +43112,8 @@ function trainApplyDeps() {
42580
43112
  // Hub-App-authority dispatch of the central tenant-control.yml (#1717) — the Hub fires the
42581
43113
  // workflow_dispatch with its App token. Never throws for an expected rejection: it returns the dispatch
42582
43114
  // outcome so runTenantControl can map a 5xx (transport-failed, retryable) vs a 4xx (rejected) vs ok.
42583
- dispatchTenantControl: async ({ repo, stage, action, lines }) => {
42584
- const res = await tenantControl({ repo, stage, action, ...lines != null ? { lines } : {} }, registryClientDeps(await loadConfig()));
43115
+ dispatchTenantControl: async ({ repo, stage, action, lines, task, artifact }) => {
43116
+ const res = await tenantControl({ repo, stage, action, ...lines != null ? { lines } : {}, ...task ? { task } : {}, ...artifact ? { artifact } : {} }, registryClientDeps(await loadConfig()));
42585
43117
  if (res.ok) return { ok: true };
42586
43118
  const body = res.body;
42587
43119
  return { ok: false, category: body?.category, error: body?.error ?? res.error };
@@ -42605,8 +43137,8 @@ function trainApplyDeps() {
42605
43137
  // Slack release announcement (#883): Hub-only + best-effort inside announceRelease itself.
42606
43138
  announce: (args) => announceRelease({
42607
43139
  run: async (file, cmdArgs) => (await execFileP2(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
42608
- readFile: (path2) => (0, import_promises11.readFile)(path2, "utf8"),
42609
- removeFile: (path2) => (0, import_promises11.unlink)(path2)
43140
+ readFile: (path2) => (0, import_promises12.readFile)(path2, "utf8"),
43141
+ removeFile: (path2) => (0, import_promises12.unlink)(path2)
42610
43142
  }, args),
42611
43143
  // #4713 (I/O-boundary census): `null` used to mean BOTH "this project configures no edge domains"
42612
43144
  // (a real answer) and "the registry read missed" — so a release verdict printed an environments block
@@ -42843,7 +43375,7 @@ for (const commandName of ["rcand", "release"]) {
42843
43375
  }
42844
43376
  let summaryLines;
42845
43377
  try {
42846
- summaryLines = summaryFileLines(await (0, import_promises11.readFile)(o.announceSummaryFile, "utf8"));
43378
+ summaryLines = summaryFileLines(await (0, import_promises12.readFile)(o.announceSummaryFile, "utf8"));
42847
43379
  } catch (e) {
42848
43380
  return fail(`release: could not read --announce-summary-file ${o.announceSummaryFile}: ${e.message}`);
42849
43381
  }
@@ -43083,12 +43615,12 @@ access.command("audit").description("audit collaborator roles + train-branch pus
43083
43615
  targets = resolution.targets;
43084
43616
  }
43085
43617
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
43086
- const fileMatrix = (0, import_node_fs47.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs47.readFileSync)("access-matrix.json", "utf8")) : {};
43618
+ const fileMatrix = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
43087
43619
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
43088
43620
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
43089
- const fileContracts = (0, import_node_fs47.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs47.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
43621
+ const fileContracts = (0, import_node_fs48.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs48.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
43090
43622
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
43091
- const sanctioned = (0, import_node_fs47.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs47.readFileSync)("access-matrix.json", "utf8")) : {};
43623
+ const sanctioned = (0, import_node_fs48.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs48.readFileSync)("access-matrix.json", "utf8")) : {};
43092
43624
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
43093
43625
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
43094
43626
  if (!report.ok) process.exitCode = 1;
@@ -43126,16 +43658,16 @@ function directoryBytes(path2) {
43126
43658
  let total = 0;
43127
43659
  let entries;
43128
43660
  try {
43129
- entries = (0, import_node_fs47.readdirSync)(path2, { withFileTypes: true });
43661
+ entries = (0, import_node_fs48.readdirSync)(path2, { withFileTypes: true });
43130
43662
  } catch {
43131
43663
  return 0;
43132
43664
  }
43133
43665
  for (const entry of entries) {
43134
- const child2 = (0, import_node_path44.join)(path2, entry.name);
43666
+ const child2 = (0, import_node_path46.join)(path2, entry.name);
43135
43667
  if (entry.isDirectory()) total += directoryBytes(child2);
43136
43668
  else {
43137
43669
  try {
43138
- total += (0, import_node_fs47.statSync)(child2).size;
43670
+ total += (0, import_node_fs48.statSync)(child2).size;
43139
43671
  } catch {
43140
43672
  }
43141
43673
  }
@@ -43143,25 +43675,25 @@ function directoryBytes(path2) {
43143
43675
  return total;
43144
43676
  }
43145
43677
  function listDirEntries(dir) {
43146
- return (0, import_node_fs47.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
43678
+ return (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
43147
43679
  }
43148
43680
  function readInstalledPluginRefs(configRoot) {
43149
43681
  const p = installedPluginsPathForConfig(configRoot);
43150
- if (!(0, import_node_fs47.existsSync)(p)) return [];
43682
+ if (!(0, import_node_fs48.existsSync)(p)) return [];
43151
43683
  try {
43152
- return installedPluginPaths((0, import_node_fs47.readFileSync)(p, "utf8"));
43684
+ return installedPluginPaths((0, import_node_fs48.readFileSync)(p, "utf8"));
43153
43685
  } catch {
43154
43686
  return null;
43155
43687
  }
43156
43688
  }
43157
43689
  function pluginCacheFsDeps(configRoot, dirBytes) {
43158
43690
  return {
43159
- exists: (p) => (0, import_node_fs47.existsSync)(p),
43160
- listVersionDirs: (root) => (0, import_node_fs47.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
43691
+ exists: (p) => (0, import_node_fs48.existsSync)(p),
43692
+ listVersionDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
43161
43693
  dirBytes,
43162
- listStagingDirs: (root) => (0, import_node_fs47.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
43694
+ listStagingDirs: (root) => (0, import_node_fs48.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
43163
43695
  try {
43164
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path44.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs47.statSync)(p).mtimeMs) };
43696
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path46.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs48.statSync)(p).mtimeMs) };
43165
43697
  } catch {
43166
43698
  return { name: d.name, mtimeMs: Date.now() };
43167
43699
  }
@@ -43175,10 +43707,10 @@ function stagingApplyFsGuard(configRoot) {
43175
43707
  return {
43176
43708
  referencedPaths: () => readInstalledPluginRefs(configRoot),
43177
43709
  mtimeMs: (name) => {
43178
- const p = (0, import_node_path44.join)(stagingRoot, name);
43179
- if (!(0, import_node_fs47.existsSync)(p)) return null;
43710
+ const p = (0, import_node_path46.join)(stagingRoot, name);
43711
+ if (!(0, import_node_fs48.existsSync)(p)) return null;
43180
43712
  try {
43181
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs47.statSync)(q).mtimeMs);
43713
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs48.statSync)(q).mtimeMs);
43182
43714
  } catch {
43183
43715
  return null;
43184
43716
  }
@@ -43198,13 +43730,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
43198
43730
  return;
43199
43731
  }
43200
43732
  const plan = buildPluginCachePlan(
43201
- (0, import_node_os19.homedir)(),
43733
+ (0, import_node_os20.homedir)(),
43202
43734
  running,
43203
43735
  pluginCacheFsDeps(configRoot, directoryBytes),
43204
43736
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
43205
43737
  );
43206
43738
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
43207
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs47.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
43739
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs48.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
43208
43740
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
43209
43741
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
43210
43742
  else console.log(renderPluginCachePlan(plan, result));
@@ -43212,7 +43744,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
43212
43744
  });
43213
43745
  function readReleaseCatchupState(path2) {
43214
43746
  try {
43215
- const parsed = JSON.parse((0, import_node_fs47.readFileSync)(path2, "utf8"));
43747
+ const parsed = JSON.parse((0, import_node_fs48.readFileSync)(path2, "utf8"));
43216
43748
  return typeof parsed?.checkedAt === "number" ? parsed : void 0;
43217
43749
  } catch {
43218
43750
  return void 0;
@@ -43220,8 +43752,8 @@ function readReleaseCatchupState(path2) {
43220
43752
  }
43221
43753
  function writeReleaseCatchupState(path2, state) {
43222
43754
  try {
43223
- (0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(path2), { recursive: true });
43224
- (0, import_node_fs47.writeFileSync)(path2, `${JSON.stringify(state)}
43755
+ (0, import_node_fs48.mkdirSync)((0, import_node_path46.dirname)(path2), { recursive: true });
43756
+ (0, import_node_fs48.writeFileSync)(path2, `${JSON.stringify(state)}
43225
43757
  `);
43226
43758
  } catch {
43227
43759
  }
@@ -43229,7 +43761,7 @@ function writeReleaseCatchupState(path2, state) {
43229
43761
  program2.command("plugin-release-catchup").description("install a newer released MMI plugin clone non-destructively and re-point the Pi registration (#4297); TTL-gated no-op when current").option("--force", "skip the 24h TTL (acceptance proof / manual run)").option("--quiet", "print only failures (the detached session-start lane)").option("--json", "machine-readable output").action(async (o) => {
43230
43762
  const outcome = await withEnvHealLock(
43231
43763
  "plugin release catch-up",
43232
- () => runReleaseCatchup((0, import_node_os19.homedir)(), process.env, {
43764
+ () => runReleaseCatchup((0, import_node_os20.homedir)(), process.env, {
43233
43765
  fetchReleased: fetchNpmReleasedVersion,
43234
43766
  runClaude: (args, step) => runPluginCli("claude", args, (msg) => {
43235
43767
  if (!o.quiet && !o.json) console.log(msg);
@@ -43245,7 +43777,7 @@ program2.command("plugin-release-catchup").description("install a newer released
43245
43777
  },
43246
43778
  readState: readReleaseCatchupState,
43247
43779
  writeState: writeReleaseCatchupState,
43248
- healRegistration: defaultRegistrationHeal((0, import_node_os19.homedir)(), process.env),
43780
+ healRegistration: defaultRegistrationHeal((0, import_node_os20.homedir)(), process.env),
43249
43781
  now: () => Date.now()
43250
43782
  }, { force: o.force })
43251
43783
  );
@@ -43313,7 +43845,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
43313
43845
  spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
43314
43846
  bannerIo.log(worktreeBanner);
43315
43847
  }
43316
- if (shouldSpawnReleaseCatchup((0, import_node_os19.homedir)(), process.env, readReleaseCatchupState)) {
43848
+ if (shouldSpawnReleaseCatchup((0, import_node_os20.homedir)(), process.env, readReleaseCatchupState)) {
43317
43849
  spawnDetachedSelf(["plugin", "release-catchup", "--quiet"], { spawn: import_node_child_process20.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
43318
43850
  }
43319
43851
  if (isLinkedWorktree(process.cwd())) {