@mutmutco/cli 2.61.1 → 2.62.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 (3) hide show
  1. package/README.md +0 -1
  2. package/dist/main.cjs +371 -637
  3. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3408,7 +3408,7 @@ var program = new Command();
3408
3408
 
3409
3409
  // src/index.ts
3410
3410
  var import_promises8 = require("node:fs/promises");
3411
- var import_node_fs26 = require("node:fs");
3411
+ var import_node_fs25 = require("node:fs");
3412
3412
 
3413
3413
  // src/rules-sync.ts
3414
3414
  function normalizeEol(s) {
@@ -3471,7 +3471,7 @@ function purgeSpine(deps, files = SPINE_FILES) {
3471
3471
  }
3472
3472
 
3473
3473
  // src/index.ts
3474
- var import_node_child_process14 = require("node:child_process");
3474
+ var import_node_child_process13 = require("node:child_process");
3475
3475
 
3476
3476
  // src/cli-shared.ts
3477
3477
  var import_promises = require("node:fs/promises");
@@ -6919,7 +6919,6 @@ function formatHandoffLine(item) {
6919
6919
 
6920
6920
  // src/handoff-commands.ts
6921
6921
  var FOREGROUND_FETCH = { attempts: 2, timeoutMs: 8e3 };
6922
- var SESSION_START_FETCH = { attempts: 1, timeoutMs: 3e3 };
6923
6922
  function timestampRank2(value) {
6924
6923
  const ms = Date.parse(value ?? "");
6925
6924
  return Number.isFinite(ms) ? ms : Number.NEGATIVE_INFINITY;
@@ -7082,20 +7081,6 @@ async function runHandoffList(opts, io = consoleIo) {
7082
7081
  }
7083
7082
  return handoffs;
7084
7083
  }
7085
- async function runHandoffOffer(io = consoleIo, opts = {}) {
7086
- if (!await requireContinuityAccess("handoff", { quiet: opts.fast }, io)) return;
7087
- const retry = opts.fast ? SESSION_START_FETCH : FOREGROUND_FETCH;
7088
- let handoffs;
7089
- try {
7090
- ({ handoffs } = await collectScopedHandoffs({ retry }));
7091
- } catch {
7092
- return;
7093
- }
7094
- if (!handoffs.length) return;
7095
- io.log("Open handoffs:");
7096
- for (const h of handoffs) io.log(`- ${formatHandoffLine(h)}`);
7097
- io.log("Use `mmi-cli handoff accept <key>` to claim, `mmi-cli handoff cancel <key>` to close, or decline in chat to leave it open.");
7098
- }
7099
7084
  async function closeSourceHandoff(key, state, opts = {}, io = consoleIo) {
7100
7085
  if (!await requireContinuityAccess("handoff", {}, io)) return;
7101
7086
  const current = await sagaKey(await loadConfig(), resolveSessionId());
@@ -8631,21 +8616,10 @@ async function runSessionStart(parallel, sequential, io) {
8631
8616
  for (const lines of buffered) flush(lines, io);
8632
8617
  for (const step of sequential) flush(await runBufferedStep(step), io);
8633
8618
  }
8634
- function buildSessionStartPlan(verbs, opts = {}) {
8635
- const continuityEnabled = opts.continuityEnabled ?? true;
8636
- const continuitySteps = continuityEnabled ? [
8637
- { name: "saga show", run: verbs.sagaShow },
8638
- { name: "handoff offer", run: verbs.handoffOffer },
8639
- // northstar context (#1228): curated plan cards when relevance is high-confidence; silent otherwise.
8640
- { name: "northstar context", run: verbs.northstarContext },
8641
- { name: "saga health", run: verbs.sagaHealth }
8642
- ] : [];
8619
+ function buildSessionStartPlan(verbs) {
8643
8620
  return {
8644
8621
  parallel: [
8645
8622
  { name: "rules purge", run: verbs.rulesPurge },
8646
- ...continuitySteps.slice(0, 2),
8647
- { name: "coop pending", run: verbs.coopPending },
8648
- ...continuitySteps.slice(2),
8649
8623
  // whoami (#879): the resolved human lands in the banner so agents act --for them without asking.
8650
8624
  // Identity reads are memoized process-wide, so this adds no extra gh/Hub round-trip.
8651
8625
  { name: "whoami", run: verbs.whoami },
@@ -8675,31 +8649,6 @@ function isInsideRepoSubdir(cwd, exists = import_node_fs13.existsSync) {
8675
8649
  dir = parent;
8676
8650
  }
8677
8651
  }
8678
- function planStoreLines(cwd) {
8679
- const mdFiles = (dir, minSize = 0) => {
8680
- const p = (0, import_node_path12.join)(cwd, dir);
8681
- if (!(0, import_node_fs13.existsSync)(p)) return [];
8682
- try {
8683
- return (0, import_node_fs13.readdirSync)(p).filter((f) => f.toLowerCase().endsWith(".md")).filter((f) => {
8684
- try {
8685
- return (0, import_node_fs13.statSync)((0, import_node_path12.join)(p, f)).size >= minSize;
8686
- } catch {
8687
- return false;
8688
- }
8689
- }).map((f) => `${dir}/${f}`);
8690
- } catch {
8691
- return [];
8692
- }
8693
- };
8694
- const tmpPlans = mdFiles("tmp", 1024);
8695
- const localPlans = mdFiles("plans");
8696
- const out = [];
8697
- if (tmpPlans.length)
8698
- out.push(`[plan-store] WARNING: SSOT-shaped file(s) in tmp/ \u2014 move to plans/<slug>.md (auto-saves to the store; never commit): ${tmpPlans.join(", ")}`);
8699
- if (localPlans.length)
8700
- out.push(`[plan-store] plans/ is gitignored on purpose \u2014 ${localPlans.length} local plan(s) auto-save to the North Star store on write; never git add or commit plans/ (use northstar push --wait only when you need immediate server confirmation).`);
8701
- return out;
8702
- }
8703
8652
  function scratchGcLines(cwd, env = process.env, now = Date.now()) {
8704
8653
  if (env.MMI_NO_AUTO_GC) return [];
8705
8654
  try {
@@ -8713,20 +8662,10 @@ function scratchGcLines(cwd, env = process.env, now = Date.now()) {
8713
8662
  return [];
8714
8663
  }
8715
8664
  }
8716
- function northstarPointer(injected = false) {
8717
- if (injected) {
8718
- return "North Stars: `mmi-cli northstar relevant` for more matches; `northstar pull <slug>` for the full SSOT.";
8719
- }
8720
- return "North Stars: run `mmi-cli northstar relevant` to load plans relevant to your task (`northstar list` for all).";
8721
- }
8722
- function sessionStartContinuityLines(opts) {
8723
- if (!opts.continuityEnabled) return [];
8724
- return [northstarPointer(opts.northstarInjected), ...planStoreLines(opts.cwd)];
8725
- }
8726
8665
 
8727
8666
  // src/coop-commands.ts
8728
8667
  var FOREGROUND_FETCH2 = { attempts: 2, timeoutMs: 15e3 };
8729
- var SESSION_START_FETCH2 = { attempts: 1, timeoutMs: 3e3 };
8668
+ var SESSION_START_FETCH = { attempts: 1, timeoutMs: 3e3 };
8730
8669
  var COOP_WAIT_DELAYS_MS = [6e4, 12e4, 18e4, 3e5, 6e5, 18e5];
8731
8670
  async function hubBase() {
8732
8671
  const cfg = await loadConfig();
@@ -8880,7 +8819,7 @@ async function runCoopWait(coopId, io, opts = {}) {
8880
8819
  console.log(JSON.stringify({ coopId, status: "timeout-open" }));
8881
8820
  }
8882
8821
  }
8883
- async function fetchCoopPending(retry = SESSION_START_FETCH2) {
8822
+ async function fetchCoopPending(retry = SESSION_START_FETCH) {
8884
8823
  const body = await getJson("/coop/pending", retry);
8885
8824
  return body.pending ?? [];
8886
8825
  }
@@ -10438,140 +10377,6 @@ State: ${payload2.statePath}
10438
10377
  return overlord;
10439
10378
  }
10440
10379
 
10441
- // src/throttle-commands.ts
10442
- var import_node_child_process8 = require("node:child_process");
10443
- var import_node_fs16 = require("node:fs");
10444
- var import_node_path14 = require("node:path");
10445
- var THROTTLE_TRACE_REL = (0, import_node_path14.join)(".mmi", "throttle", "trace.jsonl");
10446
- function resolveRepoGitRoot(cwd = process.cwd()) {
10447
- try {
10448
- const root = (0, import_node_child_process8.execFileSync)("git", ["-C", cwd, "rev-parse", "--show-toplevel"], {
10449
- encoding: "utf8",
10450
- timeout: 5e3
10451
- }).trim();
10452
- return root || cwd;
10453
- } catch {
10454
- return cwd;
10455
- }
10456
- }
10457
- function resolveThrottleTracePath(cwd = process.cwd()) {
10458
- return (0, import_node_path14.join)(resolveRepoGitRoot(cwd), THROTTLE_TRACE_REL);
10459
- }
10460
- function resolveModeFromEnv() {
10461
- const v = String(process.env.MMI_THROTTLE_MODE ?? "block").trim().toLowerCase();
10462
- if (v === "observe") return "observe";
10463
- return "block";
10464
- }
10465
- function parseTraceLines(raw) {
10466
- const out = [];
10467
- for (const line of raw.split(/\r?\n/)) {
10468
- const t = line.trim();
10469
- if (!t) continue;
10470
- try {
10471
- const o = JSON.parse(t);
10472
- if (o && typeof o === "object") out.push(o);
10473
- } catch {
10474
- }
10475
- }
10476
- return out;
10477
- }
10478
- function commandSafetyClass(reasonId) {
10479
- if (reasonId === "shell_dialect_redirect") return "dialect/redirect mismatch";
10480
- if (reasonId === "shell_log_dump") return "unbounded log/text dump";
10481
- if (reasonId.startsWith("shell_")) return "shell command safety";
10482
- return void 0;
10483
- }
10484
- function summarizeTrace(entries) {
10485
- let denials = 0;
10486
- let readBytesWouldBlock = 0;
10487
- const byTool = {};
10488
- const byReason = {};
10489
- const bySurface = {};
10490
- const byMode = {};
10491
- const commandSafetyBySurface = {};
10492
- for (const e of entries) {
10493
- denials += 1;
10494
- const tool = e.tool ?? "unknown";
10495
- byTool[tool] = (byTool[tool] ?? 0) + 1;
10496
- const reason = e.reasonId ?? "unknown";
10497
- byReason[reason] = (byReason[reason] ?? 0) + 1;
10498
- const surface = e.surface ?? "unknown";
10499
- bySurface[surface] = (bySurface[surface] ?? 0) + 1;
10500
- const mode = e.mode ?? "unknown";
10501
- byMode[mode] = (byMode[mode] ?? 0) + 1;
10502
- const safetyClass = commandSafetyClass(reason);
10503
- if (safetyClass) {
10504
- commandSafetyBySurface[surface] = commandSafetyBySurface[surface] ?? {};
10505
- commandSafetyBySurface[surface][safetyClass] = (commandSafetyBySurface[surface][safetyClass] ?? 0) + 1;
10506
- }
10507
- if (reason === "read_unbounded_large") {
10508
- readBytesWouldBlock += Number(e.fileBytes) || 0;
10509
- }
10510
- }
10511
- return { denials, readBytesWouldBlock, byTool, byReason, bySurface, byMode, commandSafetyBySurface };
10512
- }
10513
- function runThrottleReport(io, tracePath = resolveThrottleTracePath()) {
10514
- const mode = resolveModeFromEnv();
10515
- if (!(0, import_node_fs16.existsSync)(tracePath)) {
10516
- io.log(`Throttle: no trace at ${tracePath} (gates have not denied anything yet).`);
10517
- io.log(`Active mode: ${mode} (MMI_THROTTLE_MODE env; default block).`);
10518
- return 0;
10519
- }
10520
- let raw = "";
10521
- try {
10522
- raw = (0, import_node_fs16.readFileSync)(tracePath, "utf8");
10523
- } catch (e) {
10524
- io.err(`Throttle: could not read trace: ${e.message}`);
10525
- return 1;
10526
- }
10527
- const entries = parseTraceLines(raw);
10528
- const s = summarizeTrace(entries);
10529
- io.log("Throttle gate summary");
10530
- io.log(` mode (current env): ${mode}`);
10531
- io.log(` trace entries: ${s.denials}`);
10532
- io.log(` read bytes would-have-read (fs.stat only): ${s.readBytesWouldBlock}`);
10533
- io.log(" (Shell denials are not converted to a tokens-saved figure \u2014 counterfactual.)");
10534
- const modes = Object.entries(s.byMode).sort((a, b) => b[1] - a[1]);
10535
- if (modes.length) {
10536
- io.log(" by trace mode (at denial time):");
10537
- for (const [name, count] of modes) {
10538
- const label = name === "observe" ? "would-block (observe)" : name === "block" ? "denied (block)" : name;
10539
- io.log(` ${label}: ${count}`);
10540
- }
10541
- }
10542
- const tools = Object.entries(s.byTool).sort((a, b) => b[1] - a[1]);
10543
- if (tools.length) {
10544
- io.log(" by tool:");
10545
- for (const [name, count] of tools) io.log(` ${name}: ${count}`);
10546
- }
10547
- const reasons = Object.entries(s.byReason).sort((a, b) => b[1] - a[1]);
10548
- if (reasons.length) {
10549
- io.log(" by reason:");
10550
- for (const [name, count] of reasons) io.log(` ${name}: ${count}`);
10551
- }
10552
- const surfaces = Object.entries(s.bySurface).sort((a, b) => b[1] - a[1]);
10553
- if (surfaces.length) {
10554
- io.log(" by surface:");
10555
- for (const [name, count] of surfaces) io.log(` ${name}: ${count}`);
10556
- }
10557
- const safetySurfaces = Object.entries(s.commandSafetyBySurface).sort((a, b) => a[0].localeCompare(b[0]));
10558
- if (safetySurfaces.length) {
10559
- io.log(" command safety (quoting/dialect/shell) by surface:");
10560
- for (const [surface, classes] of safetySurfaces) {
10561
- const detail = Object.entries(classes).sort((a, b) => b[1] - a[1]).map(([cls, count]) => `${cls}: ${count}`).join(", ");
10562
- io.log(` ${surface}: ${detail}`);
10563
- }
10564
- }
10565
- return 0;
10566
- }
10567
- function registerThrottleCommands(program3) {
10568
- const throttle = program3.command("throttle").description("Scrooge v2 \u2014 tool-economy gate trace + report");
10569
- throttle.command("report").description("print gate denial stats from .mmi/throttle/trace.jsonl").action(() => {
10570
- const code = runThrottleReport({ log: (s) => console.log(s), err: (s) => console.error(s) });
10571
- process.exit(code);
10572
- });
10573
- }
10574
-
10575
10380
  // src/docs-sync.ts
10576
10381
  var SYNCED_DOCS = ["README.md", "architecture.md"];
10577
10382
  async function syncDocs(deps, docs2 = SYNCED_DOCS) {
@@ -10594,7 +10399,7 @@ async function syncDocs(deps, docs2 = SYNCED_DOCS) {
10594
10399
  }
10595
10400
 
10596
10401
  // src/board.ts
10597
- var import_node_child_process9 = require("node:child_process");
10402
+ var import_node_child_process8 = require("node:child_process");
10598
10403
  var import_node_util6 = require("node:util");
10599
10404
 
10600
10405
  // src/board-priority.ts
@@ -10702,7 +10507,7 @@ async function filterDependencyBlockedClaimables(items, client, opts = {}) {
10702
10507
  var BOARD_STATUSES = ["Todo", "In Progress", "In Review", "Done"];
10703
10508
 
10704
10509
  // src/board.ts
10705
- var rawExecFileP3 = (0, import_node_util6.promisify)(import_node_child_process9.execFile);
10510
+ var rawExecFileP3 = (0, import_node_util6.promisify)(import_node_child_process8.execFile);
10706
10511
  var BOARD_GIT_TIMEOUT_MS = 1e4;
10707
10512
  var WRITE_PROBE_CONCURRENCY = 8;
10708
10513
  var CLAIM_CONCURRENCY = 5;
@@ -11531,16 +11336,16 @@ function ghError(e) {
11531
11336
  }
11532
11337
 
11533
11338
  // src/board-slice-cache.ts
11534
- var import_node_fs17 = require("node:fs");
11535
- var import_node_path15 = require("node:path");
11536
- var BOARD_SLICE_CACHE_FILE = (0, import_node_path15.join)(".mmi", "board-slice.json");
11339
+ var import_node_fs16 = require("node:fs");
11340
+ var import_node_path14 = require("node:path");
11341
+ var BOARD_SLICE_CACHE_FILE = (0, import_node_path14.join)(".mmi", "board-slice.json");
11537
11342
  var BOARD_SLICE_CACHE_TTL_MS = 10 * 60 * 1e3;
11538
11343
  function boardSliceCachePath(cwd) {
11539
- return (0, import_node_path15.join)(cwd, BOARD_SLICE_CACHE_FILE);
11344
+ return (0, import_node_path14.join)(cwd, BOARD_SLICE_CACHE_FILE);
11540
11345
  }
11541
11346
  function readCachedBoardSlice(cwd) {
11542
11347
  try {
11543
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(boardSliceCachePath(cwd), "utf8"));
11348
+ const parsed = JSON.parse((0, import_node_fs16.readFileSync)(boardSliceCachePath(cwd), "utf8"));
11544
11349
  if (typeof parsed.ts !== "number") return null;
11545
11350
  return { block: typeof parsed.block === "string" ? parsed.block : null, ts: parsed.ts };
11546
11351
  } catch {
@@ -11551,12 +11356,12 @@ function writeCachedBoardSlice(cwd, slice) {
11551
11356
  const path2 = boardSliceCachePath(cwd);
11552
11357
  const tmp = `${path2}.${process.pid}.tmp`;
11553
11358
  try {
11554
- (0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(path2), { recursive: true });
11555
- (0, import_node_fs17.writeFileSync)(tmp, JSON.stringify(slice));
11556
- (0, import_node_fs17.renameSync)(tmp, path2);
11359
+ (0, import_node_fs16.mkdirSync)((0, import_node_path14.dirname)(path2), { recursive: true });
11360
+ (0, import_node_fs16.writeFileSync)(tmp, JSON.stringify(slice));
11361
+ (0, import_node_fs16.renameSync)(tmp, path2);
11557
11362
  } catch {
11558
11363
  try {
11559
- (0, import_node_fs17.rmSync)(tmp, { force: true });
11364
+ (0, import_node_fs16.rmSync)(tmp, { force: true });
11560
11365
  } catch {
11561
11366
  }
11562
11367
  }
@@ -11684,8 +11489,8 @@ async function refreshBoardSliceCache(deps) {
11684
11489
  }
11685
11490
 
11686
11491
  // src/worktree.ts
11687
- var import_node_fs18 = require("node:fs");
11688
- var import_node_path16 = require("node:path");
11492
+ var import_node_fs17 = require("node:fs");
11493
+ var import_node_path15 = require("node:path");
11689
11494
  var LOCAL_ONLY_FILES = [".claude/settings.local.json"];
11690
11495
  var PKG = "package.json";
11691
11496
  var LOCKFILE = "package-lock.json";
@@ -11693,21 +11498,21 @@ var NODE_MODULES = "node_modules";
11693
11498
  var realFsProbe = {
11694
11499
  isDir: (p) => {
11695
11500
  try {
11696
- return (0, import_node_fs18.statSync)(p).isDirectory();
11501
+ return (0, import_node_fs17.statSync)(p).isDirectory();
11697
11502
  } catch {
11698
11503
  return false;
11699
11504
  }
11700
11505
  },
11701
11506
  isFile: (p) => {
11702
11507
  try {
11703
- return (0, import_node_fs18.statSync)(p).isFile();
11508
+ return (0, import_node_fs17.statSync)(p).isFile();
11704
11509
  } catch {
11705
11510
  return false;
11706
11511
  }
11707
11512
  },
11708
11513
  listDirs: (p) => {
11709
11514
  try {
11710
- return (0, import_node_fs18.readdirSync)(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
11515
+ return (0, import_node_fs17.readdirSync)(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
11711
11516
  } catch {
11712
11517
  return [];
11713
11518
  }
@@ -11715,12 +11520,12 @@ var realFsProbe = {
11715
11520
  };
11716
11521
  function scanInstallDirs(root, fs2 = realFsProbe) {
11717
11522
  const factsFor = (dir) => {
11718
- const abs = dir ? (0, import_node_path16.join)(root, dir) : root;
11523
+ const abs = dir ? (0, import_node_path15.join)(root, dir) : root;
11719
11524
  return {
11720
11525
  dir,
11721
- hasPackageJson: fs2.isFile((0, import_node_path16.join)(abs, PKG)),
11722
- hasLockfile: fs2.isFile((0, import_node_path16.join)(abs, LOCKFILE)),
11723
- hasNodeModules: fs2.isDir((0, import_node_path16.join)(abs, NODE_MODULES))
11526
+ hasPackageJson: fs2.isFile((0, import_node_path15.join)(abs, PKG)),
11527
+ hasLockfile: fs2.isFile((0, import_node_path15.join)(abs, LOCKFILE)),
11528
+ hasNodeModules: fs2.isDir((0, import_node_path15.join)(abs, NODE_MODULES))
11724
11529
  };
11725
11530
  };
11726
11531
  const children = fs2.listDirs(root).filter((name) => name !== NODE_MODULES && name !== ".git");
@@ -11730,7 +11535,7 @@ function npmInstallTargets(dirs) {
11730
11535
  return dirs.filter((d) => d.hasPackageJson && !d.hasNodeModules).map((d) => ({ dir: d.dir, command: d.hasLockfile ? "npm ci" : "npm install" }));
11731
11536
  }
11732
11537
  function isLinkedWorktree(root, fs2 = realFsProbe) {
11733
- return fs2.isFile((0, import_node_path16.join)(root, ".git"));
11538
+ return fs2.isFile((0, import_node_path15.join)(root, ".git"));
11734
11539
  }
11735
11540
  function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
11736
11541
  if (!isLinkedWorktree(root, fs2)) return null;
@@ -11740,8 +11545,8 @@ function worktreeAutoProvisionBanner(root, fs2 = realFsProbe) {
11740
11545
  return `[worktree] provisioning tooling in the background (deps in ${where} + local config) \u2014 \`mmi-cli worktree setup\` to redo`;
11741
11546
  }
11742
11547
  function defaultCopyFile(from, to) {
11743
- (0, import_node_fs18.mkdirSync)((0, import_node_path16.dirname)(to), { recursive: true });
11744
- (0, import_node_fs18.copyFileSync)(from, to);
11548
+ (0, import_node_fs17.mkdirSync)((0, import_node_path15.dirname)(to), { recursive: true });
11549
+ (0, import_node_fs17.copyFileSync)(from, to);
11745
11550
  }
11746
11551
  async function provisionWorktree(worktreeRoot, deps) {
11747
11552
  const fs2 = deps.fs ?? realFsProbe;
@@ -11753,7 +11558,7 @@ async function provisionWorktree(worktreeRoot, deps) {
11753
11558
  const skippedInstall = allDirs.filter((d) => d.hasPackageJson && d.hasNodeModules).map((d) => d.dir);
11754
11559
  const installed = [];
11755
11560
  for (const target of targets) {
11756
- const cwd = target.dir ? (0, import_node_path16.join)(worktreeRoot, target.dir) : worktreeRoot;
11561
+ const cwd = target.dir ? (0, import_node_path15.join)(worktreeRoot, target.dir) : worktreeRoot;
11757
11562
  log(`installing deps: ${target.command} in ${target.dir || "."}`);
11758
11563
  await deps.runInstall(target.command, cwd);
11759
11564
  installed.push(target);
@@ -11762,7 +11567,7 @@ async function provisionWorktree(worktreeRoot, deps) {
11762
11567
  const copySkipped = [];
11763
11568
  const primary = await deps.primaryCheckout();
11764
11569
  for (const rel of LOCAL_ONLY_FILES) {
11765
- const dest = (0, import_node_path16.join)(worktreeRoot, rel);
11570
+ const dest = (0, import_node_path15.join)(worktreeRoot, rel);
11766
11571
  if (fs2.isFile(dest)) {
11767
11572
  copySkipped.push({ file: rel, reason: "already-present" });
11768
11573
  continue;
@@ -11771,11 +11576,11 @@ async function provisionWorktree(worktreeRoot, deps) {
11771
11576
  copySkipped.push({ file: rel, reason: "no-primary" });
11772
11577
  continue;
11773
11578
  }
11774
- if (!fs2.isFile((0, import_node_path16.join)(primary, rel))) {
11579
+ if (!fs2.isFile((0, import_node_path15.join)(primary, rel))) {
11775
11580
  copySkipped.push({ file: rel, reason: "absent-in-primary" });
11776
11581
  continue;
11777
11582
  }
11778
- copyFile((0, import_node_path16.join)(primary, rel), dest);
11583
+ copyFile((0, import_node_path15.join)(primary, rel), dest);
11779
11584
  copied.push(rel);
11780
11585
  log(`copied local config: ${rel}`);
11781
11586
  }
@@ -11783,7 +11588,7 @@ async function provisionWorktree(worktreeRoot, deps) {
11783
11588
  }
11784
11589
  function defaultWorktreePath(repoRoot, branch) {
11785
11590
  const safe = branch.replace(/[/\\]+/g, "-");
11786
- return (0, import_node_path16.join)((0, import_node_path16.dirname)(repoRoot), "mmi-worktrees", safe);
11591
+ return (0, import_node_path15.join)((0, import_node_path15.dirname)(repoRoot), "mmi-worktrees", safe);
11787
11592
  }
11788
11593
  function resolveWorktreeBase(from, remote) {
11789
11594
  const remotePrefix = `${remote}/`;
@@ -11791,121 +11596,6 @@ function resolveWorktreeBase(from, remote) {
11791
11596
  return { base: from, fetchBranch };
11792
11597
  }
11793
11598
 
11794
- // src/northstar-context.ts
11795
- var SESSION_START_NORTHSTAR_TIMEOUT_MS = 3e3;
11796
- var NORTHSTAR_CONTEXT_MIN_SCORE = 3;
11797
- var NORTHSTAR_CONTEXT_MAX_CARDS = 2;
11798
- var NORTHSTAR_CONTEXT_CHAR_BUDGET = 900;
11799
- var NORTHSTAR_CONTEXT_FRAMING = [
11800
- "> NORTH STAR CONTEXT \u2014 SSOT plans likely relevant to this task/session. Use them to",
11801
- " orient direction and constraints. This is a PRIOR, not an instruction: the live",
11802
- " user/master instruction always wins. Do not narrate or cite this block; pull the",
11803
- " full plan with `mmi-cli northstar pull <slug>` when you need detail."
11804
- ].join("\n");
11805
- function compactText(text, max) {
11806
- const t = text.replace(/\s+/g, " ").trim();
11807
- if (t.length <= max) return t;
11808
- return `${t.slice(0, max - 1)}\u2026`;
11809
- }
11810
- function derivePlanIntent(content) {
11811
- const fromFm = frontmatterValue(content, "intent") ?? frontmatterValue(content, "summary");
11812
- if (fromFm?.trim()) return compactText(fromFm, 240);
11813
- const { body } = splitFrontmatter(content);
11814
- const lines = body.split(/\r?\n/);
11815
- const paragraph = [];
11816
- for (const raw of lines) {
11817
- const line = raw.trim();
11818
- if (!line) {
11819
- if (paragraph.length) break;
11820
- continue;
11821
- }
11822
- if (/^#+\s/.test(line)) continue;
11823
- if (/^[-*]\s/.test(line) || /^```/.test(line)) break;
11824
- paragraph.push(line.replace(/^#+\s*/, ""));
11825
- if (paragraph.length >= 3) break;
11826
- }
11827
- if (!paragraph.length) return void 0;
11828
- return compactText(paragraph.join(" "), 240);
11829
- }
11830
- function formatPlanContextCard(slug, title, intent) {
11831
- const head = title?.trim() ? ` \u2022 ${slug} \u2014 ${title.trim()}` : ` \u2022 ${slug}`;
11832
- if (!intent?.trim()) return head;
11833
- return `${head}
11834
- ${intent.trim()}`;
11835
- }
11836
- function isHighConfidenceMatch(r, signals) {
11837
- if (r.score >= NORTHSTAR_CONTEXT_MIN_SCORE) return true;
11838
- const branch = (signals.branch ?? "").toLowerCase();
11839
- const slug = r.plan.slug.toLowerCase();
11840
- return slug.length >= 5 && branch.includes(slug);
11841
- }
11842
- function selectHighConfidencePlans(ranked, signals, opts = {}) {
11843
- const max = opts.maxCards ?? NORTHSTAR_CONTEXT_MAX_CARDS;
11844
- const anchor = opts.anchorSlug?.trim().toLowerCase();
11845
- const picked = [];
11846
- if (anchor) {
11847
- const direct = ranked.find((r) => r.plan.slug.toLowerCase() === anchor);
11848
- if (direct) picked.push(direct);
11849
- }
11850
- for (const r of ranked) {
11851
- if (picked.length >= max) break;
11852
- if (picked.some((p) => p.plan.slug === r.plan.slug && p.plan.project === r.plan.project)) continue;
11853
- if (isHighConfidenceMatch(r, signals)) picked.push(r);
11854
- }
11855
- return picked.slice(0, max);
11856
- }
11857
- function buildNorthstarContextBlock(ranked, signals, readLocal, opts = {}) {
11858
- const selected = selectHighConfidencePlans(ranked, signals, opts);
11859
- if (!selected.length) return null;
11860
- const cards = [];
11861
- let used = 0;
11862
- for (const r of selected) {
11863
- const local = readLocal(r.plan.slug);
11864
- const intent = local ? derivePlanIntent(local) : void 0;
11865
- const card = formatPlanContextCard(r.plan.slug, r.plan.title, intent);
11866
- if (used + card.length > NORTHSTAR_CONTEXT_CHAR_BUDGET) break;
11867
- cards.push(card);
11868
- used += card.length;
11869
- }
11870
- if (!cards.length) return null;
11871
- return [NORTHSTAR_CONTEXT_FRAMING, ...cards].join("\n");
11872
- }
11873
- async function withTimeout2(promise, ms) {
11874
- let timer;
11875
- try {
11876
- return await Promise.race([
11877
- promise,
11878
- new Promise((_, reject) => {
11879
- timer = setTimeout(() => reject(new Error("northstar context timeout")), ms);
11880
- })
11881
- ]);
11882
- } finally {
11883
- if (timer) clearTimeout(timer);
11884
- }
11885
- }
11886
- async function runNorthstarContext(io, deps) {
11887
- try {
11888
- const injected = await withTimeout2(
11889
- (async () => {
11890
- const plans = await deps.loadPlans();
11891
- if (!plans.length) return false;
11892
- const signals = await deps.gatherSignals();
11893
- const ranked = rankPlansByRelevance(plans, signals);
11894
- const block = buildNorthstarContextBlock(ranked, signals, deps.readLocal, {
11895
- anchorSlug: signals.anchorSlug
11896
- });
11897
- if (!block) return false;
11898
- io.log(block);
11899
- return true;
11900
- })(),
11901
- deps.timeoutMs ?? SESSION_START_NORTHSTAR_TIMEOUT_MS
11902
- );
11903
- return injected;
11904
- } catch {
11905
- return false;
11906
- }
11907
- }
11908
-
11909
11599
  // src/whoami.ts
11910
11600
  async function resolveWhoami(deps) {
11911
11601
  let session;
@@ -11930,7 +11620,7 @@ function whoamiLine(report) {
11930
11620
  }
11931
11621
 
11932
11622
  // src/index.ts
11933
- var import_node_path23 = require("node:path");
11623
+ var import_node_path22 = require("node:path");
11934
11624
 
11935
11625
  // src/merge-ci-policy.ts
11936
11626
  function resolveMergeCiPolicy(input) {
@@ -13182,7 +12872,7 @@ async function resolveAutoAddBoardAttach(client, cfg, selector, priority, warn =
13182
12872
  // src/gh-create.ts
13183
12873
  var import_promises5 = require("node:fs/promises");
13184
12874
  var import_node_os3 = require("node:os");
13185
- var import_node_path17 = require("node:path");
12875
+ var import_node_path16 = require("node:path");
13186
12876
  var import_node_crypto6 = require("node:crypto");
13187
12877
  var ISSUE_TYPES = ["bug", "feature", "task"];
13188
12878
  var GH_MUTATION_TIMEOUT_MS = 12e4;
@@ -13223,7 +12913,7 @@ async function bodyArgsViaFile(args, deps = {}) {
13223
12913
  } };
13224
12914
  const write = deps.write ?? import_promises5.writeFile;
13225
12915
  const remove = deps.remove ?? import_promises5.unlink;
13226
- const file = (0, import_node_path17.join)(deps.dir ?? (0, import_node_os3.tmpdir)(), `mmi-gh-body-${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}.md`);
12916
+ const file = (0, import_node_path16.join)(deps.dir ?? (0, import_node_os3.tmpdir)(), `mmi-gh-body-${process.pid}-${(0, import_node_crypto6.randomBytes)(4).toString("hex")}.md`);
13227
12917
  await write(file, args[i + 1], "utf8");
13228
12918
  return {
13229
12919
  args: [...args.slice(0, i), "--body-file", file, ...args.slice(i + 2)],
@@ -13672,7 +13362,7 @@ ${buildReportBody(body, sourceRepo)}`;
13672
13362
 
13673
13363
  // src/skill-lesson.ts
13674
13364
  var SKILL_LESSON_LABEL = "skill-lesson";
13675
- var SKILL_NAMES = ["bootstrap", "browser-automation", "build", "coop", "grind", "handoff", "hotfix", "mmi", "overlord", "rcand", "release", "secrets", "stage"];
13365
+ var SKILL_NAMES = ["bootstrap", "browser-automation", "hotfix", "mmi", "rcand", "release", "secrets", "stage"];
13676
13366
  function assertSkillName(name) {
13677
13367
  const match = SKILL_NAMES.find((skill) => skill === name);
13678
13368
  if (!match) throw new Error(`unknown skill "${name}" \u2014 expected one of: ${SKILL_NAMES.join(", ")}`);
@@ -15470,12 +15160,12 @@ function renderVerifySecrets(body) {
15470
15160
  }
15471
15161
 
15472
15162
  // src/hotfix-coverage.ts
15473
- var import_node_child_process10 = require("node:child_process");
15163
+ var import_node_child_process9 = require("node:child_process");
15474
15164
  var CHERRY_TRAILER = /\(cherry picked from commit ([0-9a-f]{7,40})\)/g;
15475
15165
  function checkHotfixCoverage(options = {}) {
15476
15166
  const { cwd = process.cwd(), mainRef = "origin/main", rcRef = "origin/rc", manifestPaths = [] } = options;
15477
15167
  const ack = (options.ack ?? []).filter(Boolean);
15478
- const git = options.git ?? ((args, opts) => (0, import_node_child_process10.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
15168
+ const git = options.git ?? ((args, opts) => (0, import_node_child_process9.execFileSync)("git", args, { cwd, encoding: "utf8", input: opts?.input, stdio: ["pipe", "pipe", "pipe"] }));
15479
15169
  const revList = (range) => {
15480
15170
  const out = git(["rev-list", "--no-merges", range]).trim();
15481
15171
  return out ? out.split("\n") : [];
@@ -16196,8 +15886,8 @@ async function announceRelease(deps, args) {
16196
15886
  }
16197
15887
 
16198
15888
  // src/port-registry.ts
16199
- var import_node_fs19 = require("node:fs");
16200
- var import_node_path18 = require("node:path");
15889
+ var import_node_fs18 = require("node:fs");
15890
+ var import_node_path17 = require("node:path");
16201
15891
 
16202
15892
  // ../infra/port-geometry.mjs
16203
15893
  var PORT_BLOCK = 100;
@@ -16211,8 +15901,8 @@ function nextPortBlock(registry2) {
16211
15901
  return [base, base + PORT_SPAN];
16212
15902
  }
16213
15903
  function loadPortRegistry(path2) {
16214
- if (!(0, import_node_fs19.existsSync)(path2)) return {};
16215
- const raw = JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8"));
15904
+ if (!(0, import_node_fs18.existsSync)(path2)) return {};
15905
+ const raw = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
16216
15906
  const out = {};
16217
15907
  for (const [key, value] of Object.entries(raw)) {
16218
15908
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -16226,9 +15916,9 @@ function ensurePortRange(repo, path2) {
16226
15916
  const existing = registry2[repo];
16227
15917
  if (existing) return existing;
16228
15918
  const range = nextPortBlock(registry2);
16229
- const raw = (0, import_node_fs19.existsSync)(path2) ? JSON.parse((0, import_node_fs19.readFileSync)(path2, "utf8")) : {};
15919
+ const raw = (0, import_node_fs18.existsSync)(path2) ? JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8")) : {};
16230
15920
  raw[repo] = range;
16231
- (0, import_node_fs19.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
15921
+ (0, import_node_fs18.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
16232
15922
  return range;
16233
15923
  }
16234
15924
  function portCursorSeed(registry2) {
@@ -16250,18 +15940,18 @@ function existingPortRange(repo, registry2) {
16250
15940
  return registry2[repo] ?? null;
16251
15941
  }
16252
15942
  function portRangeInfraAt(root, source) {
16253
- const registryPath = (0, import_node_path18.join)(root, "infra", "port-ranges.json");
16254
- const ddbScriptPath = (0, import_node_path18.join)(root, "infra", "port-ddb.mjs");
16255
- if (!(0, import_node_fs19.existsSync)(registryPath) || !(0, import_node_fs19.existsSync)(ddbScriptPath)) return null;
15943
+ const registryPath = (0, import_node_path17.join)(root, "infra", "port-ranges.json");
15944
+ const ddbScriptPath = (0, import_node_path17.join)(root, "infra", "port-ddb.mjs");
15945
+ if (!(0, import_node_fs18.existsSync)(registryPath) || !(0, import_node_fs18.existsSync)(ddbScriptPath)) return null;
16256
15946
  return { root, source, registryPath, ddbScriptPath };
16257
15947
  }
16258
15948
  function resolvePortRangeInfra(cwd) {
16259
15949
  const direct = portRangeInfraAt(cwd, "cwd");
16260
15950
  if (direct) return direct;
16261
- for (let dir = cwd; ; dir = (0, import_node_path18.dirname)(dir)) {
16262
- const sibling = portRangeInfraAt((0, import_node_path18.join)(dir, "MMI-Hub"), "sibling-hub");
15951
+ for (let dir = cwd; ; dir = (0, import_node_path17.dirname)(dir)) {
15952
+ const sibling = portRangeInfraAt((0, import_node_path17.join)(dir, "MMI-Hub"), "sibling-hub");
16263
15953
  if (sibling) return sibling;
16264
- const parent = (0, import_node_path18.dirname)(dir);
15954
+ const parent = (0, import_node_path17.dirname)(dir);
16265
15955
  if (parent === dir) return null;
16266
15956
  }
16267
15957
  }
@@ -17699,6 +17389,39 @@ function authorizeBodyHasMismatch(body) {
17699
17389
  var UNSET_KEYS = ["oauth", "requiredRuntimeSecrets", "edgeDomains", "requiredGcpApis", "publishRequired", "publishDir", "dsManifestPath", "fofuEnabled", "consumesDesignSystem", "ci", "requiredChecks", "gate"];
17700
17390
  var UNSET_KEY_SET = new Set(UNSET_KEYS);
17701
17391
  var RUNTIME_SECRET_STAGES = ["dev", "rc", "main"];
17392
+ var SECRET_CONSUMERS = ["runtime", "lambda", "actions", "agent", "box"];
17393
+ var SECRET_ENV_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
17394
+ function parseSecretsCatalogVar(raw) {
17395
+ let parsed;
17396
+ try {
17397
+ parsed = JSON.parse(raw);
17398
+ } catch {
17399
+ throw new Error('project set: secrets must be JSON keyed by KEY, e.g. {"SCRAPER_API_KEY":{"key":"SCRAPER_API_KEY","purpose":"scrape provider key","group":"scraper","owner":"oguz","stages":[],"consumers":["box"]}}');
17400
+ }
17401
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17402
+ throw new Error("project set: secrets must be a map keyed by canonical KEY");
17403
+ }
17404
+ const nonEmpty = (v) => typeof v === "string" && v.trim().length > 0;
17405
+ for (const [mapKey, value] of Object.entries(parsed)) {
17406
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`project set: secrets["${mapKey}"] must be an object`);
17407
+ const e = value;
17408
+ if (typeof e.key !== "string" || !SECRET_ENV_NAME_RE.test(e.key) || e.key !== mapKey) {
17409
+ throw new Error(`project set: secrets["${mapKey}"].key must equal the map key and be an env-name (UPPER_SNAKE)`);
17410
+ }
17411
+ if (!nonEmpty(e.purpose) || !nonEmpty(e.group) || !nonEmpty(e.owner)) throw new Error(`project set: secrets["${mapKey}"] needs non-empty purpose, group, owner`);
17412
+ if (e.provider !== void 0 && !nonEmpty(e.provider)) throw new Error(`project set: secrets["${mapKey}"].provider must be a non-empty string`);
17413
+ if (!Array.isArray(e.stages) || e.stages.some((s) => !RUNTIME_SECRET_STAGES.includes(s))) {
17414
+ throw new Error(`project set: secrets["${mapKey}"].stages must be a subset of dev/rc/main ([] = shared)`);
17415
+ }
17416
+ if (!Array.isArray(e.consumers) || e.consumers.some((c) => !SECRET_CONSUMERS.includes(c))) {
17417
+ throw new Error(`project set: secrets["${mapKey}"].consumers must be a subset of ${SECRET_CONSUMERS.join("/")}`);
17418
+ }
17419
+ if (e.aliases !== void 0 && (!Array.isArray(e.aliases) || e.aliases.some((a) => typeof a !== "string"))) {
17420
+ throw new Error(`project set: secrets["${mapKey}"].aliases must be a string array`);
17421
+ }
17422
+ }
17423
+ return parsed;
17424
+ }
17702
17425
  function parseRuntimeSecretsVar(raw) {
17703
17426
  let parsed;
17704
17427
  try {
@@ -17954,7 +17677,8 @@ var SETTABLE_VAR_KEYS = [
17954
17677
  "portRange",
17955
17678
  "ci",
17956
17679
  "requiredChecks",
17957
- "gate"
17680
+ "gate",
17681
+ "secrets"
17958
17682
  ];
17959
17683
  var SETTABLE_VAR_KEY_SET = new Set(SETTABLE_VAR_KEYS);
17960
17684
  var SETTABLE_VAR_HINTS = {
@@ -17968,6 +17692,7 @@ var SETTABLE_VAR_HINTS = {
17968
17692
  oauth: "JSON {subdomains,domains,callbackPath,fofuSubdomain}",
17969
17693
  requiredGcpApis: "comma-string",
17970
17694
  requiredRuntimeSecrets: 'JSON stage map, e.g. {"dev":["KEY"],"rc":["KEY"],"main":["KEY"]}',
17695
+ secrets: "JSON catalog map keyed by KEY {key,purpose,group,owner,stages[],consumers[]} \u2014 prefer --secrets-file",
17971
17696
  edgeDomains: "JSON {dev,rc,main} domain map",
17972
17697
  statusOptions: "JSON name\u2192id map",
17973
17698
  priorityOptions: "JSON {Urgent,High,Medium,Low}\u2192id map",
@@ -18023,6 +17748,8 @@ function buildProjectSetPatch(input) {
18023
17748
  patch[key] = n;
18024
17749
  } else if (key === "requiredRuntimeSecrets") {
18025
17750
  patch[key] = parseRuntimeSecretsVar(raw);
17751
+ } else if (key === "secrets") {
17752
+ patch[key] = parseSecretsCatalogVar(raw);
18026
17753
  } else if (key === "edgeDomains") {
18027
17754
  patch[key] = parseEdgeDomainsVar(raw);
18028
17755
  } else if (key === "priorityOptions") {
@@ -18132,15 +17859,15 @@ function requireProjectTarget(commandName, explicitTarget, currentRepo) {
18132
17859
  }
18133
17860
 
18134
17861
  // src/northstar-commands.ts
18135
- var import_node_fs20 = require("node:fs");
18136
- var import_node_child_process11 = require("node:child_process");
17862
+ var import_node_fs19 = require("node:fs");
17863
+ var import_node_child_process10 = require("node:child_process");
18137
17864
  var import_promises6 = require("node:fs/promises");
18138
17865
  var planSyncDetached = false;
18139
17866
  function detachPlanSync() {
18140
17867
  if (planSyncDetached) return;
18141
17868
  planSyncDetached = true;
18142
17869
  try {
18143
- (0, import_node_child_process11.spawn)(process.execPath, [process.argv[1], "northstar", "sync", "--quiet"], {
17870
+ (0, import_node_child_process10.spawn)(process.execPath, [process.argv[1], "northstar", "sync", "--quiet"], {
18144
17871
  detached: true,
18145
17872
  stdio: "ignore",
18146
17873
  windowsHide: true,
@@ -18150,7 +17877,7 @@ function detachPlanSync() {
18150
17877
  }
18151
17878
  }
18152
17879
  function makePlanDeps(cfg, io = consoleIo) {
18153
- const ensureDir = () => (0, import_node_fs20.mkdirSync)(PLANS_DIR, { recursive: true });
17880
+ const ensureDir = () => (0, import_node_fs19.mkdirSync)(PLANS_DIR, { recursive: true });
18154
17881
  return {
18155
17882
  apiUrl: cfg.sagaApiUrl,
18156
17883
  fetch: (url, init = {}) => fetch(url, { ...init, signal: init.signal ?? AbortSignal.timeout(1e4) }),
@@ -18158,31 +17885,31 @@ function makePlanDeps(cfg, io = consoleIo) {
18158
17885
  project: async () => (await sagaKey(cfg)).project,
18159
17886
  readLocal: (slug) => {
18160
17887
  try {
18161
- return (0, import_node_fs20.readFileSync)(planPath(slug), "utf8");
17888
+ return (0, import_node_fs19.readFileSync)(planPath(slug), "utf8");
18162
17889
  } catch {
18163
17890
  return null;
18164
17891
  }
18165
17892
  },
18166
17893
  listLocalSlugs: () => {
18167
17894
  try {
18168
- return (0, import_node_fs20.readdirSync)(PLANS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && /^[A-Za-z0-9][A-Za-z0-9_-]*\.md$/.test(entry.name)).map((entry) => entry.name.replace(/\.md$/, ""));
17895
+ return (0, import_node_fs19.readdirSync)(PLANS_DIR, { withFileTypes: true }).filter((entry) => entry.isFile() && /^[A-Za-z0-9][A-Za-z0-9_-]*\.md$/.test(entry.name)).map((entry) => entry.name.replace(/\.md$/, ""));
18169
17896
  } catch {
18170
17897
  return [];
18171
17898
  }
18172
17899
  },
18173
17900
  writeLocal: (slug, content) => {
18174
17901
  ensureDir();
18175
- (0, import_node_fs20.writeFileSync)(planPath(slug), content, "utf8");
17902
+ (0, import_node_fs19.writeFileSync)(planPath(slug), content, "utf8");
18176
17903
  },
18177
17904
  removeLocal: (slug) => {
18178
17905
  try {
18179
- (0, import_node_fs20.rmSync)(planPath(slug));
17906
+ (0, import_node_fs19.rmSync)(planPath(slug));
18180
17907
  } catch {
18181
17908
  }
18182
17909
  },
18183
17910
  readMetaRaw: () => {
18184
17911
  try {
18185
- return (0, import_node_fs20.readFileSync)(META_FILE, "utf8");
17912
+ return (0, import_node_fs19.readFileSync)(META_FILE, "utf8");
18186
17913
  } catch {
18187
17914
  return null;
18188
17915
  }
@@ -18193,7 +17920,7 @@ function makePlanDeps(cfg, io = consoleIo) {
18193
17920
  },
18194
17921
  readIndexRaw: () => {
18195
17922
  try {
18196
- return (0, import_node_fs20.readFileSync)(INDEX_FILE, "utf8");
17923
+ return (0, import_node_fs19.readFileSync)(INDEX_FILE, "utf8");
18197
17924
  } catch {
18198
17925
  return null;
18199
17926
  }
@@ -18204,7 +17931,7 @@ function makePlanDeps(cfg, io = consoleIo) {
18204
17931
  },
18205
17932
  readQueueRaw: () => {
18206
17933
  try {
18207
- return (0, import_node_fs20.readFileSync)(QUEUE_FILE, "utf8");
17934
+ return (0, import_node_fs19.readFileSync)(QUEUE_FILE, "utf8");
18208
17935
  } catch {
18209
17936
  return null;
18210
17937
  }
@@ -18226,7 +17953,7 @@ function openInEditor(path2) {
18226
17953
  return;
18227
17954
  }
18228
17955
  try {
18229
- (0, import_node_child_process11.spawn)(editor, [path2], { stdio: "inherit" });
17956
+ (0, import_node_child_process10.spawn)(editor, [path2], { stdio: "inherit" });
18230
17957
  } catch {
18231
17958
  console.log(`open ${path2} manually`);
18232
17959
  }
@@ -18266,7 +17993,7 @@ function repoInfoFromRemote(remote) {
18266
17993
  }
18267
17994
  function readStageUrl() {
18268
17995
  try {
18269
- const state = JSON.parse((0, import_node_fs20.readFileSync)("tmp/stage/state.json", "utf8"));
17996
+ const state = JSON.parse((0, import_node_fs19.readFileSync)("tmp/stage/state.json", "utf8"));
18270
17997
  if (typeof state.url === "string" && state.url.trim()) return state.url.trim();
18271
17998
  if (typeof state.port === "number" && Number.isFinite(state.port)) return `http://127.0.0.1:${state.port}/`;
18272
17999
  if (typeof state.healthUrl === "string" && state.healthUrl.trim()) {
@@ -18702,6 +18429,7 @@ function formatDrift(d) {
18702
18429
  section("declared but MISSING in SSM", d.declaredMissing.map((m) => m.path));
18703
18430
  section("ORPHAN in SSM, undeclared \u2014 declare or delete", d.orphan.map((o) => o.path));
18704
18431
  section("OFF-SCHEME path", d.offScheme.map((o) => `${o.path} (${o.reason})`));
18432
+ section("STAGE-SPLIT \u2014 collapse to one shared key", (d.stageSplit ?? []).map((s) => `${s.slug}/${s.leaf} split across ${s.stages.join("/")}`));
18705
18433
  section("DUPLICATE org-infra secret", d.duplicate.map((dup) => `${dup.logical}: ${dup.paths.join(" , ")}`));
18706
18434
  return lines.join("\n").trimEnd();
18707
18435
  }
@@ -18746,6 +18474,34 @@ async function secretsDoctor(deps, opts) {
18746
18474
  deps.log(formatDrift(data.drift));
18747
18475
  return data.drift.ok;
18748
18476
  }
18477
+ async function secretsOrgCatalogSet(deps, fileBody) {
18478
+ let parsed;
18479
+ try {
18480
+ parsed = JSON.parse(fileBody);
18481
+ } catch {
18482
+ deps.err('secrets org-catalog: file is not valid JSON ({ entries: { "<provider>/<KEY>": {...} } })');
18483
+ return false;
18484
+ }
18485
+ let res;
18486
+ try {
18487
+ res = await deps.fetch(`${deps.apiUrl}/catalog/org-infra`, {
18488
+ method: "POST",
18489
+ headers: await deps.headers({ "content-type": "application/json" }),
18490
+ body: JSON.stringify(parsed),
18491
+ signal: AbortSignal.timeout(TIMEOUT_MS2)
18492
+ });
18493
+ } catch (e) {
18494
+ deps.err(`secrets org-catalog: ${e.message}`);
18495
+ return false;
18496
+ }
18497
+ if (!res.ok) {
18498
+ deps.err(await upgradeMessage(res) ?? `secrets org-catalog failed: HTTP ${res.status}${await readErr(res)}`);
18499
+ return false;
18500
+ }
18501
+ const out = await res.json();
18502
+ deps.log(`org-infra catalog updated: ${out.entries ?? "?"} entries`);
18503
+ return true;
18504
+ }
18749
18505
  var DEFAULT_RUNTIME_SECRET_NAMES2 = ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"];
18750
18506
  function stringList(v) {
18751
18507
  return Array.isArray(v) ? v.filter((x) => typeof x === "string" && isValidSecretKey(x)) : [];
@@ -19117,8 +18873,8 @@ async function secretsUse(deps, key, opts) {
19117
18873
  }
19118
18874
 
19119
18875
  // src/secrets-commands.ts
19120
- var import_node_fs21 = require("node:fs");
19121
- var import_node_path19 = require("node:path");
18876
+ var import_node_fs20 = require("node:fs");
18877
+ var import_node_path18 = require("node:path");
19122
18878
  var RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS = 3e4;
19123
18879
  var DEFAULT_RAILS_CREDENTIALS_FILE = "config/credentials.yml.enc";
19124
18880
  var DEFAULT_RAILS_MASTER_KEY_FILE = "config/master.key";
@@ -19126,18 +18882,18 @@ function collectMap(value, previous = []) {
19126
18882
  return [...previous, value];
19127
18883
  }
19128
18884
  async function decryptRailsCredentials(input) {
19129
- const appDir = (0, import_node_path19.resolve)(input.appDir ?? process.cwd());
18885
+ const appDir = (0, import_node_path18.resolve)(input.appDir ?? process.cwd());
19130
18886
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
19131
18887
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
19132
- const credentialsPath = (0, import_node_path19.resolve)(appDir, credentialsFile);
19133
- const masterKeyPath = (0, import_node_path19.resolve)(appDir, masterKeyFile);
18888
+ const credentialsPath = (0, import_node_path18.resolve)(appDir, credentialsFile);
18889
+ const masterKeyPath = (0, import_node_path18.resolve)(appDir, masterKeyFile);
19134
18890
  const env = {
19135
18891
  ...process.env,
19136
18892
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
19137
18893
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
19138
18894
  };
19139
- if ((0, import_node_fs21.existsSync)(masterKeyPath)) {
19140
- env.RAILS_MASTER_KEY = (0, import_node_fs21.readFileSync)(masterKeyPath, "utf8").trim();
18895
+ if ((0, import_node_fs20.existsSync)(masterKeyPath)) {
18896
+ env.RAILS_MASTER_KEY = (0, import_node_fs20.readFileSync)(masterKeyPath, "utf8").trim();
19141
18897
  }
19142
18898
  const script = [
19143
18899
  'require "json"',
@@ -19200,6 +18956,15 @@ function registerSecretsCommands(program3) {
19200
18956
  secrets.command("doctor").description("vault drift report \u2014 declared-missing / orphan / off-scheme / duplicate (master-only, #2244)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets(async (d) => {
19201
18957
  if (!await secretsDoctor(d, o)) process.exitCode = 1;
19202
18958
  }));
18959
+ secrets.command("org-catalog").description("MASTER-ONLY: set the _org/<provider>/* catalog declarations from a JSON file (#2244)").requiredOption("--file <path>", 'JSON file: { "entries": { "<provider>/<KEY>": {key,purpose,group,owner,provider,stages[],consumers[]} } }').action((o) => withSecrets(async (d) => {
18960
+ let body;
18961
+ try {
18962
+ body = (0, import_node_fs20.readFileSync)((0, import_node_path18.resolve)(o.file), "utf8");
18963
+ } catch (e) {
18964
+ return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
18965
+ }
18966
+ if (!await secretsOrgCatalogSet(d, body)) process.exitCode = 1;
18967
+ }));
19203
18968
  secrets.command("preflight").description("check required stage secret names for a deploy/train without reading values").requiredOption("--stage <dev|rc|main>", "stage to check").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--required <KEY...>", "required keys; bare keys are scoped under --stage").action(async (o) => {
19204
18969
  if (!["dev", "rc", "main"].includes(o.stage)) {
19205
18970
  return fail("secrets preflight: --stage must be dev, rc, or main");
@@ -19269,7 +19034,7 @@ function registerSecretsCommands(program3) {
19269
19034
  {
19270
19035
  ...d,
19271
19036
  decryptRailsCredentials,
19272
- removeFile: (path2) => (0, import_node_fs21.unlinkSync)((0, import_node_path19.resolve)(o.appDir ?? process.cwd(), path2))
19037
+ removeFile: (path2) => (0, import_node_fs20.unlinkSync)((0, import_node_path18.resolve)(o.appDir ?? process.cwd(), path2))
19273
19038
  },
19274
19039
  {
19275
19040
  repo: o.repo,
@@ -19355,10 +19120,10 @@ function registerEdgeCommands(program3) {
19355
19120
  }
19356
19121
 
19357
19122
  // src/doctor-run.ts
19358
- var import_node_fs25 = require("node:fs");
19359
- var import_node_child_process13 = require("node:child_process");
19123
+ var import_node_fs24 = require("node:fs");
19124
+ var import_node_child_process12 = require("node:child_process");
19360
19125
  var import_promises7 = require("node:fs/promises");
19361
- var import_node_path22 = require("node:path");
19126
+ var import_node_path21 = require("node:path");
19362
19127
  var import_node_os5 = require("node:os");
19363
19128
 
19364
19129
  // src/plugin-guard.ts
@@ -19379,10 +19144,10 @@ function buildGuardSessionStartLine(state, opts = {}) {
19379
19144
  }
19380
19145
 
19381
19146
  // src/cursor-plugin-seed.ts
19382
- var import_node_child_process12 = require("node:child_process");
19383
- var import_node_fs22 = require("node:fs");
19147
+ var import_node_child_process11 = require("node:child_process");
19148
+ var import_node_fs21 = require("node:fs");
19384
19149
  var import_node_os4 = require("node:os");
19385
- var import_node_path20 = require("node:path");
19150
+ var import_node_path19 = require("node:path");
19386
19151
  var import_node_util7 = require("node:util");
19387
19152
  function isSemverVersion(v) {
19388
19153
  return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
@@ -19390,7 +19155,7 @@ function isSemverVersion(v) {
19390
19155
  var MMI_HUB_REPO = "mutmutco/MMI-Hub";
19391
19156
  var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
19392
19157
  var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
19393
- var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process12.execFile);
19158
+ var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process11.execFile);
19394
19159
  function gitFetchReleaseTagArgs(hubCheckout, tag) {
19395
19160
  return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
19396
19161
  }
@@ -19399,17 +19164,17 @@ function ghReleaseTarballApiArgs(tag) {
19399
19164
  }
19400
19165
  function cursorUserGlobalStatePath() {
19401
19166
  if (process.platform === "win32") {
19402
- const base = process.env.APPDATA || (0, import_node_path20.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
19403
- return (0, import_node_path20.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
19167
+ const base = process.env.APPDATA || (0, import_node_path19.join)((0, import_node_os4.homedir)(), "AppData", "Roaming");
19168
+ return (0, import_node_path19.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
19404
19169
  }
19405
19170
  if (process.platform === "darwin") {
19406
- return (0, import_node_path20.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
19171
+ return (0, import_node_path19.join)((0, import_node_os4.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
19407
19172
  }
19408
- return (0, import_node_path20.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
19173
+ return (0, import_node_path19.join)((0, import_node_os4.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
19409
19174
  }
19410
19175
  async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
19411
19176
  const dbPath = cursorUserGlobalStatePath();
19412
- if (!(0, import_node_fs22.existsSync)(dbPath)) return void 0;
19177
+ if (!(0, import_node_fs21.existsSync)(dbPath)) return void 0;
19413
19178
  try {
19414
19179
  const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
19415
19180
  timeout: 5e3
@@ -19423,57 +19188,57 @@ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
19423
19188
  }
19424
19189
  }
19425
19190
  function syncDirContents(src, dest) {
19426
- (0, import_node_fs22.mkdirSync)(dest, { recursive: true });
19427
- for (const name of (0, import_node_fs22.readdirSync)(dest)) {
19428
- (0, import_node_fs22.rmSync)((0, import_node_path20.join)(dest, name), { recursive: true, force: true });
19191
+ (0, import_node_fs21.mkdirSync)(dest, { recursive: true });
19192
+ for (const name of (0, import_node_fs21.readdirSync)(dest)) {
19193
+ (0, import_node_fs21.rmSync)((0, import_node_path19.join)(dest, name), { recursive: true, force: true });
19429
19194
  }
19430
- (0, import_node_fs22.cpSync)(src, dest, { recursive: true });
19195
+ (0, import_node_fs21.cpSync)(src, dest, { recursive: true });
19431
19196
  }
19432
19197
  function releaseTag(releasedVersion) {
19433
19198
  return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
19434
19199
  }
19435
19200
  async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
19436
- const tarFile = (0, import_node_path20.join)(tmpRoot, "archive.tar");
19201
+ const tarFile = (0, import_node_path19.join)(tmpRoot, "archive.tar");
19437
19202
  try {
19438
19203
  await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
19439
19204
  await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
19440
19205
  timeout: 6e4
19441
19206
  });
19442
19207
  await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
19443
- const pluginMmi = (0, import_node_path20.join)(tmpRoot, "plugins", "mmi");
19444
- return (0, import_node_fs22.existsSync)((0, import_node_path20.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
19208
+ const pluginMmi = (0, import_node_path19.join)(tmpRoot, "plugins", "mmi");
19209
+ return (0, import_node_fs21.existsSync)((0, import_node_path19.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
19445
19210
  } catch {
19446
19211
  return void 0;
19447
19212
  }
19448
19213
  }
19449
19214
  async function downloadPluginMmiViaGh(tag, tmpRoot) {
19450
- const tarPath = (0, import_node_path20.join)(tmpRoot, "repo.tgz");
19215
+ const tarPath = (0, import_node_path19.join)(tmpRoot, "repo.tgz");
19451
19216
  try {
19452
- (0, import_node_fs22.mkdirSync)(tmpRoot, { recursive: true });
19217
+ (0, import_node_fs21.mkdirSync)(tmpRoot, { recursive: true });
19453
19218
  const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
19454
19219
  timeout: 12e4,
19455
19220
  maxBuffer: 100 * 1024 * 1024,
19456
19221
  encoding: "buffer",
19457
19222
  windowsHide: true
19458
19223
  });
19459
- (0, import_node_fs22.writeFileSync)(tarPath, stdout);
19224
+ (0, import_node_fs21.writeFileSync)(tarPath, stdout);
19460
19225
  await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
19461
- const top = (0, import_node_fs22.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
19226
+ const top = (0, import_node_fs21.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
19462
19227
  if (!top) return void 0;
19463
- const pluginMmi = (0, import_node_path20.join)(tmpRoot, top, "plugins", "mmi");
19464
- return (0, import_node_fs22.existsSync)((0, import_node_path20.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
19228
+ const pluginMmi = (0, import_node_path19.join)(tmpRoot, top, "plugins", "mmi");
19229
+ return (0, import_node_fs21.existsSync)((0, import_node_path19.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
19465
19230
  } catch {
19466
19231
  return void 0;
19467
19232
  }
19468
19233
  }
19469
19234
  async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
19470
- (0, import_node_fs22.mkdirSync)(tmpRoot, { recursive: true });
19235
+ (0, import_node_fs21.mkdirSync)(tmpRoot, { recursive: true });
19471
19236
  const tag = releaseTag(releasedVersion);
19472
19237
  if (hubCheckout) {
19473
19238
  const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
19474
19239
  if (fromHub) return fromHub;
19475
19240
  }
19476
- return downloadPluginMmiViaGh(tag, (0, import_node_path20.join)(tmpRoot, "gh"));
19241
+ return downloadPluginMmiViaGh(tag, (0, import_node_path19.join)(tmpRoot, "gh"));
19477
19242
  }
19478
19243
  function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
19479
19244
  if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
@@ -19494,7 +19259,7 @@ async function applyCursorPluginCacheSeed(input) {
19494
19259
  for (const pin of pinsToSeed) {
19495
19260
  syncDirContents(source, pin.path);
19496
19261
  }
19497
- (0, import_node_fs22.rmSync)(tmpRoot, { recursive: true, force: true });
19262
+ (0, import_node_fs21.rmSync)(tmpRoot, { recursive: true, force: true });
19498
19263
  return true;
19499
19264
  }
19500
19265
 
@@ -20343,7 +20108,7 @@ function cursorPluginInstallFix(input) {
20343
20108
  const cacheDir = joinCachePath(input.cacheRoot, pin);
20344
20109
  const autoSeed = "run `mmi-cli doctor --apply` to seed plugins/mmi from the latest release into the active pin";
20345
20110
  const localSeed = input.hubCheckout ? `temporary fallback: copy ${joinCachePath(input.hubCheckout, "plugins", "mmi")} to ${cacheDir}, then restart Cursor` : `temporary fallback: copy plugins/mmi from a local MMI-Hub checkout to ${cacheDir}, then restart Cursor`;
20346
- return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL}, ${CURSOR_HOOKS_JSON_REL}, or the shell-dialect guard scripts \u2014 ${autoSeed}; ${marketplaceRefresh}; ${authSteps}; ${localSeed}; ${logHint}; ${guide}`;
20111
+ return `Cursor plugin cache at ${cacheDir} is empty or missing ${CURSOR_PLUGIN_JSON_REL}, ${CURSOR_HOOKS_JSON_REL}, or the cursor-hook dispatcher script \u2014 ${autoSeed}; ${marketplaceRefresh}; ${authSteps}; ${localSeed}; ${logHint}; ${guide}`;
20347
20112
  }
20348
20113
  function buildCursorPluginInstallCheck(input) {
20349
20114
  const base = {
@@ -20365,7 +20130,7 @@ function buildCursorPluginInstallCheck(input) {
20365
20130
  };
20366
20131
  }
20367
20132
  for (const pin of input.pins) {
20368
- if (!pin.hasPluginJson || !pin.hasHooksJson || pin.hasShellDialectGuard === false || pin.isEmpty) {
20133
+ if (!pin.hasPluginJson || !pin.hasHooksJson || pin.hasCursorHookScript === false || pin.isEmpty) {
20369
20134
  return {
20370
20135
  ...base,
20371
20136
  ok: false,
@@ -20647,9 +20412,9 @@ function buildPluginResolvabilityCheck(input) {
20647
20412
  }
20648
20413
 
20649
20414
  // src/cli-doctor-shared.ts
20415
+ var import_node_fs22 = require("node:fs");
20416
+ var import_node_path20 = require("node:path");
20650
20417
  var import_node_fs23 = require("node:fs");
20651
- var import_node_path21 = require("node:path");
20652
- var import_node_fs24 = require("node:fs");
20653
20418
  var GC_GH_TIMEOUT_MS = 2e4;
20654
20419
  async function awsCallerArn() {
20655
20420
  try {
@@ -20695,7 +20460,7 @@ async function localBranchHeads() {
20695
20460
  }
20696
20461
  async function currentRepoWorktreeGitRoot(repoRoot) {
20697
20462
  const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
20698
- return gitCommonDir ? (0, import_node_path21.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
20463
+ return gitCommonDir ? (0, import_node_path20.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
20699
20464
  }
20700
20465
  async function worktreeBranches() {
20701
20466
  const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
@@ -20715,18 +20480,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
20715
20480
  const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
20716
20481
  if (!match?.[1]) return void 0;
20717
20482
  const raw = match[1].trim();
20718
- return (0, import_node_path21.isAbsolute)(raw) ? raw : (0, import_node_path21.resolve)(worktreePath, raw);
20483
+ return (0, import_node_path20.isAbsolute)(raw) ? raw : (0, import_node_path20.resolve)(worktreePath, raw);
20719
20484
  }
20720
20485
  function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
20721
20486
  if (!worktreeGitRoot) return false;
20722
20487
  try {
20723
- const entries = (0, import_node_fs24.readdirSync)(worktreeGitRoot, { withFileTypes: true });
20488
+ const entries = (0, import_node_fs23.readdirSync)(worktreeGitRoot, { withFileTypes: true });
20724
20489
  for (const ent of entries) {
20725
20490
  if (!ent.isDirectory()) continue;
20726
20491
  try {
20727
- const gitdirPath = (0, import_node_fs23.readFileSync)((0, import_node_path21.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
20728
- const resolvedGitdir = (0, import_node_path21.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path21.resolve)(worktreeGitRoot, ent.name, gitdirPath);
20729
- if (sameWorktreeMetadataPath((0, import_node_path21.dirname)(resolvedGitdir), worktreePath)) return true;
20492
+ const gitdirPath = (0, import_node_fs22.readFileSync)((0, import_node_path20.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
20493
+ const resolvedGitdir = (0, import_node_path20.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path20.resolve)(worktreeGitRoot, ent.name, gitdirPath);
20494
+ if (sameWorktreeMetadataPath((0, import_node_path20.dirname)(resolvedGitdir), worktreePath)) return true;
20730
20495
  } catch {
20731
20496
  }
20732
20497
  }
@@ -20736,7 +20501,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
20736
20501
  }
20737
20502
  function pathExistsKnown(path2) {
20738
20503
  try {
20739
- (0, import_node_fs24.statSync)(path2);
20504
+ (0, import_node_fs23.statSync)(path2);
20740
20505
  return true;
20741
20506
  } catch (e) {
20742
20507
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
@@ -20745,10 +20510,10 @@ function pathExistsKnown(path2) {
20745
20510
  }
20746
20511
  }
20747
20512
  function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
20748
- const gitPath = (0, import_node_path21.join)(path2, ".git");
20513
+ const gitPath = (0, import_node_path20.join)(path2, ".git");
20749
20514
  let st;
20750
20515
  try {
20751
- st = (0, import_node_fs24.lstatSync)(gitPath);
20516
+ st = (0, import_node_fs23.lstatSync)(gitPath);
20752
20517
  } catch (e) {
20753
20518
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
20754
20519
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -20765,7 +20530,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
20765
20530
  if (st.isDirectory()) return { path: path2, gitType: "dir" };
20766
20531
  if (!st.isFile()) return { path: path2, gitType: "other" };
20767
20532
  try {
20768
- const gitFileContent = (0, import_node_fs23.readFileSync)(gitPath, "utf8");
20533
+ const gitFileContent = (0, import_node_fs22.readFileSync)(gitPath, "utf8");
20769
20534
  const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
20770
20535
  const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
20771
20536
  return {
@@ -20782,7 +20547,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
20782
20547
  }
20783
20548
  function inspectDeadWorktreeDirContent(path2) {
20784
20549
  try {
20785
- return { entries: (0, import_node_fs24.readdirSync)(path2) };
20550
+ return { entries: (0, import_node_fs23.readdirSync)(path2) };
20786
20551
  } catch (e) {
20787
20552
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
20788
20553
  return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
@@ -20801,8 +20566,8 @@ async function siblingWorktreeDirs() {
20801
20566
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot);
20802
20567
  const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
20803
20568
  try {
20804
- const entries = (0, import_node_fs24.readdirSync)(siblingRoot, { withFileTypes: true });
20805
- return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path21.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
20569
+ const entries = (0, import_node_fs23.readdirSync)(siblingRoot, { withFileTypes: true });
20570
+ return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path20.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
20806
20571
  } catch {
20807
20572
  return [];
20808
20573
  }
@@ -20852,7 +20617,7 @@ async function fetchHubVersionInfo(baseUrl) {
20852
20617
  }
20853
20618
  function readRepoVersion() {
20854
20619
  try {
20855
- return JSON.parse((0, import_node_fs25.readFileSync)((0, import_node_path22.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
20620
+ return JSON.parse((0, import_node_fs24.readFileSync)((0, import_node_path21.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
20856
20621
  } catch {
20857
20622
  return void 0;
20858
20623
  }
@@ -20912,7 +20677,7 @@ function reexecMmiCli(args) {
20912
20677
  }
20913
20678
  };
20914
20679
  const env = { ...process.env, [DOCTOR_POST_SELF_UPDATE_ENV]: "1" };
20915
- const child = isWin ? (0, import_node_child_process13.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", env }) : (0, import_node_child_process13.spawn)("mmi-cli", args, { stdio: "inherit", env });
20680
+ const child = isWin ? (0, import_node_child_process12.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", env }) : (0, import_node_child_process12.spawn)("mmi-cli", args, { stdio: "inherit", env });
20916
20681
  child.on("error", () => done(-1));
20917
20682
  child.on("exit", (code) => done(code ?? 0));
20918
20683
  });
@@ -20969,11 +20734,11 @@ async function applyPluginHeal(token, surface, log, opts) {
20969
20734
  }
20970
20735
  var installedPluginsPath = (surface = detectSurface(process.env)) => {
20971
20736
  const homeDir = surface === "codex" ? ".codex" : ".claude";
20972
- return (0, import_node_path22.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
20737
+ return (0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "installed_plugins.json");
20973
20738
  };
20974
20739
  function readInstalledPlugins(surface = detectSurface(process.env)) {
20975
20740
  try {
20976
- return JSON.parse((0, import_node_fs25.readFileSync)(installedPluginsPath(surface), "utf8"));
20741
+ return JSON.parse((0, import_node_fs24.readFileSync)(installedPluginsPath(surface), "utf8"));
20977
20742
  } catch {
20978
20743
  return null;
20979
20744
  }
@@ -20984,15 +20749,15 @@ function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRep
20984
20749
  return {
20985
20750
  isOrgRepo,
20986
20751
  installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
20987
- marketplaceClonePresent: (0, import_node_fs25.existsSync)((0, import_node_path22.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "marketplaces", "mutmutco")),
20988
- pluginCachePresent: (0, import_node_fs25.existsSync)((0, import_node_path22.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
20752
+ marketplaceClonePresent: (0, import_node_fs24.existsSync)((0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "marketplaces", "mutmutco")),
20753
+ pluginCachePresent: (0, import_node_fs24.existsSync)((0, import_node_path21.join)((0, import_node_os5.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
20989
20754
  };
20990
20755
  }
20991
20756
  function installedPluginSources() {
20992
20757
  return ["claude", "codex"].map((surface) => {
20993
- const recordPath = (0, import_node_path22.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
20758
+ const recordPath = (0, import_node_path21.join)((0, import_node_os5.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
20994
20759
  try {
20995
- return { surface, installed: JSON.parse((0, import_node_fs25.readFileSync)(recordPath, "utf8")), recordPath };
20760
+ return { surface, installed: JSON.parse((0, import_node_fs24.readFileSync)(recordPath, "utf8")), recordPath };
20996
20761
  } catch {
20997
20762
  return { surface, installed: null, recordPath };
20998
20763
  }
@@ -21000,7 +20765,7 @@ function installedPluginSources() {
21000
20765
  }
21001
20766
  function readClaudeSettings() {
21002
20767
  try {
21003
- return JSON.parse((0, import_node_fs25.readFileSync)((0, import_node_path22.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
20768
+ return JSON.parse((0, import_node_fs24.readFileSync)((0, import_node_path21.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
21004
20769
  } catch {
21005
20770
  return null;
21006
20771
  }
@@ -21022,7 +20787,7 @@ function writeProjectInstallRecord(record) {
21022
20787
  const list = file.plugins[MMI_PLUGIN_ID] ?? [];
21023
20788
  list.push(record);
21024
20789
  file.plugins[MMI_PLUGIN_ID] = list;
21025
- (0, import_node_fs25.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
20790
+ (0, import_node_fs24.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
21026
20791
  `, "utf8");
21027
20792
  return true;
21028
20793
  } catch {
@@ -21035,9 +20800,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
21035
20800
  if (!file) return false;
21036
20801
  if (!file.plugins) file.plugins = {};
21037
20802
  const path2 = installedPluginsPath();
21038
- (0, import_node_fs25.copyFileSync)(path2, `${path2}.bak`);
20803
+ (0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
21039
20804
  file.plugins[pluginId] = records;
21040
- (0, import_node_fs25.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
20805
+ (0, import_node_fs24.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
21041
20806
  `, "utf8");
21042
20807
  return true;
21043
20808
  } catch {
@@ -21045,22 +20810,22 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
21045
20810
  }
21046
20811
  }
21047
20812
  function opencodeConfigDir() {
21048
- return (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".config", "opencode");
20813
+ return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".config", "opencode");
21049
20814
  }
21050
20815
  function opencodeConfigPath() {
21051
- return (0, import_node_path22.join)(opencodeConfigDir(), "opencode.jsonc");
20816
+ return (0, import_node_path21.join)(opencodeConfigDir(), "opencode.jsonc");
21052
20817
  }
21053
20818
  function opencodeCommandsDir() {
21054
- return (0, import_node_path22.join)(opencodeConfigDir(), "commands");
20819
+ return (0, import_node_path21.join)(opencodeConfigDir(), "commands");
21055
20820
  }
21056
20821
  function opencodeSkillsPath() {
21057
- return (0, import_node_path22.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
20822
+ return (0, import_node_path21.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
21058
20823
  }
21059
20824
  function opencodeConfigSnapshot() {
21060
20825
  const path2 = opencodeConfigPath();
21061
- if (!(0, import_node_fs25.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
20826
+ if (!(0, import_node_fs24.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
21062
20827
  try {
21063
- const raw = (0, import_node_fs25.readFileSync)(path2, "utf8");
20828
+ const raw = (0, import_node_fs24.readFileSync)(path2, "utf8");
21064
20829
  const parsed = JSON.parse(stripJsonc(raw));
21065
20830
  const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
21066
20831
  const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
@@ -21083,9 +20848,9 @@ function writeOpencodeConfigPlugin(snapshot) {
21083
20848
  const plan2 = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
21084
20849
  if (plan2.action === "already") return true;
21085
20850
  if (!plan2.text || plan2.action === "unsafe") return false;
21086
- (0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(path2), { recursive: true });
21087
- if (snapshot.hasConfig) (0, import_node_fs25.copyFileSync)(path2, `${path2}.bak`);
21088
- (0, import_node_fs25.writeFileSync)(path2, plan2.text, "utf8");
20851
+ (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
20852
+ if (snapshot.hasConfig) (0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
20853
+ (0, import_node_fs24.writeFileSync)(path2, plan2.text, "utf8");
21089
20854
  return true;
21090
20855
  } catch {
21091
20856
  return false;
@@ -21100,9 +20865,9 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
21100
20865
  const normalized = skillsPath.replace(/\\/g, "/");
21101
20866
  if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
21102
20867
  parsed.skills = { ...skills, paths };
21103
- (0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(snapshot.path), { recursive: true });
21104
- if (snapshot.hasConfig && (0, import_node_fs25.existsSync)(snapshot.path)) (0, import_node_fs25.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
21105
- (0, import_node_fs25.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
20868
+ (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(snapshot.path), { recursive: true });
20869
+ if (snapshot.hasConfig && (0, import_node_fs24.existsSync)(snapshot.path)) (0, import_node_fs24.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
20870
+ (0, import_node_fs24.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
21106
20871
  `, "utf8");
21107
20872
  return true;
21108
20873
  } catch {
@@ -21111,7 +20876,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
21111
20876
  }
21112
20877
  function opencodeExistingCommands() {
21113
20878
  try {
21114
- return (0, import_node_fs25.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
20879
+ return (0, import_node_fs24.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
21115
20880
  } catch {
21116
20881
  return [];
21117
20882
  }
@@ -21119,9 +20884,9 @@ function opencodeExistingCommands() {
21119
20884
  function writeOpencodeCommandFiles() {
21120
20885
  try {
21121
20886
  const dir = opencodeCommandsDir();
21122
- (0, import_node_fs25.mkdirSync)(dir, { recursive: true });
20887
+ (0, import_node_fs24.mkdirSync)(dir, { recursive: true });
21123
20888
  for (const command of OPENCODE_WORKFLOW_COMMANDS) {
21124
- (0, import_node_fs25.writeFileSync)((0, import_node_path22.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
20889
+ (0, import_node_fs24.writeFileSync)((0, import_node_path21.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
21125
20890
  }
21126
20891
  return true;
21127
20892
  } catch {
@@ -21130,12 +20895,12 @@ function writeOpencodeCommandFiles() {
21130
20895
  }
21131
20896
  function readOpencodeAdapterDiskVersion() {
21132
20897
  const candidates = [
21133
- (0, import_node_path22.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
21134
- (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
20898
+ (0, import_node_path21.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
20899
+ (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
21135
20900
  ];
21136
20901
  for (const path2 of candidates) {
21137
20902
  try {
21138
- const parsed = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
20903
+ const parsed = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
21139
20904
  if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
21140
20905
  } catch {
21141
20906
  continue;
@@ -21151,7 +20916,7 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
21151
20916
  try {
21152
20917
  const specs = opencodeMmiPluginSpecs(snapshot);
21153
20918
  log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
21154
- (0, import_node_fs25.mkdirSync)(opencodeConfigDir(), { recursive: true });
20919
+ (0, import_node_fs24.mkdirSync)(opencodeConfigDir(), { recursive: true });
21155
20920
  await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
21156
20921
  return true;
21157
20922
  } catch {
@@ -21166,30 +20931,30 @@ function opencodePluginVersionsForReport() {
21166
20931
  }
21167
20932
  function opencodeDesktopLogsRoot() {
21168
20933
  if (process.platform === "win32") {
21169
- const base = process.env.APPDATA || (0, import_node_path22.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
21170
- return (0, import_node_path22.join)(base, "ai.opencode.desktop", "logs");
20934
+ const base = process.env.APPDATA || (0, import_node_path21.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
20935
+ return (0, import_node_path21.join)(base, "ai.opencode.desktop", "logs");
21171
20936
  }
21172
20937
  if (process.platform === "darwin") {
21173
- return (0, import_node_path22.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
20938
+ return (0, import_node_path21.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
21174
20939
  }
21175
- return (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
20940
+ return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".config", "ai.opencode.desktop", "logs");
21176
20941
  }
21177
20942
  function opencodeDesktopBootstrapSnapshot() {
21178
20943
  const root = opencodeDesktopLogsRoot();
21179
20944
  try {
21180
- const sessionDirs = (0, import_node_fs25.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path22.join)(root, entry.name)).sort((a, b) => (0, import_node_fs25.statSync)(b).mtimeMs - (0, import_node_fs25.statSync)(a).mtimeMs);
20945
+ const sessionDirs = (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path21.join)(root, entry.name)).sort((a, b) => (0, import_node_fs24.statSync)(b).mtimeMs - (0, import_node_fs24.statSync)(a).mtimeMs);
21181
20946
  const newest = sessionDirs[0];
21182
20947
  if (!newest) return [];
21183
- const logPath = (0, import_node_path22.join)(newest, "renderer.log");
21184
- const text = (0, import_node_fs25.readFileSync)(logPath, "utf8");
21185
- return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs25.existsSync)(directory)).map((directory) => ({ directory, logPath }));
20948
+ const logPath = (0, import_node_path21.join)(newest, "renderer.log");
20949
+ const text = (0, import_node_fs24.readFileSync)(logPath, "utf8");
20950
+ return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs24.existsSync)(directory)).map((directory) => ({ directory, logPath }));
21186
20951
  } catch {
21187
20952
  return [];
21188
20953
  }
21189
20954
  }
21190
20955
  function opencodeLegacyConfigSnapshot() {
21191
- const legacyPath = (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
21192
- if (!(0, import_node_fs25.existsSync)(legacyPath)) return {};
20956
+ const legacyPath = (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".opencode", "opencode.json");
20957
+ if (!(0, import_node_fs24.existsSync)(legacyPath)) return {};
21193
20958
  const content = readTextFile(legacyPath);
21194
20959
  if (content == null) return {};
21195
20960
  const plugins = parseOpencodeLegacyConfigPlugins(content);
@@ -21201,46 +20966,45 @@ function opencodeLegacyConfigSnapshot() {
21201
20966
  function quarantineOpencodeLegacyConfig(legacyPath) {
21202
20967
  try {
21203
20968
  const backupPath = `${legacyPath}.bak`;
21204
- if ((0, import_node_fs25.existsSync)(backupPath)) return false;
21205
- (0, import_node_fs25.renameSync)(legacyPath, backupPath);
20969
+ if ((0, import_node_fs24.existsSync)(backupPath)) return false;
20970
+ (0, import_node_fs24.renameSync)(legacyPath, backupPath);
21206
20971
  return true;
21207
20972
  } catch {
21208
20973
  return false;
21209
20974
  }
21210
20975
  }
21211
20976
  function cursorPluginCacheRoot() {
21212
- return (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
20977
+ return (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
21213
20978
  }
21214
20979
  function cursorPluginCachePinSnapshots() {
21215
20980
  const root = cursorPluginCacheRoot();
21216
20981
  try {
21217
- return (0, import_node_fs25.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
21218
- const path2 = (0, import_node_path22.join)(root, entry.name);
21219
- const pluginJson = (0, import_node_path22.join)(path2, ".cursor-plugin", "plugin.json");
21220
- const hooksJson = (0, import_node_path22.join)(path2, "hooks", "hooks.json");
21221
- const cliBundle = (0, import_node_path22.join)(path2, "cli", "dist", "index.cjs");
21222
- const throttleGateCursor = (0, import_node_path22.join)(path2, "scripts", "throttle-gate-cursor.mjs");
21223
- const throttleCore = (0, import_node_path22.join)(path2, "scripts", "throttle-core.mjs");
20982
+ return (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
20983
+ const path2 = (0, import_node_path21.join)(root, entry.name);
20984
+ const pluginJson = (0, import_node_path21.join)(path2, ".cursor-plugin", "plugin.json");
20985
+ const hooksJson = (0, import_node_path21.join)(path2, "hooks", "hooks.json");
20986
+ const cliBundle = (0, import_node_path21.join)(path2, "cli", "dist", "index.cjs");
20987
+ const cursorHook = (0, import_node_path21.join)(path2, "scripts", "cursor-hook.mjs");
21224
20988
  let version;
21225
20989
  try {
21226
- const raw = JSON.parse((0, import_node_fs25.readFileSync)(pluginJson, "utf8"));
20990
+ const raw = JSON.parse((0, import_node_fs24.readFileSync)(pluginJson, "utf8"));
21227
20991
  version = typeof raw.version === "string" ? raw.version : void 0;
21228
20992
  } catch {
21229
20993
  version = void 0;
21230
20994
  }
21231
20995
  let isEmpty = true;
21232
20996
  try {
21233
- isEmpty = (0, import_node_fs25.readdirSync)(path2).length === 0;
20997
+ isEmpty = (0, import_node_fs24.readdirSync)(path2).length === 0;
21234
20998
  } catch {
21235
20999
  isEmpty = true;
21236
21000
  }
21237
21001
  return {
21238
21002
  name: entry.name,
21239
21003
  path: path2,
21240
- hasPluginJson: (0, import_node_fs25.existsSync)(pluginJson),
21241
- hasHooksJson: (0, import_node_fs25.existsSync)(hooksJson),
21242
- hasCliBundle: (0, import_node_fs25.existsSync)(cliBundle),
21243
- hasShellDialectGuard: (0, import_node_fs25.existsSync)(throttleGateCursor) && (0, import_node_fs25.existsSync)(throttleCore),
21004
+ hasPluginJson: (0, import_node_fs24.existsSync)(pluginJson),
21005
+ hasHooksJson: (0, import_node_fs24.existsSync)(hooksJson),
21006
+ hasCliBundle: (0, import_node_fs24.existsSync)(cliBundle),
21007
+ hasCursorHookScript: (0, import_node_fs24.existsSync)(cursorHook),
21244
21008
  isEmpty,
21245
21009
  version
21246
21010
  };
@@ -21250,19 +21014,19 @@ function cursorPluginCachePinSnapshots() {
21250
21014
  }
21251
21015
  }
21252
21016
  function hubCheckoutForCursorSeed() {
21253
- const manifest = (0, import_node_path22.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
21254
- return (0, import_node_fs25.existsSync)(manifest) ? process.cwd() : void 0;
21017
+ const manifest = (0, import_node_path21.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
21018
+ return (0, import_node_fs24.existsSync)(manifest) ? process.cwd() : void 0;
21255
21019
  }
21256
21020
  function mmiPluginCacheRootSnapshots() {
21257
21021
  const roots = [
21258
- { surface: "claude", root: (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
21259
- { surface: "codex", root: (0, import_node_path22.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
21022
+ { surface: "claude", root: (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
21023
+ { surface: "codex", root: (0, import_node_path21.join)((0, import_node_os5.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
21260
21024
  ];
21261
21025
  return roots.flatMap(({ surface, root }) => {
21262
21026
  try {
21263
- const entries = (0, import_node_fs25.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
21027
+ const entries = (0, import_node_fs24.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
21264
21028
  name: entry.name,
21265
- path: (0, import_node_path22.join)(root, entry.name),
21029
+ path: (0, import_node_path21.join)(root, entry.name),
21266
21030
  isDirectory: entry.isDirectory()
21267
21031
  }));
21268
21032
  return [{ surface, root, entries }];
@@ -21273,7 +21037,7 @@ function mmiPluginCacheRootSnapshots() {
21273
21037
  }
21274
21038
  function hasNestedMmiChild(versionDir) {
21275
21039
  try {
21276
- return (0, import_node_fs25.statSync)((0, import_node_path22.join)(versionDir, "mmi")).isDirectory();
21040
+ return (0, import_node_fs24.statSync)((0, import_node_path21.join)(versionDir, "mmi")).isDirectory();
21277
21041
  } catch {
21278
21042
  return false;
21279
21043
  }
@@ -21284,10 +21048,10 @@ function nestedPluginTreeSnapshot() {
21284
21048
  );
21285
21049
  }
21286
21050
  function uniqueQuarantineTarget(path2) {
21287
- if (!(0, import_node_fs25.existsSync)(path2)) return path2;
21051
+ if (!(0, import_node_fs24.existsSync)(path2)) return path2;
21288
21052
  for (let i = 1; i < 100; i += 1) {
21289
21053
  const candidate = `${path2}-${i}`;
21290
- if (!(0, import_node_fs25.existsSync)(candidate)) return candidate;
21054
+ if (!(0, import_node_fs24.existsSync)(candidate)) return candidate;
21291
21055
  }
21292
21056
  return `${path2}-${Date.now()}`;
21293
21057
  }
@@ -21296,10 +21060,10 @@ function quarantinePluginCacheDirs(plan2) {
21296
21060
  const failed = [];
21297
21061
  for (const move of plan2) {
21298
21062
  try {
21299
- if (!(0, import_node_fs25.existsSync)(move.from)) continue;
21063
+ if (!(0, import_node_fs24.existsSync)(move.from)) continue;
21300
21064
  const target = uniqueQuarantineTarget(move.to);
21301
- (0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(target), { recursive: true });
21302
- (0, import_node_fs25.renameSync)(move.from, target);
21065
+ (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(target), { recursive: true });
21066
+ (0, import_node_fs24.renameSync)(move.from, target);
21303
21067
  moved += 1;
21304
21068
  } catch {
21305
21069
  failed.push(move);
@@ -21318,23 +21082,23 @@ async function robocopyMirrorEmpty(emptyDir, target) {
21318
21082
  }
21319
21083
  async function clearNestedPluginTreeDir(targetPath) {
21320
21084
  try {
21321
- if (!(0, import_node_fs25.existsSync)(targetPath)) return true;
21085
+ if (!(0, import_node_fs24.existsSync)(targetPath)) return true;
21322
21086
  if (isWin) {
21323
- const emptyDir = (0, import_node_path22.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
21324
- (0, import_node_fs25.mkdirSync)(emptyDir, { recursive: true });
21087
+ const emptyDir = (0, import_node_path21.join)((0, import_node_os5.tmpdir)(), `mmi-empty-${Date.now()}`);
21088
+ (0, import_node_fs24.mkdirSync)(emptyDir, { recursive: true });
21325
21089
  try {
21326
21090
  await robocopyMirrorEmpty(emptyDir, targetPath);
21327
- (0, import_node_fs25.rmSync)(targetPath, { recursive: true, force: true });
21091
+ (0, import_node_fs24.rmSync)(targetPath, { recursive: true, force: true });
21328
21092
  } finally {
21329
21093
  try {
21330
- (0, import_node_fs25.rmSync)(emptyDir, { recursive: true, force: true });
21094
+ (0, import_node_fs24.rmSync)(emptyDir, { recursive: true, force: true });
21331
21095
  } catch {
21332
21096
  }
21333
21097
  }
21334
- return !(0, import_node_fs25.existsSync)(targetPath);
21098
+ return !(0, import_node_fs24.existsSync)(targetPath);
21335
21099
  }
21336
- (0, import_node_fs25.rmSync)(targetPath, { recursive: true, force: true });
21337
- return !(0, import_node_fs25.existsSync)(targetPath);
21100
+ (0, import_node_fs24.rmSync)(targetPath, { recursive: true, force: true });
21101
+ return !(0, import_node_fs24.existsSync)(targetPath);
21338
21102
  } catch {
21339
21103
  return false;
21340
21104
  }
@@ -21347,11 +21111,11 @@ async function applyNestedPluginTreeCleanup(paths, log) {
21347
21111
  }
21348
21112
  return true;
21349
21113
  }
21350
- var gitignorePath = () => (0, import_node_path22.join)(process.cwd(), ".gitignore");
21114
+ var gitignorePath = () => (0, import_node_path21.join)(process.cwd(), ".gitignore");
21351
21115
  function readTextFile(path2) {
21352
21116
  try {
21353
- if (!(0, import_node_fs25.existsSync)(path2)) return null;
21354
- return (0, import_node_fs25.readFileSync)(path2, "utf8");
21117
+ if (!(0, import_node_fs24.existsSync)(path2)) return null;
21118
+ return (0, import_node_fs24.readFileSync)(path2, "utf8");
21355
21119
  } catch {
21356
21120
  return null;
21357
21121
  }
@@ -21360,10 +21124,10 @@ function playwrightMcpConfigSnapshots() {
21360
21124
  const cwd = process.cwd();
21361
21125
  const home = (0, import_node_os5.homedir)();
21362
21126
  const candidates = [
21363
- (0, import_node_path22.join)(cwd, ".mcp.json"),
21364
- (0, import_node_path22.join)(cwd, ".cursor", "mcp.json"),
21365
- (0, import_node_path22.join)(home, ".cursor", "mcp.json"),
21366
- (0, import_node_path22.join)(home, ".codex", "config.toml")
21127
+ (0, import_node_path21.join)(cwd, ".mcp.json"),
21128
+ (0, import_node_path21.join)(cwd, ".cursor", "mcp.json"),
21129
+ (0, import_node_path21.join)(home, ".cursor", "mcp.json"),
21130
+ (0, import_node_path21.join)(home, ".codex", "config.toml")
21367
21131
  ];
21368
21132
  const out = [];
21369
21133
  for (const path2 of candidates) {
@@ -21376,7 +21140,7 @@ function strayBrowserArtifactPaths() {
21376
21140
  const cwd = process.cwd();
21377
21141
  return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
21378
21142
  try {
21379
- return (0, import_node_fs25.existsSync)((0, import_node_path22.join)(cwd, rel));
21143
+ return (0, import_node_fs24.existsSync)((0, import_node_path21.join)(cwd, rel));
21380
21144
  } catch {
21381
21145
  return false;
21382
21146
  }
@@ -21395,8 +21159,8 @@ function latestIso(values) {
21395
21159
  return best;
21396
21160
  }
21397
21161
  function latestNorthstarContinuityAt() {
21398
- const meta = parseMeta(readTextFile((0, import_node_path22.join)(process.cwd(), META_FILE)));
21399
- const queue = parseQueue(readTextFile((0, import_node_path22.join)(process.cwd(), QUEUE_FILE)));
21162
+ const meta = parseMeta(readTextFile((0, import_node_path21.join)(process.cwd(), META_FILE)));
21163
+ const queue = parseQueue(readTextFile((0, import_node_path21.join)(process.cwd(), QUEUE_FILE)));
21400
21164
  return latestIso([
21401
21165
  ...Object.values(meta).map((entry) => entry.syncedAt),
21402
21166
  ...queue.map((entry) => entry.queuedAt)
@@ -21410,14 +21174,14 @@ async function latestBranchWorkAt() {
21410
21174
  }
21411
21175
  function readGitignore() {
21412
21176
  try {
21413
- return (0, import_node_fs25.readFileSync)(gitignorePath(), "utf8");
21177
+ return (0, import_node_fs24.readFileSync)(gitignorePath(), "utf8");
21414
21178
  } catch {
21415
21179
  return null;
21416
21180
  }
21417
21181
  }
21418
21182
  function writeGitignore(content) {
21419
21183
  try {
21420
- (0, import_node_fs25.writeFileSync)(gitignorePath(), content, "utf8");
21184
+ (0, import_node_fs24.writeFileSync)(gitignorePath(), content, "utf8");
21421
21185
  return true;
21422
21186
  } catch {
21423
21187
  return false;
@@ -21456,7 +21220,7 @@ async function runDoctor(opts, io = consoleIo) {
21456
21220
  const semverPrefix = /^\d+\.\d+\.\d+/;
21457
21221
  const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
21458
21222
  const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
21459
- const cursorCacheStale = (0, import_node_fs25.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
21223
+ const cursorCacheStale = (0, import_node_fs24.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
21460
21224
  const healPlan = doctorHealPlan({
21461
21225
  isOrgRepo: Boolean(cfg.sagaApiUrl),
21462
21226
  surface,
@@ -21491,7 +21255,7 @@ async function runDoctor(opts, io = consoleIo) {
21491
21255
  let onPath = pathProbe;
21492
21256
  if (!onPath) {
21493
21257
  const root = process.env.CLAUDE_PLUGIN_ROOT;
21494
- if (root && (0, import_node_fs25.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
21258
+ if (root && (0, import_node_fs24.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
21495
21259
  }
21496
21260
  checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
21497
21261
  const reloadHint = reloadAction(surface);
@@ -21860,7 +21624,7 @@ async function runDoctor(opts, io = consoleIo) {
21860
21624
  isOrgRepo: Boolean(cfg.sagaApiUrl),
21861
21625
  surface,
21862
21626
  cacheRoot: cursorCacheRoot,
21863
- cacheRootExists: (0, import_node_fs25.existsSync)(cursorCacheRoot),
21627
+ cacheRootExists: (0, import_node_fs24.existsSync)(cursorCacheRoot),
21864
21628
  pins: cursorPins,
21865
21629
  hubCheckout: hubCheckoutForCursorSeed(),
21866
21630
  releasedVersion
@@ -21871,7 +21635,7 @@ async function runDoctor(opts, io = consoleIo) {
21871
21635
  releasedVersion,
21872
21636
  hubCheckout: hubCheckoutForCursorSeed(),
21873
21637
  execFileP: execFileP2,
21874
- mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0, import_node_path22.join)((0, import_node_os5.tmpdir)(), prefix)),
21638
+ mkdtemp: (prefix) => (0, import_promises7.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), prefix)),
21875
21639
  log: (m) => io.err(m)
21876
21640
  });
21877
21641
  if (seeded) {
@@ -21880,7 +21644,7 @@ async function runDoctor(opts, io = consoleIo) {
21880
21644
  isOrgRepo: Boolean(cfg.sagaApiUrl),
21881
21645
  surface,
21882
21646
  cacheRoot: cursorCacheRoot,
21883
- cacheRootExists: (0, import_node_fs25.existsSync)(cursorCacheRoot),
21647
+ cacheRootExists: (0, import_node_fs24.existsSync)(cursorCacheRoot),
21884
21648
  pins: cursorPins,
21885
21649
  hubCheckout: hubCheckoutForCursorSeed(),
21886
21650
  releasedVersion
@@ -22034,23 +21798,23 @@ function mergeGuardHook(settings) {
22034
21798
  next.hooks = hooks;
22035
21799
  return next;
22036
21800
  }
22037
- var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path22.join)((0, import_node_os5.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
21801
+ var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path21.join)((0, import_node_os5.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
22038
21802
  function ensureUserScopeGuardHook(opts = {}) {
22039
21803
  const path2 = opts.settingsPath ?? userScopeSettingsPath();
22040
21804
  try {
22041
21805
  let current = null;
22042
- if ((0, import_node_fs25.existsSync)(path2)) {
21806
+ if ((0, import_node_fs24.existsSync)(path2)) {
22043
21807
  try {
22044
- current = JSON.parse((0, import_node_fs25.readFileSync)(path2, "utf8"));
21808
+ current = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
22045
21809
  } catch {
22046
21810
  return "failed";
22047
21811
  }
22048
21812
  }
22049
21813
  if (settingsHasGuardHook(current)) return "already";
22050
21814
  const merged = mergeGuardHook(current);
22051
- (0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(path2), { recursive: true });
22052
- if ((0, import_node_fs25.existsSync)(path2)) (0, import_node_fs25.copyFileSync)(path2, `${path2}.bak`);
22053
- (0, import_node_fs25.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
21815
+ (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(path2), { recursive: true });
21816
+ if ((0, import_node_fs24.existsSync)(path2)) (0, import_node_fs24.copyFileSync)(path2, `${path2}.bak`);
21817
+ (0, import_node_fs24.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
22054
21818
  `, "utf8");
22055
21819
  return "written";
22056
21820
  } catch {
@@ -22185,7 +21949,7 @@ async function applyGcPlan(plan2, remote) {
22185
21949
  cleanupBranch: (branch, expectedHeadOid) => cleanupPrMergeLocalBranch(branch.branch, {
22186
21950
  beforeWorktrees,
22187
21951
  startingPath: branch.worktreePath,
22188
- pathExists: (p) => (0, import_node_fs26.existsSync)(p),
21952
+ pathExists: (p) => (0, import_node_fs25.existsSync)(p),
22189
21953
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
22190
21954
  teardownWorktreeStage,
22191
21955
  deferredStore,
@@ -22208,7 +21972,7 @@ async function applyGcPlan(plan2, remote) {
22208
21972
  for (const wt of plan2.worktreeDirs) {
22209
21973
  try {
22210
21974
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
22211
- realpath: (path2) => (0, import_node_fs26.realpathSync)(path2)
21975
+ realpath: (path2) => (0, import_node_fs25.realpathSync)(path2)
22212
21976
  });
22213
21977
  if (!cleanupTarget.ok) {
22214
21978
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -22288,10 +22052,10 @@ async function runRulesSync(opts, io = consoleIo) {
22288
22052
  for (const entry of fetched) {
22289
22053
  if ("error" in entry) continue;
22290
22054
  const { file, source } = entry;
22291
- const current = (0, import_node_fs26.existsSync)(file) ? await (0, import_promises8.readFile)(file, "utf8") : null;
22055
+ const current = (0, import_node_fs25.existsSync)(file) ? await (0, import_promises8.readFile)(file, "utf8") : null;
22292
22056
  if (needsUpdate(source, current)) {
22293
22057
  const slash = file.lastIndexOf("/");
22294
- if (slash > 0) (0, import_node_fs26.mkdirSync)(file.slice(0, slash), { recursive: true });
22058
+ if (slash > 0) (0, import_node_fs25.mkdirSync)(file.slice(0, slash), { recursive: true });
22295
22059
  await (0, import_promises8.writeFile)(file, normalizeEol(source), "utf8");
22296
22060
  changed++;
22297
22061
  if (!opts.quiet) io.log(`mmi-cli rules: updated ${file}`);
@@ -22311,8 +22075,8 @@ async function runRulesPurge(opts, io = consoleIo) {
22311
22075
  return true;
22312
22076
  }
22313
22077
  const report = purgeSpine({
22314
- readFile: (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null,
22315
- remove: (p) => (0, import_node_fs26.rmSync)(p, { force: true })
22078
+ readFile: (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null,
22079
+ remove: (p) => (0, import_node_fs25.rmSync)(p, { force: true })
22316
22080
  });
22317
22081
  if (!opts.quiet) {
22318
22082
  for (const f of report.removed) io.log(`mmi-cli rules: removed org-delivered ${f}`);
@@ -22329,13 +22093,13 @@ rules.command("purge").option("--quiet", "stay silent unless something changed o
22329
22093
  if (!await runRulesPurge(opts)) process.exitCode = 1;
22330
22094
  });
22331
22095
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
22332
- const path2 = (0, import_node_path23.join)(process.cwd(), ".gitignore");
22333
- const current = (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null;
22096
+ const path2 = (0, import_node_path22.join)(process.cwd(), ".gitignore");
22097
+ const current = (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null;
22334
22098
  const plan2 = planManagedGitignore(current);
22335
22099
  const drift = [...plan2.added.map((l) => `+${l}`), ...plan2.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
22336
22100
  if (opts.write) {
22337
22101
  if (plan2.changed) {
22338
- (0, import_node_fs26.writeFileSync)(path2, plan2.content, "utf8");
22102
+ (0, import_node_fs25.writeFileSync)(path2, plan2.content, "utf8");
22339
22103
  console.log(`mmi-cli rules gitignore: updated .gitignore (${drift})`);
22340
22104
  } else {
22341
22105
  console.log("mmi-cli rules gitignore: up to date");
@@ -22362,7 +22126,7 @@ async function runDocsSync(opts, io = consoleIo) {
22362
22126
  return null;
22363
22127
  }
22364
22128
  },
22365
- localContent: async (f) => (0, import_node_fs26.existsSync)(f) ? await (0, import_promises8.readFile)(f, "utf8") : null,
22129
+ localContent: async (f) => (0, import_node_fs25.existsSync)(f) ? await (0, import_promises8.readFile)(f, "utf8") : null,
22366
22130
  writeDoc: async (f, c) => {
22367
22131
  await (0, import_promises8.writeFile)(f, c, "utf8");
22368
22132
  }
@@ -22377,7 +22141,6 @@ registerSagaCommands(program2);
22377
22141
  registerHandoffCommands(program2);
22378
22142
  registerCoopCommands(program2);
22379
22143
  registerOverlordCommands(program2);
22380
- registerThrottleCommands(program2);
22381
22144
  program2.command("commands").description("print the command manifest \u2014 every subcommand + its flags (ground against this instead of guessing)").option("--json", "machine-readable JSON: { name, version, tree, index } \u2014 index is a flat list of every leaf command path").action((o) => {
22382
22145
  const manifest = buildCommandManifest(program2);
22383
22146
  consoleIo.log(o.json ? JSON.stringify(manifest, null, 2) : formatManifestHuman(manifest));
@@ -22515,7 +22278,7 @@ function runWorktreeInstall(command, cwd, quiet) {
22515
22278
  const file = isWin2 ? "cmd.exe" : bin;
22516
22279
  const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
22517
22280
  return new Promise((resolve6, reject) => {
22518
- const child = (0, import_node_child_process14.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
22281
+ const child = (0, import_node_child_process13.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
22519
22282
  const timer = setTimeout(() => {
22520
22283
  try {
22521
22284
  child.kill();
@@ -22537,7 +22300,7 @@ function runWorktreeInstall(command, cwd, quiet) {
22537
22300
  async function primaryCheckoutRoot(worktreeRoot) {
22538
22301
  try {
22539
22302
  const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
22540
- return out ? (0, import_node_path23.dirname)(out) : void 0;
22303
+ return out ? (0, import_node_path22.dirname)(out) : void 0;
22541
22304
  } catch {
22542
22305
  return void 0;
22543
22306
  }
@@ -22550,28 +22313,28 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
22550
22313
  };
22551
22314
  }
22552
22315
  function acquireWorktreeSetupLock(worktreeRoot) {
22553
- const lockPath = (0, import_node_path23.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
22316
+ const lockPath = (0, import_node_path22.join)(worktreeRoot, ".mmi", "worktree-setup.lock");
22554
22317
  const take = () => {
22555
- const fd = (0, import_node_fs26.openSync)(lockPath, "wx");
22318
+ const fd = (0, import_node_fs25.openSync)(lockPath, "wx");
22556
22319
  try {
22557
- (0, import_node_fs26.writeSync)(fd, String(Date.now()));
22320
+ (0, import_node_fs25.writeSync)(fd, String(Date.now()));
22558
22321
  } finally {
22559
- (0, import_node_fs26.closeSync)(fd);
22322
+ (0, import_node_fs25.closeSync)(fd);
22560
22323
  }
22561
22324
  return () => {
22562
22325
  try {
22563
- (0, import_node_fs26.rmSync)(lockPath, { force: true });
22326
+ (0, import_node_fs25.rmSync)(lockPath, { force: true });
22564
22327
  } catch {
22565
22328
  }
22566
22329
  };
22567
22330
  };
22568
22331
  try {
22569
- (0, import_node_fs26.mkdirSync)((0, import_node_path23.dirname)(lockPath), { recursive: true });
22332
+ (0, import_node_fs25.mkdirSync)((0, import_node_path22.dirname)(lockPath), { recursive: true });
22570
22333
  return take();
22571
22334
  } catch {
22572
22335
  try {
22573
- if (Date.now() - (0, import_node_fs26.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
22574
- (0, import_node_fs26.rmSync)(lockPath, { force: true });
22336
+ if (Date.now() - (0, import_node_fs25.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
22337
+ (0, import_node_fs25.rmSync)(lockPath, { force: true });
22575
22338
  return take();
22576
22339
  }
22577
22340
  } catch {
@@ -22710,7 +22473,7 @@ function scheduleRelatedDiscovery(o) {
22710
22473
  try {
22711
22474
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
22712
22475
  if (o.repo) args.push("--repo", o.repo);
22713
- (0, import_node_child_process14.spawn)(process.execPath, [process.argv[1], ...args], {
22476
+ (0, import_node_child_process13.spawn)(process.execPath, [process.argv[1], ...args], {
22714
22477
  detached: true,
22715
22478
  stdio: "ignore",
22716
22479
  windowsHide: true,
@@ -23060,7 +22823,7 @@ project.command("attest [owner/repo]").description("attest this repo's app-owned
23060
22823
  const res = await attestAppGaps(slugOf(target), repo, registryClientDeps(cfg));
23061
22824
  return reportWrite("project attest", res);
23062
22825
  });
23063
- project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
22826
+ project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").option("--class <class>", "deployable | content").option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (v2 capability shape)`).option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).option("--var <KEY=VALUE...>", settableVarHelp()).option("--secrets-file <path>", "read the #2244 secrets catalog map (JSON) from a file and set it as --var secrets (robust for large maps)").option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, edgeDomains, requiredGcpApis, publishRequired").option("--clear-web-profile", "remove web-only registry fields (oauth, edgeDomains) for non-web/content projects").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
23064
22827
  const cfg = await loadConfig();
23065
22828
  let target;
23066
22829
  try {
@@ -23070,6 +22833,14 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
23070
22833
  }
23071
22834
  const slug = slugOf(target);
23072
22835
  const repo = target.includes("/") ? target : `mutmutco/${slug}`;
22836
+ const vars = rawValues("--var");
22837
+ if (o.secretsFile) {
22838
+ try {
22839
+ vars.push(`secrets=${(0, import_node_fs25.readFileSync)(o.secretsFile, "utf8")}`);
22840
+ } catch (e) {
22841
+ return fail(`project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
22842
+ }
22843
+ }
23073
22844
  let patch;
23074
22845
  try {
23075
22846
  patch = buildProjectSetPatch({
@@ -23077,7 +22848,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
23077
22848
  projectType: o.projectType,
23078
22849
  deployModel: o.deployModel,
23079
22850
  releaseTrack: o.releaseTrack,
23080
- vars: rawValues("--var"),
22851
+ vars,
23081
22852
  unsets: rawValues("--unset"),
23082
22853
  clearWebProfile: Boolean(o.clearWebProfile)
23083
22854
  });
@@ -23688,9 +23459,9 @@ pr.command("create").description("create a PR and print {number,url} JSON").opti
23688
23459
  console.log(JSON.stringify(created));
23689
23460
  });
23690
23461
  async function listCiWorkflowPaths(cwd = process.cwd()) {
23691
- const wfDir = (0, import_node_path23.join)(cwd, ".github", "workflows");
23692
- if (!(0, import_node_fs26.existsSync)(wfDir)) return [];
23693
- return (0, import_node_fs26.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
23462
+ const wfDir = (0, import_node_path22.join)(cwd, ".github", "workflows");
23463
+ if (!(0, import_node_fs25.existsSync)(wfDir)) return [];
23464
+ return (0, import_node_fs25.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
23694
23465
  }
23695
23466
  async function resolveMergeCiPolicyForCheckout(repoOpt) {
23696
23467
  const repo = repoOpt ?? await resolveRepo();
@@ -23709,7 +23480,7 @@ function ciAuditDeps() {
23709
23480
  // Continuous CI delivery (#1550): the gate re-seed renders from the Hub's on-disk seed templates. The
23710
23481
  // reconcile runs IN the Hub checkout, so this is local-file I/O (no network fetch). Path is relative to
23711
23482
  // the repo root (e.g. skills/bootstrap/seeds/gate.template.yml).
23712
- readSeedFile: (path2) => (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null
23483
+ readSeedFile: (path2) => (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null
23713
23484
  };
23714
23485
  }
23715
23486
  pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
@@ -23846,7 +23617,7 @@ async function remoteBranchExists2(branch, options = {}) {
23846
23617
  }
23847
23618
  var COMPOSE_TIMEOUT_MS = 12e4;
23848
23619
  function spawnDeferredGcSweep() {
23849
- spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
23620
+ spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
23850
23621
  }
23851
23622
  async function createDeferredWorktreeStore() {
23852
23623
  try {
@@ -23862,7 +23633,7 @@ async function createDeferredWorktreeStore() {
23862
23633
  },
23863
23634
  write: async (entries) => {
23864
23635
  try {
23865
- await (0, import_promises8.mkdir)((0, import_node_path23.dirname)(registryPath), { recursive: true });
23636
+ await (0, import_promises8.mkdir)((0, import_node_path22.dirname)(registryPath), { recursive: true });
23866
23637
  await (0, import_promises8.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
23867
23638
  } catch {
23868
23639
  }
@@ -23876,13 +23647,13 @@ var realWorktreeDirRemover = {
23876
23647
  probe: (p) => {
23877
23648
  let st;
23878
23649
  try {
23879
- st = (0, import_node_fs26.lstatSync)(p);
23650
+ st = (0, import_node_fs25.lstatSync)(p);
23880
23651
  } catch {
23881
23652
  return null;
23882
23653
  }
23883
23654
  if (st.isSymbolicLink()) return "link";
23884
23655
  try {
23885
- (0, import_node_fs26.readlinkSync)(p);
23656
+ (0, import_node_fs25.readlinkSync)(p);
23886
23657
  return "link";
23887
23658
  } catch {
23888
23659
  }
@@ -23890,7 +23661,7 @@ var realWorktreeDirRemover = {
23890
23661
  },
23891
23662
  readdir: (p) => {
23892
23663
  try {
23893
- return (0, import_node_fs26.readdirSync)(p);
23664
+ return (0, import_node_fs25.readdirSync)(p);
23894
23665
  } catch {
23895
23666
  return [];
23896
23667
  }
@@ -23899,9 +23670,9 @@ var realWorktreeDirRemover = {
23899
23670
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
23900
23671
  detachLink: (p) => {
23901
23672
  try {
23902
- (0, import_node_fs26.rmdirSync)(p);
23673
+ (0, import_node_fs25.rmdirSync)(p);
23903
23674
  } catch {
23904
- (0, import_node_fs26.unlinkSync)(p);
23675
+ (0, import_node_fs25.unlinkSync)(p);
23905
23676
  }
23906
23677
  },
23907
23678
  removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
@@ -23931,9 +23702,9 @@ async function worktreeHasStageState(worktreePath) {
23931
23702
  }
23932
23703
  }
23933
23704
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
23934
- if (!(0, import_node_fs26.existsSync)(statePath)) return false;
23705
+ if (!(0, import_node_fs25.existsSync)(statePath)) return false;
23935
23706
  try {
23936
- const state = JSON.parse((0, import_node_fs26.readFileSync)(statePath, "utf8"));
23707
+ const state = JSON.parse((0, import_node_fs25.readFileSync)(statePath, "utf8"));
23937
23708
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
23938
23709
  return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
23939
23710
  } catch {
@@ -24006,7 +23777,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
24006
23777
  } : await cleanupPrMergeLocalBranch(headRef, {
24007
23778
  beforeWorktrees,
24008
23779
  startingPath,
24009
- pathExists: (p) => (0, import_node_fs26.existsSync)(p),
23780
+ pathExists: (p) => (0, import_node_fs25.existsSync)(p),
24010
23781
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
24011
23782
  teardownWorktreeStage,
24012
23783
  deferredStore,
@@ -24203,7 +23974,7 @@ function stageScopedRunOpts(o) {
24203
23974
  };
24204
23975
  }
24205
23976
  function printLine(value) {
24206
- (0, import_node_fs26.writeSync)(1, `${value}
23977
+ (0, import_node_fs25.writeSync)(1, `${value}
24207
23978
  `);
24208
23979
  }
24209
23980
  function stageKeepAlive() {
@@ -24220,8 +23991,8 @@ async function resolveStage() {
24220
23991
  local,
24221
23992
  shell: shellFor(),
24222
23993
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
24223
- hasCompose: (0, import_node_fs26.existsSync)((0, import_node_path23.join)(process.cwd(), "docker-compose.yml")),
24224
- hasEnvExample: (0, import_node_fs26.existsSync)((0, import_node_path23.join)(process.cwd(), ".env.example"))
23994
+ hasCompose: (0, import_node_fs25.existsSync)((0, import_node_path22.join)(process.cwd(), "docker-compose.yml")),
23995
+ hasEnvExample: (0, import_node_fs25.existsSync)((0, import_node_path22.join)(process.cwd(), ".env.example"))
24225
23996
  });
24226
23997
  }
24227
23998
  async function fetchStageVaultEnvMerge() {
@@ -24685,7 +24456,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
24685
24456
  client: defaultGitHubClient(),
24686
24457
  projectMeta: meta,
24687
24458
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
24688
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs26.existsSync)(path2) ? (0, import_node_fs26.readFileSync)(path2, "utf8") : null,
24459
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs25.existsSync)(path2) ? (0, import_node_fs25.readFileSync)(path2, "utf8") : null,
24689
24460
  // requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
24690
24461
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
24691
24462
  requiredGcpApis: (() => {
@@ -24728,12 +24499,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
24728
24499
  return fail(`bootstrap apply: ${e.message}`);
24729
24500
  }
24730
24501
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
24731
- if (!(0, import_node_fs26.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
24732
- const manifest = loadBootstrapSeeds((0, import_node_fs26.readFileSync)(manifestPath, "utf8"));
24502
+ if (!(0, import_node_fs25.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
24503
+ const manifest = loadBootstrapSeeds((0, import_node_fs25.readFileSync)(manifestPath, "utf8"));
24733
24504
  const baseBranch = o.class === "content" ? "main" : "development";
24734
24505
  const slug = parsedRepo.slug;
24735
24506
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
24736
- const readFile7 = (p) => (0, import_node_fs26.existsSync)(p) ? (0, import_node_fs26.readFileSync)(p, "utf8") : null;
24507
+ const readFile7 = (p) => (0, import_node_fs25.existsSync)(p) ? (0, import_node_fs25.readFileSync)(p, "utf8") : null;
24737
24508
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
24738
24509
  const rawVars = {};
24739
24510
  for (const value of rawValues("--var")) {
@@ -24980,16 +24751,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
24980
24751
  const repoClass = o.class;
24981
24752
  targets = [{ repo: o.repo, class: repoClass, releaseTrack: repoClass === "content" ? "trunk" : resolveReleaseTrack(meta, void 0, o.repo) }];
24982
24753
  } else {
24983
- const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs26.existsSync)("projects.json") ? (0, import_node_fs26.readFileSync)("projects.json", "utf8") : null;
24754
+ const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs25.existsSync)("projects.json") ? (0, import_node_fs25.readFileSync)("projects.json", "utf8") : null;
24984
24755
  if (!projectsJson) return failGraceful("access audit: no project registry \u2014 Hub API unreachable and projects.json not found; run from the MMI-Hub repo root or pass --repo <owner/repo>");
24985
- const fanoutJson = (0, import_node_fs26.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs26.readFileSync)(".github/fanout-targets.json", "utf8") : null;
24756
+ const fanoutJson = (0, import_node_fs25.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs25.readFileSync)(".github/fanout-targets.json", "utf8") : null;
24986
24757
  targets = loadAccessTargets(projectsJson, fanoutJson);
24987
24758
  }
24988
24759
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
24989
- const fileMatrix = (0, import_node_fs26.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs26.readFileSync)("access-matrix.json", "utf8")) : {};
24760
+ const fileMatrix = (0, import_node_fs25.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs25.readFileSync)("access-matrix.json", "utf8")) : {};
24990
24761
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
24991
24762
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
24992
- const fileContracts = (0, import_node_fs26.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs26.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
24763
+ const fileContracts = (0, import_node_fs25.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs25.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
24993
24764
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
24994
24765
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
24995
24766
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
@@ -25003,77 +24774,40 @@ program2.command("doctor").description("check onboarding gates and auto-heal CLI
25003
24774
  ));
25004
24775
  program2.command("guard").description("detect a pruned/unresolved MMI plugin on disk; loud one-line stderr at session start").option("--session-start", "run in user-scope SessionStart mode").action((opts) => runGuard({ sessionStart: opts.sessionStart }));
25005
24776
  program2.command("plugin-heal").description("reinstall + re-enable the MMI plugin (recover from a marketplace prune)").action(() => runPluginHeal());
25006
- program2.command("session-start").description("run the SessionStart verbs (rules sync, Jervaise-only continuity, whoami, doctor, plan-store check) in one process; docs sync runs detached").action(async () => {
24777
+ program2.command("session-start").description("run the SessionStart verbs (rules purge, whoami, board slice, doctor) in one process; docs sync runs detached").action(async () => {
25007
24778
  if (isInsideRepoSubdir(process.cwd())) {
25008
24779
  console.error("[mmi-hook] session-start: cwd is a repository SUBDIRECTORY \u2014 skipping the SessionStart hook (spine/docs/plan/saga delivery); run it from the repo root.");
25009
24780
  return;
25010
24781
  }
25011
24782
  if (!isOrgRepoRoot(process.cwd())) return;
25012
- const continuityEnabled = (await continuityAccess().catch(() => ({ allowed: false }))).allowed;
25013
- if (continuityEnabled) {
25014
- try {
25015
- const hook = parseHookInput(await readStdin());
25016
- if (hook.session_id) persistSession(hook.session_id);
25017
- } catch (e) {
25018
- console.error(`[mmi-hook] saga session failed: ${e.message}`);
25019
- }
25020
- }
25021
- spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
24783
+ spawnDetachedSelf(["docs", "sync", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
25022
24784
  spawnDeferredGcSweep();
25023
- let northstarInjected = false;
25024
- const { parallel, sequential } = buildSessionStartPlan(
25025
- {
25026
- rulesPurge: (io) => runRulesPurge({ quiet: true }, io),
25027
- sagaShow: (io) => runSagaShow({ quiet: true }, io),
25028
- handoffOffer: (io) => runHandoffOffer(io, { fast: true }),
25029
- coopPending: (io) => runCoopPendingBanner(io),
25030
- northstarContext: async (io) => {
25031
- const cfg = await loadConfig();
25032
- if (!cfg.sagaApiUrl) return;
25033
- const planDeps = makePlanDeps(cfg, io);
25034
- northstarInjected = await runNorthstarContext(io, {
25035
- loadPlans: () => scopedPlanList(planDeps),
25036
- readLocal: (slug) => planDeps.readLocal(slug),
25037
- // #1812: thread the saga HEAD's North Star anchor (its NEXT slug) into the relevance gate so
25038
- // the plan the agent is actively on is force-injected even on a generic branch with no token
25039
- // overlap. fetchSagaHead errors are swallowed via a silent io — a missing/failed HEAD just
25040
- // falls back to token-overlap scoring, never noises or blocks the banner.
25041
- gatherSignals: () => gatherRelevanceSignals({
25042
- anchorSlug: () => fetchSagaHead({ log: () => {
25043
- }, err: () => {
25044
- } }).then((h) => h?.anchor?.slug ?? void 0)
25045
- })
25046
- });
25047
- },
25048
- sagaHealth: (io) => runSagaHealth({ banner: true, quiet: true }, io),
25049
- // whoami (#879): surface the resolved human so agents act --for them without asking. Silent
25050
- // when unknown — a missing gh login must not noise or fail the banner.
25051
- whoami: async (io) => {
25052
- const report = await resolveWhoami({
25053
- hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
25054
- ghLogin: githubLogin
25055
- });
25056
- const line = whoamiLine(report);
25057
- if (line) io.log(line);
25058
- },
25059
- boardSlice: (io) => runBoardSlice(io, {
25060
- loadConfig: () => loadConfigForRepo(),
25061
- readBoard,
25062
- // #1813: warm the slice cache out-of-band (detached, like docs sync) so the ~20s live read
25063
- // never costs banner time and next session's glance renders instantly within budget.
25064
- scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
25065
- }),
25066
- doctor: (io) => runDoctor({ banner: true }, io)
24785
+ const { parallel, sequential } = buildSessionStartPlan({
24786
+ rulesPurge: (io) => runRulesPurge({ quiet: true }, io),
24787
+ // whoami (#879): surface the resolved human so agents act --for them without asking. Silent
24788
+ // when unknown a missing gh login must not noise or fail the banner.
24789
+ whoami: async (io) => {
24790
+ const report = await resolveWhoami({
24791
+ hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
24792
+ ghLogin: githubLogin
24793
+ });
24794
+ const line = whoamiLine(report);
24795
+ if (line) io.log(line);
25067
24796
  },
25068
- { continuityEnabled }
25069
- );
24797
+ boardSlice: (io) => runBoardSlice(io, {
24798
+ loadConfig: () => loadConfigForRepo(),
24799
+ readBoard,
24800
+ // #1813: warm the slice cache out-of-band (detached, like docs sync) so the ~20s live read
24801
+ // never costs banner time and next session's glance renders instantly within budget.
24802
+ scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
24803
+ }),
24804
+ doctor: (io) => runDoctor({ banner: true }, io)
24805
+ });
25070
24806
  await runSessionStart(parallel, sequential, consoleIo);
25071
- for (const line of sessionStartContinuityLines({ continuityEnabled, northstarInjected, cwd: process.cwd() })) consoleIo.log(line);
25072
- if (continuityEnabled) await runPlanAutosave(consoleIo, { quiet: true }).catch(() => void 0);
25073
24807
  for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
25074
24808
  const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
25075
24809
  if (worktreeBanner) {
25076
- spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
24810
+ spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process13.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
25077
24811
  consoleIo.log(worktreeBanner);
25078
24812
  }
25079
24813
  });