@mutmutco/cli 3.96.0 → 3.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +431 -113
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -5235,7 +5235,7 @@ function deferredWorktreesRegistryPath(gitDir) {
5235
5235
  return `${base}/mmi-deferred-worktrees.json`;
5236
5236
  }
5237
5237
  function parseDeferredWorktreesFile(text) {
5238
- const parsed = JSON.parse(text);
5238
+ const parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
5239
5239
  if (!parsed || !Array.isArray(parsed.entries)) return [];
5240
5240
  return parsed.entries.filter((e) => Boolean(e) && typeof e === "object" && typeof e.path === "string" && typeof e.branch === "string" && e.reason === "lock-held").map((e) => ({ ...e, registeredAt: e.registeredAt || (/* @__PURE__ */ new Date(0)).toISOString() }));
5241
5241
  }
@@ -6999,7 +6999,7 @@ function commandLadderHint() {
6999
6999
  }
7000
7000
 
7001
7001
  // src/index.ts
7002
- var import_node_path38 = require("node:path");
7002
+ var import_node_path37 = require("node:path");
7003
7003
 
7004
7004
  // src/merge-ci-policy.ts
7005
7005
  function resolveMergeCiPolicy(input) {
@@ -8788,6 +8788,9 @@ function detectSurface(env) {
8788
8788
  if (env.MMI_AGENT_SURFACE === "codex" || has("CODEX_THREAD_ID") || has("CODEX_MANAGED_BY_NPM") || has("CODEX_MANAGED_PACKAGE_ROOT") || (env.CLAUDE_PLUGIN_ROOT ?? "").includes(".codex")) {
8789
8789
  return "codex";
8790
8790
  }
8791
+ if (env.MMI_AGENT_SURFACE === "jervcode" || has("PI_SESSION_ID")) {
8792
+ return "jervcode";
8793
+ }
8791
8794
  if (env.MMI_AGENT_SURFACE === "kimi" || has("KIMI_PLUGIN_ROOT") || has("KIMI_CODE_HOME")) {
8792
8795
  return "kimi";
8793
8796
  }
@@ -8817,6 +8820,8 @@ function surfaceToken(surface) {
8817
8820
  return "kilo";
8818
8821
  case "cursor":
8819
8822
  return "cursor";
8823
+ case "jervcode":
8824
+ return "jervcode";
8820
8825
  case "opencode":
8821
8826
  return "opencode";
8822
8827
  case "shell":
@@ -8838,6 +8843,8 @@ function reloadAction(surface) {
8838
8843
  return "restart OpenCode";
8839
8844
  case "cursor":
8840
8845
  return "reload the Cursor window";
8846
+ case "jervcode":
8847
+ return "restart JervCode (the package set is read at launch)";
8841
8848
  case "claude-cli":
8842
8849
  case "shell":
8843
8850
  default:
@@ -8905,7 +8912,7 @@ function nonClaudeSurfaceHealMessage(surface) {
8905
8912
  if (surface === "cursor") {
8906
8913
  return "Cursor ships an MMI plugin (#3920). Install or repair its managed local checkout with:\n mmi-cli plugin heal\n Then reload the Cursor window. For one-off CLI use, pass --plugin-dir <MMI-Hub checkout>.\n Update the CLI too: npm i -g @mutmutco/cli";
8907
8914
  }
8908
- return "No Hub-shipped MMI plugin on this host \u2014 the Hub ships plugins for Claude, Codex, Kimi, Cursor, and Kilo only.\n Update the CLI instead: npm i -g @mutmutco/cli (org rules ride an AGENTS.md authored outside the Hub)";
8915
+ return "No Hub-shipped MMI plugin on this host \u2014 the Hub ships plugins for Claude, Codex, Kimi, Cursor, Kilo, and JervCode only.\n Update the CLI instead: npm i -g @mutmutco/cli (org rules ride an AGENTS.md authored outside the Hub)";
8909
8916
  }
8910
8917
  function healStepAborts(step, ok) {
8911
8918
  return !ok && step.gated;
@@ -8953,6 +8960,7 @@ function surfaceConfigRoot(surface, env = process.env, home = (0, import_node_os
8953
8960
  if (surface === "kimi") return env.KIMI_CODE_HOME?.trim() || (0, import_node_path14.join)(home, ".kimi-code");
8954
8961
  if (surface === "kilo") return env.KILO_CONFIG_DIR?.trim() || (0, import_node_path14.join)(home, ".config", "kilo");
8955
8962
  if (surface === "cursor") return (0, import_node_path14.join)(home, ".cursor");
8963
+ if (surface === "jervcode") return env.PI_CODING_AGENT_DIR?.trim() || (0, import_node_path14.join)(home, ".pi", "agent");
8956
8964
  return (0, import_node_path14.join)(home, ".claude");
8957
8965
  }
8958
8966
  var installedPluginsPath = (surface = detectSurface(process.env)) => {
@@ -8976,6 +8984,7 @@ function marketplaceCloneCandidates(surface, home, env = process.env) {
8976
8984
  if (surface === "kimi") return [];
8977
8985
  if (surface === "kilo") return [];
8978
8986
  if (surface === "cursor") return [];
8987
+ if (surface === "jervcode") return [];
8979
8988
  return [(0, import_node_path14.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
8980
8989
  }
8981
8990
  function marketplaceClonePresent(surface, home, exists = import_node_fs15.existsSync, env = process.env) {
@@ -9104,22 +9113,170 @@ function kimiPluginTreeHealthy(root, exists = import_node_fs15.existsSync) {
9104
9113
  "scripts/hook-run.mjs"
9105
9114
  ].every((path2) => exists((0, import_node_path14.join)(root, ...path2.split("/"))));
9106
9115
  }
9116
+ var JERVCODE_WRAPPER_DIR = ".pi-plugin";
9117
+ function normalizePiEntry(value) {
9118
+ return decodeURIComponent(value.replace(/^file:\/\/\/?/, "")).replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
9119
+ }
9120
+ function piEntryFsPath(entry) {
9121
+ return decodeURIComponent(entry.replace(/^file:\/\/\/?/, ""));
9122
+ }
9123
+ function samePiEntry(entry, packagePath) {
9124
+ return typeof entry === "string" && normalizePiEntry(entry) === normalizePiEntry(packagePath);
9125
+ }
9126
+ function jervcodePackageFamily(entry) {
9127
+ if (typeof entry !== "string") return null;
9128
+ const parts = normalizePiEntry(entry).split("/");
9129
+ if (parts.length < 3 || parts[parts.length - 1] !== JERVCODE_WRAPPER_DIR) return null;
9130
+ if (!/^\d+\.\d+\.\d+$/.test(parts[parts.length - 2])) return null;
9131
+ parts.splice(parts.length - 2, 1);
9132
+ return parts.join("/");
9133
+ }
9134
+ function isMmiOwnedPiEntry(entry) {
9135
+ if (typeof entry !== "string") return false;
9136
+ try {
9137
+ const pkg = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path14.join)(piEntryFsPath(entry), "package.json"), "utf8"));
9138
+ return pkg.name === "mmi";
9139
+ } catch {
9140
+ return false;
9141
+ }
9142
+ }
9143
+ function mergeMmiPiPackageEntries(entries, packagePath) {
9144
+ const family = jervcodePackageFamily(packagePath);
9145
+ const others = entries.filter((entry) => {
9146
+ if (samePiEntry(entry, packagePath)) return false;
9147
+ if (family === null || jervcodePackageFamily(entry) !== family) return true;
9148
+ return !isMmiOwnedPiEntry(entry);
9149
+ });
9150
+ const already = entries.some((entry) => samePiEntry(entry, packagePath));
9151
+ const superseded = entries.length - others.length - (already ? 1 : 0);
9152
+ return {
9153
+ next: [...others, packagePath],
9154
+ superseded,
9155
+ changed: !(already && others.length === entries.length - 1)
9156
+ };
9157
+ }
9158
+ function readPiSettings(path2) {
9159
+ if (!(0, import_node_fs15.existsSync)(path2)) return void 0;
9160
+ try {
9161
+ const parsed = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
9162
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
9163
+ return parsed;
9164
+ } catch {
9165
+ return null;
9166
+ }
9167
+ }
9168
+ function mmiPiWrapperEntry(env = process.env, home = (0, import_node_os4.homedir)()) {
9169
+ const settings = readPiSettings((0, import_node_path14.join)(surfaceConfigRoot("jervcode", env, home), "settings.json"));
9170
+ const entries = Array.isArray(settings?.packages) ? settings.packages : [];
9171
+ for (const entry of entries) {
9172
+ if (typeof entry !== "string") continue;
9173
+ if (jervcodePackageFamily(entry)?.endsWith("/mutmutco/mmi/" + JERVCODE_WRAPPER_DIR) || isMmiOwnedPiEntry(entry)) {
9174
+ return entry;
9175
+ }
9176
+ }
9177
+ return null;
9178
+ }
9179
+ function mmiPiWrapperHealthy(entry) {
9180
+ if (!entry) return false;
9181
+ const wrapper = piEntryFsPath(entry);
9182
+ return isMmiOwnedPiEntry(entry) && (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_path14.dirname)(wrapper), "skills", "mmi", "SKILL.md"));
9183
+ }
9184
+ function findMmiPiSourceClone(home = (0, import_node_os4.homedir)()) {
9185
+ const cacheRoot = (0, import_node_path14.join)(home, ".claude", "plugins", "cache", "mutmutco", "mmi");
9186
+ let best = null;
9187
+ try {
9188
+ for (const entry of (0, import_node_fs15.readdirSync)(cacheRoot, { withFileTypes: true })) {
9189
+ if (!entry.isDirectory() || !/^\d+\.\d+\.\d+$/.test(entry.name)) continue;
9190
+ if (!(0, import_node_fs15.existsSync)((0, import_node_path14.join)(cacheRoot, entry.name, JERVCODE_WRAPPER_DIR, "package.json"))) continue;
9191
+ if (!best || compareVersions(entry.name, best.version) > 0) {
9192
+ best = { path: (0, import_node_path14.join)(cacheRoot, entry.name), version: entry.name };
9193
+ }
9194
+ }
9195
+ } catch {
9196
+ return null;
9197
+ }
9198
+ return best;
9199
+ }
9200
+ function healJervCodePackageRegistration(opts = {}) {
9201
+ const env = opts.env ?? process.env;
9202
+ if (env.PI_SESSION_ID?.trim()) {
9203
+ return {
9204
+ available: true,
9205
+ ok: true,
9206
+ changed: false,
9207
+ version: null,
9208
+ detail: "skipped \u2014 live JervCode/Pi seat (PI_SESSION_ID set); the package set is read at launch, so this registration is deferred to an out-of-session doctor run"
9209
+ };
9210
+ }
9211
+ const home = opts.home ?? (0, import_node_os4.homedir)();
9212
+ const agentDir = surfaceConfigRoot("jervcode", env, home);
9213
+ if (!(0, import_node_fs15.existsSync)(agentDir)) {
9214
+ return { available: false, ok: true, changed: false, version: null, detail: "skipped \u2014 no Pi/JervCode install (no agent config dir)" };
9215
+ }
9216
+ const clone = opts.clone === void 0 ? findMmiPiSourceClone(home) : opts.clone;
9217
+ if (!clone) {
9218
+ return { available: true, ok: true, changed: false, version: null, detail: "skipped \u2014 no installed MMI clone carries the pi wrapper (install the Claude plugin first)" };
9219
+ }
9220
+ const packagePath = `${clone.path.replace(/\\/g, "/").replace(/\/+$/, "")}/${JERVCODE_WRAPPER_DIR}`;
9221
+ const settingsPath = (0, import_node_path14.join)(agentDir, "settings.json");
9222
+ const settings = readPiSettings(settingsPath);
9223
+ if (settings === null) {
9224
+ return {
9225
+ available: true,
9226
+ ok: false,
9227
+ changed: false,
9228
+ version: clone.version,
9229
+ detail: `package NOT registered \u2014 ${settingsPath} is not a strict JSON object, so mmi left it alone; add ${packagePath} to its packages array by hand`
9230
+ };
9231
+ }
9232
+ const current = settings ?? {};
9233
+ const entries = Array.isArray(current.packages) ? current.packages : [];
9234
+ const merged = mergeMmiPiPackageEntries(entries, packagePath);
9235
+ if (!merged.changed) {
9236
+ return { available: true, ok: true, changed: false, version: clone.version, detail: `holds ${clone.version}` };
9237
+ }
9238
+ current.packages = merged.next;
9239
+ try {
9240
+ (0, import_node_fs15.mkdirSync)((0, import_node_path14.dirname)(settingsPath), { recursive: true });
9241
+ const tmp = `${settingsPath}.tmp-${process.pid}`;
9242
+ (0, import_node_fs15.writeFileSync)(tmp, `${JSON.stringify(current, null, 2)}
9243
+ `, "utf8");
9244
+ try {
9245
+ (0, import_node_fs15.renameSync)(tmp, settingsPath);
9246
+ } catch (renameError) {
9247
+ (0, import_node_fs15.rmSync)(tmp, { force: true });
9248
+ throw renameError;
9249
+ }
9250
+ } catch (error) {
9251
+ return { available: true, ok: false, changed: false, version: clone.version, detail: `package NOT registered \u2014 ${error.message}` };
9252
+ }
9253
+ const beside = `package registered in settings.json (${merged.next.length - 1} other package(s) untouched)`;
9254
+ return {
9255
+ available: true,
9256
+ ok: true,
9257
+ changed: true,
9258
+ version: clone.version,
9259
+ detail: `${clone.version}: ${merged.superseded > 0 ? `${beside}, superseding ${merged.superseded} earlier mmi version(s)` : beside}`
9260
+ };
9261
+ }
9107
9262
  function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
9108
9263
  const root = surfaceConfigRoot(surface);
9109
9264
  const installed = readInstalledPlugins(surface);
9110
9265
  const codexStatus = surface === "codex" ? codexPluginStatus() : void 0;
9266
+ const piEntry = surface === "jervcode" ? mmiPiWrapperEntry() : null;
9111
9267
  return {
9112
9268
  isOrgRepo,
9113
9269
  installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()) || // Kimi's managed plugin directory is its native install record; it has no Claude-style ledger.
9114
9270
  surface === "kimi" && (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) || // kilo-p1: the install record is the config file itself.
9115
- surface === "kilo" && kiloConfigListsPlugin(root) || surface === "cursor" && (0, import_node_fs15.existsSync)(cursorLocalPluginRoot()),
9271
+ surface === "kilo" && kiloConfigListsPlugin(root) || // #4188: jervcode's install record is the settings-file packages[] entry itself.
9272
+ surface === "jervcode" && piEntry !== null || surface === "cursor" && (0, import_node_fs15.existsSync)(cursorLocalPluginRoot()),
9116
9273
  // Kilo has no marketplace to clone — the config file IS the install record, so this dimension of
9117
- // the shared guard table is vacuously satisfied.
9118
- marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" ? true : marketplaceClonePresent(surface, (0, import_node_os4.homedir)()),
9274
+ // the shared guard table is vacuously satisfied. Same for jervcode's settings entry.
9275
+ marketplaceClonePresent: surface === "kimi" || surface === "kilo" || surface === "cursor" || surface === "jervcode" ? true : marketplaceClonePresent(surface, (0, import_node_os4.homedir)()),
9119
9276
  // Kimi keeps no plugin cache dir — installs are copied to plugins/managed/<id> and run from there.
9120
9277
  // Kilo (kilo-p1) keeps no cache dir either: the plugin's server() provisions ~/.kilo behind the
9121
9278
  // version stamp, so the stamp's presence is the cache signal.
9122
- pluginCachePresent: surface === "kilo" ? (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_os4.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
9279
+ pluginCachePresent: surface === "jervcode" ? mmiPiWrapperHealthy(piEntry) : surface === "kilo" ? (0, import_node_fs15.existsSync)((0, import_node_path14.join)((0, import_node_os4.homedir)(), ".kilo", ".mmi-kilo-version")) : surface === "kimi" ? kimiPluginTreeHealthy((0, import_node_path14.join)(root, "plugins", "managed", "mmi")) : surface === "cursor" ? cursorPluginTreeHealthy(cursorLocalPluginRoot()) : surface === "codex" ? Boolean(
9123
9280
  codexStatus?.installed && codexStatus.enabled && codexStatus.version && (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "cache", CODEX_MARKETPLACE, "mmi", codexStatus.version))
9124
9281
  ) : (0, import_node_fs15.existsSync)((0, import_node_path14.join)(root, "plugins", "cache", "mutmutco", "mmi"))
9125
9282
  };
@@ -9298,13 +9455,26 @@ async function healClaudePluginForDoctor(surface = detectSurface(process.env), o
9298
9455
  detail: ok ? `marketplace remove \u2192 add \u2192 install succeeded${pinNote ? `; ${pinNote}` : ""}` : `\`claude plugin\` reinstall failed or was skipped${steps.length ? ` (${steps[steps.length - 1]})` : ""}`
9299
9456
  };
9300
9457
  }
9458
+ async function healJervCodeForDoctor() {
9459
+ const heal = healJervCodePackageRegistration();
9460
+ if (!heal.ok) return { ok: false, detail: heal.detail };
9461
+ const snapshot = snapshotPluginGuardInput("jervcode", true);
9462
+ const state = buildPluginGuardDecision(snapshot).state;
9463
+ const verification = `record=${snapshot.installRecordPresent ? "yes" : "no"}, payload=${snapshot.pluginCachePresent ? "yes" : "no"}`;
9464
+ if (!heal.changed && heal.detail.startsWith("skipped")) return { ok: true, detail: heal.detail };
9465
+ return {
9466
+ ok: state === "healthy",
9467
+ detail: state === "healthy" ? `${heal.detail}; full guard verified (${verification})` : `pi package registration did not verify (${verification}); ${heal.detail}`
9468
+ };
9469
+ }
9301
9470
  async function healActivePluginForDoctor(surface = detectSurface(process.env), onStep) {
9302
9471
  const token = surfaceToken(surface);
9303
- if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
9472
+ if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
9304
9473
  return { ok: false, detail: `not a supported plugin surface (${surface})` };
9305
9474
  }
9306
9475
  if (token === "claude") return healClaudePluginForDoctor(surface, onStep);
9307
9476
  if (token === "cursor") return installCursorPluginCheckout();
9477
+ if (token === "jervcode") return healJervCodeForDoctor();
9308
9478
  const steps = [];
9309
9479
  const applied = await applyPluginHeal(surface, (msg) => {
9310
9480
  const line = msg.trim();
@@ -9421,7 +9591,7 @@ async function runGuard(readOrigin) {
9421
9591
  const surface = detectSurface(process.env);
9422
9592
  try {
9423
9593
  const token = surfaceToken(surface);
9424
- if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
9594
+ if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
9425
9595
  process.exitCode = 0;
9426
9596
  return;
9427
9597
  }
@@ -9441,8 +9611,8 @@ async function runGuard(readOrigin) {
9441
9611
  if (surfaceToken(surface) === "codex") {
9442
9612
  console.error("[mmi-guard] Could not inspect the active Codex plugin; run `mmi-cli plugin heal`.");
9443
9613
  process.exitCode = 1;
9444
- } else if (surfaceToken(surface) === "kilo" || surfaceToken(surface) === "cursor") {
9445
- const host = surfaceToken(surface) === "cursor" ? "Cursor" : "Kilo";
9614
+ } else if (surfaceToken(surface) === "kilo" || surfaceToken(surface) === "cursor" || surfaceToken(surface) === "jervcode") {
9615
+ const host = surfaceToken(surface) === "cursor" ? "Cursor" : surfaceToken(surface) === "jervcode" ? "JervCode" : "Kilo";
9446
9616
  console.error(`[mmi-guard] Could not inspect the active ${host} plugin; run \`mmi-cli plugin heal\`.`);
9447
9617
  process.exitCode = 1;
9448
9618
  } else {
@@ -9452,10 +9622,21 @@ async function runGuard(readOrigin) {
9452
9622
  }
9453
9623
  async function runPluginHeal(surface = detectSurface(process.env)) {
9454
9624
  const token = surfaceToken(surface);
9455
- if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo") {
9625
+ if (token !== "claude" && token !== "codex" && token !== "cursor" && token !== "kilo" && token !== "jervcode") {
9456
9626
  console.log(nonClaudeSurfaceHealMessage(token ?? void 0));
9457
9627
  return;
9458
9628
  }
9629
+ if (token === "jervcode") {
9630
+ const result = await healJervCodeForDoctor();
9631
+ console.log(` \u21BB ${result.detail}`);
9632
+ if (result.ok) {
9633
+ console.log(` \u2713 MMI pi package registered \u2014 ${reloadAction(surface)} to load MMI skills.`);
9634
+ } else {
9635
+ process.exitCode = 1;
9636
+ console.log(" \u2717 Auto-heal failed or was skipped. Install the Claude MMI plugin first (it owns the referenced clone), then rerun `mmi-cli plugin heal`.");
9637
+ }
9638
+ return;
9639
+ }
9459
9640
  const descriptor = PLUGIN_SURFACE_HEAL[token];
9460
9641
  const cursorResult = token === "cursor" ? await installCursorPluginCheckout() : void 0;
9461
9642
  if (cursorResult) console.log(` \u21BB ${cursorResult.detail}`);
@@ -17227,7 +17408,7 @@ function applyChecklistCheck(body, query, checked) {
17227
17408
  // src/command-taxonomy.ts
17228
17409
  var COMMAND_METADATA = /* @__PURE__ */ Symbol.for("mmi.commandTaxonomy.metadata");
17229
17410
  var PRIMARY_GROUPS = [
17230
- ["Orient", ["onboard", "status", "next", "doctor", "whoami", "commands", "explain"]],
17411
+ ["Orient", ["onboard", "status", "next", "find", "doctor", "whoami", "commands", "explain"]],
17231
17412
  ["Plan and work", ["board", "issue", "worktree", "stage"]],
17232
17413
  ["Review and ship", ["pr", "ci", "rcand", "release", "hotfix", "train"]],
17233
17414
  // `tests` sits beside `docs` deliberately: both are deterministic, repo-local gates a workflow
@@ -17237,7 +17418,7 @@ var PRIMARY_GROUPS = [
17237
17418
  ["Coordinate and improve", ["wave", "report", "skill-lesson"]]
17238
17419
  ];
17239
17420
  var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
17240
- var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "tests", "spawn", "wave", "report", "skill-lesson"]);
17421
+ var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "find", "tests", "spawn", "wave", "report", "skill-lesson"]);
17241
17422
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
17242
17423
  var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
17243
17424
  var topLevelPosition = 0;
@@ -17248,6 +17429,10 @@ for (const [helpGroup, names] of PRIMARY_GROUPS) {
17248
17429
  HELP_GROUP_ORDER.set("Operations", HELP_GROUP_ORDER.size);
17249
17430
  for (const name of OPERATIONAL_TOP_LEVEL) TOP_LEVEL_ORDER.set(name, topLevelPosition++);
17250
17431
  var PATH_OVERRIDES = {
17432
+ // #4184: the estate-search door reads as Orient, while rebuild/publish/gc/sync-estate stay
17433
+ // Setup and support — that split is the point.
17434
+ "repo-index search": { category: "support", discovery: "primary", help_group: "Orient" },
17435
+ "repo-index status": { category: "support", discovery: "primary", help_group: "Orient" },
17251
17436
  "secrets find": { category: "admin", discovery: "all-only", help_group: "Operations" },
17252
17437
  "secrets catalog": { category: "admin", discovery: "all-only", help_group: "Operations" },
17253
17438
  "secrets doctor": { category: "admin", discovery: "all-only", help_group: "Operations" },
@@ -17277,6 +17462,7 @@ var COMMAND_OWNERSHIP = {
17277
17462
  secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
17278
17463
  docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
17279
17464
  "repo-index": { module_owner: "cli/src/repo-index.ts", consumer: "agent-session" },
17465
+ find: { module_owner: "cli/src/repo-index.ts", consumer: "agent-session" },
17280
17466
  tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
17281
17467
  spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
17282
17468
  wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
@@ -19825,17 +20011,56 @@ async function syncEstateRepoIndex(opts) {
19825
20011
 
19826
20012
  // src/repo-index-health.ts
19827
20013
  var import_node_fs24 = require("node:fs");
19828
- var import_node_path22 = require("node:path");
19829
- function defaultGoldenPath(repoRoot2) {
19830
- return (0, import_node_path22.join)(repoRoot2, "cli", "testdata", "repo-index-golden-queries.json");
19831
- }
19832
- function loadGoldenSuite(path2) {
19833
- const raw = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
20014
+
20015
+ // testdata/repo-index-golden-queries.json
20016
+ var repo_index_golden_queries_default = {
20017
+ schema: 1,
20018
+ staleThresholdHours: 12,
20019
+ hubRepo: "mutmutco/MMI-Hub",
20020
+ hubEmbMinCoverage: 0.5,
20021
+ maxSyncEmbeds: 100,
20022
+ queries: [
20023
+ {
20024
+ id: "rebuild-symbol-lexical",
20025
+ q: "rebuildRepoIndex",
20026
+ mode: "lexical",
20027
+ expect: {
20028
+ topRepos: ["mutmutco/MMI-Hub"],
20029
+ topPaths: ["cli/src/repo-index.ts"],
20030
+ minScore: 0.1
20031
+ }
20032
+ },
20033
+ {
20034
+ id: "schedules-register-semantic",
20035
+ q: "schedules register",
20036
+ mode: "semantic",
20037
+ expect: {
20038
+ topRepos: ["mutmutco/MMI-Hub"],
20039
+ minScore: 0.05
20040
+ }
20041
+ }
20042
+ ]
20043
+ };
20044
+
20045
+ // src/repo-index-health.ts
20046
+ function assertGoldenSuite(raw, source) {
19834
20047
  if (!raw || raw.schema !== 1 || !Array.isArray(raw.queries)) {
19835
- throw new Error(`invalid golden suite at ${path2}`);
20048
+ throw new Error(`invalid golden suite at ${source}`);
19836
20049
  }
19837
20050
  return raw;
19838
20051
  }
20052
+ function loadGoldenSuite(path2) {
20053
+ let text;
20054
+ try {
20055
+ text = (0, import_node_fs24.readFileSync)(path2, "utf8");
20056
+ } catch (e) {
20057
+ throw new Error(`golden suite unreadable at ${path2}: ${e.message}`);
20058
+ }
20059
+ return assertGoldenSuite(JSON.parse(text), path2);
20060
+ }
20061
+ function defaultGoldenSuite() {
20062
+ return assertGoldenSuite(repo_index_golden_queries_default, "bundled repo-index-golden-queries.json");
20063
+ }
19839
20064
  function evaluateQueryHits(query, hits) {
19840
20065
  if (!hits.length) {
19841
20066
  return { ok: false, code: "empty-hits", detail: `${query.id}: no hits for ${JSON.stringify(query.q)}` };
@@ -19972,7 +20197,7 @@ async function runRepoIndexHealth(opts) {
19972
20197
  // src/spawn-policy-core.ts
19973
20198
  var import_node_child_process13 = require("node:child_process");
19974
20199
  var import_node_fs25 = require("node:fs");
19975
- var import_node_path23 = require("node:path");
20200
+ var import_node_path22 = require("node:path");
19976
20201
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
19977
20202
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
19978
20203
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -20058,7 +20283,7 @@ function runSpawnPolicy(root) {
20058
20283
  for (const file of files) {
20059
20284
  let raw;
20060
20285
  try {
20061
- raw = (0, import_node_fs25.readFileSync)((0, import_node_path23.join)(root, file), "utf8");
20286
+ raw = (0, import_node_fs25.readFileSync)((0, import_node_path22.join)(root, file), "utf8");
20062
20287
  } catch {
20063
20288
  continue;
20064
20289
  }
@@ -20077,7 +20302,7 @@ function runSpawnPolicy(root) {
20077
20302
  // src/test-policy-core.ts
20078
20303
  var import_node_child_process14 = require("node:child_process");
20079
20304
  var import_node_fs26 = require("node:fs");
20080
- var import_node_path24 = require("node:path");
20305
+ var import_node_path23 = require("node:path");
20081
20306
  var POLICY_FILE = "test-policy.json";
20082
20307
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
20083
20308
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -20130,7 +20355,7 @@ function isTestPath(path2) {
20130
20355
  return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
20131
20356
  }
20132
20357
  function loadPolicy(root, readFile9 = readFileOrNull2) {
20133
- const raw = readFile9((0, import_node_path24.join)(root, POLICY_FILE));
20358
+ const raw = readFile9((0, import_node_path23.join)(root, POLICY_FILE));
20134
20359
  if (raw == null) return { mandatory: [], declared: false };
20135
20360
  try {
20136
20361
  return { ...JSON.parse(raw), declared: true };
@@ -20168,11 +20393,11 @@ function classify(changed, policy, present = () => false) {
20168
20393
  return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
20169
20394
  }
20170
20395
  function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
20171
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path24.join)(root, p)));
20396
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path23.join)(root, p)));
20172
20397
  }
20173
20398
  function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
20174
20399
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
20175
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path24.join)(root, p)));
20400
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path23.join)(root, p)));
20176
20401
  }
20177
20402
  function evaluate(changed, policy, present = () => false) {
20178
20403
  const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -20360,7 +20585,7 @@ function runTestPolicy(root, deps = {}) {
20360
20585
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
20361
20586
  const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
20362
20587
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
20363
- const present = (path2) => exists((0, import_node_path24.join)(root, path2));
20588
+ const present = (path2) => exists((0, import_node_path23.join)(root, path2));
20364
20589
  const removedByThisDiff = removedPaths(changed);
20365
20590
  const staleFindings = [];
20366
20591
  const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
@@ -20517,7 +20742,7 @@ function docsAuditStatus(fetch2, opts) {
20517
20742
 
20518
20743
  // src/project-info-sync.ts
20519
20744
  var import_node_fs27 = require("node:fs");
20520
- var import_node_path25 = require("node:path");
20745
+ var import_node_path24 = require("node:path");
20521
20746
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
20522
20747
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
20523
20748
  projectV2 { id }
@@ -20562,7 +20787,7 @@ function sharedName(entries, fallback) {
20562
20787
  }
20563
20788
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
20564
20789
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
20565
- const readmePath = (0, import_node_path25.join)(repoRoot2, "README.md");
20790
+ const readmePath = (0, import_node_path24.join)(repoRoot2, "README.md");
20566
20791
  if (!(0, import_node_fs27.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
20567
20792
  const entries = entriesFor(project2, projects);
20568
20793
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
@@ -20588,8 +20813,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
20588
20813
  const targetBase = `https://github.com/${targetRepo2}`;
20589
20814
  const targetBranch = branchFor(targetRepo2, projects);
20590
20815
  const orgDocs = [
20591
- (0, import_node_fs27.existsSync)((0, import_node_path25.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
20592
- (0, import_node_fs27.existsSync)((0, import_node_path25.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
20816
+ (0, import_node_fs27.existsSync)((0, import_node_path24.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
20817
+ (0, import_node_fs27.existsSync)((0, import_node_path24.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
20593
20818
  ].filter(Boolean);
20594
20819
  if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
20595
20820
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
@@ -21449,7 +21674,7 @@ function writeError(res) {
21449
21674
 
21450
21675
  // src/secrets-commands.ts
21451
21676
  var import_node_fs28 = require("node:fs");
21452
- var import_node_path26 = require("node:path");
21677
+ var import_node_path25 = require("node:path");
21453
21678
  var import_node_os10 = require("node:os");
21454
21679
 
21455
21680
  // src/project-runtime.ts
@@ -21573,11 +21798,11 @@ function collectMap(value, previous = []) {
21573
21798
  return [...previous, value];
21574
21799
  }
21575
21800
  async function decryptRailsCredentials(input) {
21576
- const appDir = (0, import_node_path26.resolve)(input.appDir ?? process.cwd());
21801
+ const appDir = (0, import_node_path25.resolve)(input.appDir ?? process.cwd());
21577
21802
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
21578
21803
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
21579
- const credentialsPath = (0, import_node_path26.resolve)(appDir, credentialsFile);
21580
- const masterKeyPath = (0, import_node_path26.resolve)(appDir, masterKeyFile);
21804
+ const credentialsPath = (0, import_node_path25.resolve)(appDir, credentialsFile);
21805
+ const masterKeyPath = (0, import_node_path25.resolve)(appDir, masterKeyFile);
21581
21806
  const env = {
21582
21807
  ...process.env,
21583
21808
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
@@ -21594,8 +21819,8 @@ async function decryptRailsCredentials(input) {
21594
21819
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
21595
21820
  "puts JSON.generate(config.config)"
21596
21821
  ].join("\n");
21597
- const scriptDir = (0, import_node_fs28.mkdtempSync)((0, import_node_path26.join)((0, import_node_os10.tmpdir)(), "mmi-rails-decrypt-"));
21598
- const scriptPath = (0, import_node_path26.join)(scriptDir, "decrypt.rb");
21822
+ const scriptDir = (0, import_node_fs28.mkdtempSync)((0, import_node_path25.join)((0, import_node_os10.tmpdir)(), "mmi-rails-decrypt-"));
21823
+ const scriptPath = (0, import_node_path25.join)(scriptDir, "decrypt.rb");
21599
21824
  (0, import_node_fs28.writeFileSync)(scriptPath, script, "utf8");
21600
21825
  try {
21601
21826
  const args = ["exec", "ruby", scriptPath];
@@ -21698,7 +21923,7 @@ function registerSecretsCommands(program3) {
21698
21923
  let body;
21699
21924
  if (o.file) {
21700
21925
  try {
21701
- body = (0, import_node_fs28.readFileSync)((0, import_node_path26.resolve)(o.file), "utf8");
21926
+ body = (0, import_node_fs28.readFileSync)((0, import_node_path25.resolve)(o.file), "utf8");
21702
21927
  } catch (e) {
21703
21928
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
21704
21929
  }
@@ -21803,7 +22028,7 @@ function registerSecretsCommands(program3) {
21803
22028
  {
21804
22029
  ...d,
21805
22030
  decryptRailsCredentials,
21806
- removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0, import_node_path26.resolve)(o.appDir ?? process.cwd(), path2))
22031
+ removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0, import_node_path25.resolve)(o.appDir ?? process.cwd(), path2))
21807
22032
  },
21808
22033
  {
21809
22034
  repo: o.repo,
@@ -22859,7 +23084,7 @@ function registerSchedulesCommands(program3) {
22859
23084
 
22860
23085
  // src/file-lock.ts
22861
23086
  var import_promises5 = require("node:fs/promises");
22862
- var import_node_path27 = require("node:path");
23087
+ var import_node_path26 = require("node:path");
22863
23088
  var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
22864
23089
  var IMMEDIATE_RETRY_BUDGET = 3;
22865
23090
  var FileLockBusyError = class extends Error {
@@ -22944,7 +23169,7 @@ async function releaseFileLock(lockPath, guard) {
22944
23169
  }
22945
23170
  async function withFileLock(lockPath, opts, fn) {
22946
23171
  const resolved = resolveFileLockOpts(opts);
22947
- await (0, import_promises5.mkdir)((0, import_node_path27.dirname)(lockPath), { recursive: true }).catch(() => void 0);
23172
+ await (0, import_promises5.mkdir)((0, import_node_path26.dirname)(lockPath), { recursive: true }).catch(() => void 0);
22948
23173
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
22949
23174
  try {
22950
23175
  return await fn();
@@ -22955,7 +23180,7 @@ async function withFileLock(lockPath, opts, fn) {
22955
23180
 
22956
23181
  // src/schedules-lift-command.ts
22957
23182
  var import_promises6 = require("node:fs/promises");
22958
- var import_node_path28 = require("node:path");
23183
+ var import_node_path27 = require("node:path");
22959
23184
 
22960
23185
  // src/schedules-lift.ts
22961
23186
  var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
@@ -23061,7 +23286,7 @@ async function readWorkflowFiles(dir) {
23061
23286
  const files = [];
23062
23287
  for (const name of names.sort()) {
23063
23288
  if (!/\.ya?ml$/.test(name)) continue;
23064
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path28.join)(dir, name), "utf8") });
23289
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path27.join)(dir, name), "utf8") });
23065
23290
  }
23066
23291
  return files;
23067
23292
  }
@@ -23605,7 +23830,7 @@ function registerQueryCommands(program3) {
23605
23830
  // src/bootstrap-commands.ts
23606
23831
  var import_node_fs30 = require("node:fs");
23607
23832
  var import_node_os11 = require("node:os");
23608
- var import_node_path29 = require("node:path");
23833
+ var import_node_path28 = require("node:path");
23609
23834
 
23610
23835
  // src/bootstrap-drift.ts
23611
23836
  function byteComparableSeeds(manifest, cls) {
@@ -24474,7 +24699,7 @@ function registerBootstrapCommands(program3) {
24474
24699
  const readFile9 = (p) => (0, import_node_fs30.existsSync)(p) ? (0, import_node_fs30.readFileSync)(p, "utf8") : null;
24475
24700
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
24476
24701
  const putSeed = async (target, content, ref, sha) => {
24477
- const tmp = (0, import_node_path29.join)((0, import_node_os11.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
24702
+ const tmp = (0, import_node_path28.join)((0, import_node_os11.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
24478
24703
  (0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
24479
24704
  try {
24480
24705
  await gh(contentPutInputArgs(repo, target, tmp));
@@ -24738,11 +24963,11 @@ LIVE apply to ${repo}:
24738
24963
 
24739
24964
  // src/stage-commands.ts
24740
24965
  var import_node_fs32 = require("node:fs");
24741
- var import_node_path31 = require("node:path");
24966
+ var import_node_path30 = require("node:path");
24742
24967
 
24743
24968
  // src/port-registry.ts
24744
24969
  var import_node_fs31 = require("node:fs");
24745
- var import_node_path30 = require("node:path");
24970
+ var import_node_path29 = require("node:path");
24746
24971
 
24747
24972
  // ../infra/port-geometry.mjs
24748
24973
  var PORT_BLOCK = 100;
@@ -24795,22 +25020,22 @@ function existingPortRange(repo, registry2) {
24795
25020
  return registry2[repo] ?? null;
24796
25021
  }
24797
25022
  function portRangeInfraAt(root, source) {
24798
- const registryPath = (0, import_node_path30.join)(root, "infra", "port-ranges.json");
24799
- const ddbScriptPath = (0, import_node_path30.join)(root, "infra", "port-ddb.mjs");
25023
+ const registryPath = (0, import_node_path29.join)(root, "infra", "port-ranges.json");
25024
+ const ddbScriptPath = (0, import_node_path29.join)(root, "infra", "port-ddb.mjs");
24800
25025
  if (!(0, import_node_fs31.existsSync)(registryPath) || !(0, import_node_fs31.existsSync)(ddbScriptPath)) return null;
24801
25026
  return { root, source, registryPath, ddbScriptPath };
24802
25027
  }
24803
25028
  function resolvePortRangeInfra(cwd, packageDir) {
24804
25029
  const direct = portRangeInfraAt(cwd, "cwd");
24805
25030
  if (direct) return direct;
24806
- for (let dir = cwd; ; dir = (0, import_node_path30.dirname)(dir)) {
24807
- const sibling = portRangeInfraAt((0, import_node_path30.join)(dir, "MMI-Hub"), "sibling-hub");
25031
+ for (let dir = cwd; ; dir = (0, import_node_path29.dirname)(dir)) {
25032
+ const sibling = portRangeInfraAt((0, import_node_path29.join)(dir, "MMI-Hub"), "sibling-hub");
24808
25033
  if (sibling) return sibling;
24809
- const parent = (0, import_node_path30.dirname)(dir);
25034
+ const parent = (0, import_node_path29.dirname)(dir);
24810
25035
  if (parent === dir) break;
24811
25036
  }
24812
25037
  if (packageDir) {
24813
- const pkgRoot = (0, import_node_path30.join)(packageDir, "..", "..");
25038
+ const pkgRoot = (0, import_node_path29.join)(packageDir, "..", "..");
24814
25039
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
24815
25040
  if (pkgFrom) return pkgFrom;
24816
25041
  }
@@ -25004,8 +25229,8 @@ function registerStageCommands(program3) {
25004
25229
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
25005
25230
  return decideStage({
25006
25231
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
25007
- hasCompose: (0, import_node_fs32.existsSync)((0, import_node_path31.join)(process.cwd(), "docker-compose.yml")),
25008
- hasEnvExample: (0, import_node_fs32.existsSync)((0, import_node_path31.join)(process.cwd(), ".env.example"))
25232
+ hasCompose: (0, import_node_fs32.existsSync)((0, import_node_path30.join)(process.cwd(), "docker-compose.yml")),
25233
+ hasEnvExample: (0, import_node_fs32.existsSync)((0, import_node_path30.join)(process.cwd(), ".env.example"))
25009
25234
  });
25010
25235
  }
25011
25236
  async function fetchStageVaultEnvMerge() {
@@ -25445,7 +25670,7 @@ function registerBoardCommands(program3) {
25445
25670
  // src/merge-cleanup.ts
25446
25671
  var import_node_fs33 = require("node:fs");
25447
25672
  var import_promises8 = require("node:fs/promises");
25448
- var import_node_path33 = require("node:path");
25673
+ var import_node_path32 = require("node:path");
25449
25674
  var import_node_os12 = require("node:os");
25450
25675
  var import_node_child_process16 = require("node:child_process");
25451
25676
 
@@ -25533,7 +25758,7 @@ function boardAdvanceFailureMessage(result) {
25533
25758
 
25534
25759
  // src/deferred-registry-store.ts
25535
25760
  var import_promises7 = require("node:fs/promises");
25536
- var import_node_path32 = require("node:path");
25761
+ var import_node_path31 = require("node:path");
25537
25762
  var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
25538
25763
  async function atomicWrite(target, contents) {
25539
25764
  const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
@@ -25584,12 +25809,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
25584
25809
  },
25585
25810
  // Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
25586
25811
  write: async (entries) => {
25587
- await (0, import_promises7.mkdir)((0, import_node_path32.dirname)(registryPath), { recursive: true });
25812
+ await (0, import_promises7.mkdir)((0, import_node_path31.dirname)(registryPath), { recursive: true });
25588
25813
  await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
25589
25814
  },
25590
25815
  // Serialized read-modify-write under the repo-wide lock (#2846).
25591
25816
  update: async (mutate) => {
25592
- await (0, import_promises7.mkdir)((0, import_node_path32.dirname)(registryPath), { recursive: true });
25817
+ await (0, import_promises7.mkdir)((0, import_node_path31.dirname)(registryPath), { recursive: true });
25593
25818
  const deadline = Date.now() + opts.maxWaitMs;
25594
25819
  for (; ; ) {
25595
25820
  const guard = await acquireLock(lockPath, opts, deadline);
@@ -25737,7 +25962,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
25737
25962
  );
25738
25963
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
25739
25964
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
25740
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path33.dirname)((0, import_node_path33.dirname)(worktreeGitRoot)) : repoRoot2;
25965
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path32.dirname)((0, import_node_path32.dirname)(worktreeGitRoot)) : repoRoot2;
25741
25966
  const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
25742
25967
  const owners = readWorktreeOwners(primaryRepoRoot);
25743
25968
  const removalNow = Date.now();
@@ -25882,8 +26107,8 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
25882
26107
  const commits = JSON.parse(raw).commits ?? [];
25883
26108
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
25884
26109
  if (!body) return void 0;
25885
- const dir = (0, import_node_fs33.mkdtempSync)((0, import_node_path33.join)((0, import_node_os12.tmpdir)(), "mmi-squash-body-"));
25886
- const path2 = (0, import_node_path33.join)(dir, "body.txt");
26110
+ const dir = (0, import_node_fs33.mkdtempSync)((0, import_node_path32.join)((0, import_node_os12.tmpdir)(), "mmi-squash-body-"));
26111
+ const path2 = (0, import_node_path32.join)(dir, "body.txt");
25887
26112
  (0, import_node_fs33.writeFileSync)(path2, `${body}
25888
26113
  `, "utf8");
25889
26114
  return { path: path2, cleanup: () => {
@@ -26403,7 +26628,7 @@ async function checkDocsIndexAtHead(opts, deps) {
26403
26628
  // src/worktree-lifecycle-commands.ts
26404
26629
  var import_node_fs34 = require("node:fs");
26405
26630
  var import_promises9 = require("node:fs/promises");
26406
- var import_node_path34 = require("node:path");
26631
+ var import_node_path33 = require("node:path");
26407
26632
  var GH_TIMEOUT_MS = 2e4;
26408
26633
  var DEFAULT_BASE = "origin/development";
26409
26634
  var DEFAULT_REMOTE = "origin";
@@ -26549,7 +26774,7 @@ function classifyStaleLeaks(input) {
26549
26774
  var defaultOrphanDirScanDeps = {
26550
26775
  listDirs: (root) => {
26551
26776
  try {
26552
- return (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path34.join)(root, e.name));
26777
+ return (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path33.join)(root, e.name));
26553
26778
  } catch {
26554
26779
  return [];
26555
26780
  }
@@ -26702,13 +26927,13 @@ function registerWorktreeCommands(program3) {
26702
26927
  const detached = headBorn && !symbolicBranch;
26703
26928
  const branch = symbolicBranch || (detached ? "HEAD" : "");
26704
26929
  if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
26705
- const gitFile = (0, import_node_path34.join)(wtPath, ".git");
26930
+ const gitFile = (0, import_node_path33.join)(wtPath, ".git");
26706
26931
  const isLinked = (0, import_node_fs34.existsSync)(gitFile) && (0, import_node_fs34.statSync)(gitFile).isFile();
26707
26932
  if (apply && !isLinked) {
26708
26933
  return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
26709
26934
  }
26710
26935
  const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
26711
- const primaryCheckout = commonDir ? (0, import_node_path34.dirname)(commonDir) : wtPath;
26936
+ const primaryCheckout = commonDir ? (0, import_node_path33.dirname)(commonDir) : wtPath;
26712
26937
  const localBranchNames = await execFileP2("git", ["-C", primaryCheckout, "for-each-ref", "--format=%(refname:short)", "refs/heads"], { timeout: GIT_TIMEOUT_MS }).then(({ stdout }) => new Set((stdout || "").split("\n").map((l) => l.trim()).filter(Boolean))).catch(() => void 0);
26713
26938
  const orphan = classifyOrphanedWorktree({
26714
26939
  branch,
@@ -26919,7 +27144,7 @@ async function gatherWorktreeContext() {
26919
27144
  if (s) stages.push({ path: wt.path, port: s.port });
26920
27145
  }
26921
27146
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
26922
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path34.dirname)((0, import_node_path34.dirname)(worktreeGitRoot)) : repoRoot2;
27147
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path33.dirname)((0, import_node_path33.dirname)(worktreeGitRoot)) : repoRoot2;
26923
27148
  const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
26924
27149
  let orphanDirs = [];
26925
27150
  if ((0, import_node_fs34.existsSync)(wtRoot)) {
@@ -27604,7 +27829,7 @@ ${lines}`, {
27604
27829
 
27605
27830
  // src/train-commands.ts
27606
27831
  var import_node_fs36 = require("node:fs");
27607
- var import_node_path35 = require("node:path");
27832
+ var import_node_path34 = require("node:path");
27608
27833
 
27609
27834
  // src/train-status.ts
27610
27835
  function buildTrainStatusReport(input) {
@@ -27644,7 +27869,7 @@ function formatTrainStatus(r) {
27644
27869
  // src/train-commands.ts
27645
27870
  function readRepoVersion() {
27646
27871
  try {
27647
- return JSON.parse((0, import_node_fs36.readFileSync)((0, import_node_path35.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
27872
+ return JSON.parse((0, import_node_fs36.readFileSync)((0, import_node_path34.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
27648
27873
  } catch {
27649
27874
  return void 0;
27650
27875
  }
@@ -27792,7 +28017,7 @@ function registerDeployCommands(program3) {
27792
28017
  // src/discovery-commands.ts
27793
28018
  var import_node_fs37 = require("node:fs");
27794
28019
  var import_node_os13 = require("node:os");
27795
- var import_node_path36 = require("node:path");
28020
+ var import_node_path35 = require("node:path");
27796
28021
  var GC_GH_TIMEOUT_MS3 = 2e4;
27797
28022
  async function collectStatus() {
27798
28023
  const repo = await resolveRepo();
@@ -27982,8 +28207,8 @@ async function collectOnboardStatus(opts = {}) {
27982
28207
  }
27983
28208
  const home = (0, import_node_os13.homedir)();
27984
28209
  const plugin = onboardPluginGate({
27985
- readKnown: () => readFileSyncSafe((0, import_node_path36.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs37.readFileSync),
27986
- readSettings: () => readFileSyncSafe((0, import_node_path36.join)(home, ".claude", "settings.json"), import_node_fs37.readFileSync)
28210
+ readKnown: () => readFileSyncSafe((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs37.readFileSync),
28211
+ readSettings: () => readFileSyncSafe((0, import_node_path35.join)(home, ".claude", "settings.json"), import_node_fs37.readFileSync)
27987
28212
  });
27988
28213
  return { track, board, registry: registry2, secrets, plugin, estateCli, nextCommand };
27989
28214
  }
@@ -29246,6 +29471,63 @@ var surfaces_default = {
29246
29471
  "Some read, write, and web-fetch tool outputs are not rewriteable by the host.",
29247
29472
  "Provisioned skills and agents become discoverable only after a fresh session."
29248
29473
  ]
29474
+ },
29475
+ {
29476
+ token: "jervcode",
29477
+ displayName: "JervCode",
29478
+ lifecycle: "active",
29479
+ artifactIds: ["mmi-pi-plugin", "mmi-skills", "mmi-cli"],
29480
+ plannedArtifactIds: [],
29481
+ assembly: {
29482
+ mode: "source-tree",
29483
+ rootPath: ".",
29484
+ sync: []
29485
+ },
29486
+ install: {
29487
+ mechanism: "managed-config",
29488
+ locator: "~/.pi/agent/settings.json"
29489
+ },
29490
+ upgrade: {
29491
+ mechanism: "reinstall",
29492
+ reload: "restart"
29493
+ },
29494
+ skills: {
29495
+ delivery: "manifest",
29496
+ sourceSurfaceId: "mmi-skills",
29497
+ artifactId: "mmi-skills",
29498
+ invocation: {
29499
+ entry: "/mmi",
29500
+ any: "/<skill>"
29501
+ }
29502
+ },
29503
+ hooks: {
29504
+ adapterPath: ".pi-plugin/package.json",
29505
+ sourceSurfaceId: "mmi-hook-policy",
29506
+ gates: [],
29507
+ probe: "unsupported",
29508
+ execution: "unsupported",
29509
+ preToolUse: "unsupported",
29510
+ postToolOutput: "unsupported",
29511
+ finalOutput: "unsupported"
29512
+ },
29513
+ cli: {
29514
+ delivery: "standalone",
29515
+ artifactId: "mmi-cli"
29516
+ },
29517
+ ownership: {
29518
+ trust: "host",
29519
+ cache: "host",
29520
+ repair: "mmi-cli"
29521
+ },
29522
+ certification: {
29523
+ hostCommand: "jervcode",
29524
+ versionArgs: ["--version"]
29525
+ },
29526
+ enforcementCeilings: [
29527
+ "Pi reads the user package set from ~/.pi/agent/settings.json at launch: a live seat keeps the packages it started with, the mmi-cli heal refuses to edit the file while PI_SESSION_ID is set, and a new registration reaches JervCode only on the next launch.",
29528
+ "No hook event is wired on this host \u2014 there is no PreToolUse deny gate, no vault-edit gate, and no secret-output redaction or detection; the hook policy artifact does not ship here.",
29529
+ "The settings entry points into a version-pinned plugin cache clone; a release that mints a new clone leaves the entry stale until `mmi-cli doctor` re-points it, and Pi keeps loading the dead path silently until then."
29530
+ ]
29249
29531
  }
29250
29532
  ],
29251
29533
  surfaces: [
@@ -29556,12 +29838,38 @@ var surfaces_default = {
29556
29838
  applicability: "master DM notifications only; chatops out by default",
29557
29839
  versionCoordinated: false,
29558
29840
  publishVisibility: "n/a"
29841
+ },
29842
+ {
29843
+ id: "mmi-pi-plugin",
29844
+ classification: "packaging",
29845
+ kind: "plugin",
29846
+ ownerPath: ".pi-plugin/package.json",
29847
+ deliveryPath: ".pi-plugin/package.json",
29848
+ delivery: "release",
29849
+ applicability: "JervCode (branded launcher of the pi coding agent). A generated pi package wrapper whose `pi` block references the canonical skills tree by relative path inside the shipped clone \u2014 content is never copied. mmi-cli registers the wrapper directory as a local-path entry in the `packages` array of ~/.pi/agent/settings.json.",
29850
+ versionCoordinated: true,
29851
+ surfaceToken: "jervcode",
29852
+ versionPaths: [
29853
+ { path: ".pi-plugin/package.json", pointer: "version" }
29854
+ ],
29855
+ artifactIdentity: {
29856
+ kind: "sha256-tree",
29857
+ paths: [".pi-plugin/package.json", "skills"]
29858
+ },
29859
+ verify: [
29860
+ {
29861
+ command: "node",
29862
+ args: ["scripts/check-jervcode-consumer.mjs"],
29863
+ expected: "JervCode consumer fitness: clean ({version})"
29864
+ }
29865
+ ],
29866
+ publishVisibility: "public"
29559
29867
  }
29560
29868
  ]
29561
29869
  };
29562
29870
 
29563
29871
  // src/surface-doctor.ts
29564
- var TOKENS = /* @__PURE__ */ new Set(["claude", "codex", "kimi", "cursor", "kilo"]);
29872
+ var TOKENS = /* @__PURE__ */ new Set(["claude", "codex", "kimi", "cursor", "kilo", "jervcode"]);
29565
29873
  function isActiveToken(value) {
29566
29874
  return TOKENS.has(value);
29567
29875
  }
@@ -30625,17 +30933,17 @@ function parseOriginRepo(remoteUrl) {
30625
30933
  }
30626
30934
  function ghHostsConfigPath(env, platform2) {
30627
30935
  const sep3 = platform2 === "win32" ? "\\" : "/";
30628
- const join34 = (...parts) => parts.join(sep3);
30936
+ const join33 = (...parts) => parts.join(sep3);
30629
30937
  const explicit = env.GH_CONFIG_DIR?.trim();
30630
- if (explicit) return join34(explicit, "hosts.yml");
30938
+ if (explicit) return join33(explicit, "hosts.yml");
30631
30939
  if (platform2 === "win32") {
30632
30940
  const appData = (env.AppData ?? env.APPDATA)?.trim();
30633
- return appData ? join34(appData, "GitHub CLI", "hosts.yml") : void 0;
30941
+ return appData ? join33(appData, "GitHub CLI", "hosts.yml") : void 0;
30634
30942
  }
30635
30943
  const xdg = env.XDG_CONFIG_HOME?.trim();
30636
- if (xdg) return join34(xdg, "gh", "hosts.yml");
30944
+ if (xdg) return join33(xdg, "gh", "hosts.yml");
30637
30945
  const home = env.HOME?.trim();
30638
- return home ? join34(home, ".config", "gh", "hosts.yml") : void 0;
30946
+ return home ? join33(home, ".config", "gh", "hosts.yml") : void 0;
30639
30947
  }
30640
30948
  function parseGhHostsAccounts(yaml, host = "github.com") {
30641
30949
  let hostIndent = null;
@@ -30687,7 +30995,7 @@ function ghAccountCaveat(announcedLogin, accounts) {
30687
30995
  // src/doctor-io.ts
30688
30996
  var import_node_fs38 = require("node:fs");
30689
30997
  var import_node_os14 = require("node:os");
30690
- var import_node_path37 = require("node:path");
30998
+ var import_node_path36 = require("node:path");
30691
30999
  var import_node_child_process17 = require("node:child_process");
30692
31000
  var import_node_util8 = require("node:util");
30693
31001
  var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process17.execFile);
@@ -30695,7 +31003,7 @@ var MMI_PLUGIN_ID2 = "mmi@mutmutco";
30695
31003
  function installedClaudePluginVersion() {
30696
31004
  try {
30697
31005
  const file = JSON.parse(
30698
- (0, import_node_fs38.readFileSync)((0, import_node_path37.join)((0, import_node_os14.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
31006
+ (0, import_node_fs38.readFileSync)((0, import_node_path36.join)((0, import_node_os14.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
30699
31007
  );
30700
31008
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
30701
31009
  if (versions.length === 0) return void 0;
@@ -30716,17 +31024,22 @@ function installedSurfacePluginVersion(surface) {
30716
31024
  const token = surfaceToken(surface);
30717
31025
  if (token === "kilo") {
30718
31026
  try {
30719
- const stamp = (0, import_node_fs38.readFileSync)((0, import_node_path37.join)((0, import_node_os14.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
31027
+ const stamp = (0, import_node_fs38.readFileSync)((0, import_node_path36.join)((0, import_node_os14.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
30720
31028
  return stamp || void 0;
30721
31029
  } catch {
30722
31030
  return void 0;
30723
31031
  }
30724
31032
  }
30725
31033
  if (token === "cursor") {
30726
- return manifestVersion((0, import_node_path37.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
31034
+ return manifestVersion((0, import_node_path36.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
31035
+ }
31036
+ if (token === "jervcode") {
31037
+ const entry = mmiPiWrapperEntry();
31038
+ if (!entry) return void 0;
31039
+ return manifestVersion((0, import_node_path36.join)(decodeURIComponent(entry.replace(/^file:\/\/\/?/, "")), "package.json"));
30727
31040
  }
30728
31041
  if (token === "kimi") {
30729
- return manifestVersion((0, import_node_path37.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
31042
+ return manifestVersion((0, import_node_path36.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
30730
31043
  }
30731
31044
  if (token === "claude") return installedClaudePluginVersion();
30732
31045
  if (token !== "codex") return void 0;
@@ -30764,7 +31077,7 @@ function worktreeRootSync() {
30764
31077
  }
30765
31078
  var gitignorePath = () => {
30766
31079
  const root = worktreeRootSync();
30767
- return root === null ? null : (0, import_node_path37.join)(root, ".gitignore");
31080
+ return root === null ? null : (0, import_node_path36.join)(root, ".gitignore");
30768
31081
  };
30769
31082
  function readGitignore() {
30770
31083
  const path2 = gitignorePath();
@@ -30803,7 +31116,7 @@ async function repoRoot() {
30803
31116
  }
30804
31117
  function hasRepoLocalWorktrees() {
30805
31118
  const root = worktreeRootSync();
30806
- return root !== null && (0, import_node_fs38.existsSync)((0, import_node_path37.join)(root, ".worktrees"));
31119
+ return root !== null && (0, import_node_fs38.existsSync)((0, import_node_path36.join)(root, ".worktrees"));
30807
31120
  }
30808
31121
 
30809
31122
  // src/index.ts
@@ -30848,7 +31161,7 @@ function ghMultiAccountCaveat(announcedLogin) {
30848
31161
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
30849
31162
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
30850
31163
  function envHealLockPath(home) {
30851
- return (0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
31164
+ return (0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
30852
31165
  }
30853
31166
  async function withEnvHealLock(what, run) {
30854
31167
  try {
@@ -30974,14 +31287,14 @@ function mmiDoctorDeps(opts = {}) {
30974
31287
  const home = (0, import_node_os15.homedir)();
30975
31288
  const rows = marketplaceRows(
30976
31289
  MMI_MARKETPLACE_NAME,
30977
- readFileSyncSafe((0, import_node_path38.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
30978
- readFileSyncSafe((0, import_node_path38.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync),
31290
+ readFileSyncSafe((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
31291
+ readFileSyncSafe((0, import_node_path37.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync),
30979
31292
  // #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
30980
31293
  // edit. Unconditional ? it is a fact about mmi-cli, not about the lane this run is on.
30981
31294
  true
30982
31295
  );
30983
31296
  const pending = readMarketplacePinPending(
30984
- (0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
31297
+ (0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
30985
31298
  MMI_MARKETPLACE_NAME
30986
31299
  );
30987
31300
  if (!pending) return rows;
@@ -31007,9 +31320,9 @@ function mmiDoctorDeps(opts = {}) {
31007
31320
  if (detectSurface(process.env) === "codex") return void 0;
31008
31321
  const home = (0, import_node_os15.homedir)();
31009
31322
  const names = [MMI_MARKETPLACE_NAME];
31010
- const result = applyOrgMarketplacePins((0, import_node_path38.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
31323
+ const result = applyOrgMarketplacePins((0, import_node_path37.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
31011
31324
  if (result?.wrote) {
31012
- writeMarketplacePinPending((0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
31325
+ writeMarketplacePinPending((0, import_node_path37.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
31013
31326
  }
31014
31327
  return result;
31015
31328
  } catch {
@@ -31025,7 +31338,7 @@ function mmiDoctorDeps(opts = {}) {
31025
31338
  // adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
31026
31339
  // get a permanent ? demanding an artifact it never asked for.
31027
31340
  docsIndexState: (root) => {
31028
- if (!(0, import_node_fs39.existsSync)((0, import_node_path38.join)(root, DOCS_INDEX_PATH))) return void 0;
31341
+ if (!(0, import_node_fs39.existsSync)((0, import_node_path37.join)(root, DOCS_INDEX_PATH))) return void 0;
31029
31342
  const real = createDocsIndexDeps(root);
31030
31343
  let docs2;
31031
31344
  const listDocs = () => docs2 ??= real.listDocs();
@@ -31034,7 +31347,7 @@ function mmiDoctorDeps(opts = {}) {
31034
31347
  },
31035
31348
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
31036
31349
  healDocsIndex: (root) => {
31037
- if (!(0, import_node_fs39.existsSync)((0, import_node_path38.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
31350
+ if (!(0, import_node_fs39.existsSync)((0, import_node_path37.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
31038
31351
  const real = createDocsIndexDeps(root);
31039
31352
  let docs2;
31040
31353
  const listDocs = () => docs2 ??= real.listDocs();
@@ -31368,7 +31681,7 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
31368
31681
  });
31369
31682
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
31370
31683
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
31371
- const path2 = (0, import_node_path38.join)(process.cwd(), ".gitignore");
31684
+ const path2 = (0, import_node_path37.join)(process.cwd(), ".gitignore");
31372
31685
  const current = (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null;
31373
31686
  const plan = planManagedGitignore(current);
31374
31687
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
@@ -31537,7 +31850,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
31537
31850
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
31538
31851
  let root;
31539
31852
  if (o.root !== void 0) {
31540
- root = (0, import_node_path38.resolve)(o.root);
31853
+ root = (0, import_node_path37.resolve)(o.root);
31541
31854
  if (!(0, import_node_fs39.existsSync)(root) || !(0, import_node_fs39.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
31542
31855
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
31543
31856
  if (isPathUnderDirectory(gcRepoRoot, root)) {
@@ -31653,7 +31966,7 @@ function acquireWorktreeSetupLock(worktreeRoot) {
31653
31966
  };
31654
31967
  };
31655
31968
  try {
31656
- (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(lockPath), { recursive: true });
31969
+ (0, import_node_fs39.mkdirSync)((0, import_node_path37.dirname)(lockPath), { recursive: true });
31657
31970
  return take();
31658
31971
  } catch {
31659
31972
  try {
@@ -31965,7 +32278,7 @@ repoIndex.command("publish").description("publish this checkout's projection to
31965
32278
  return await failGraceful(e.message);
31966
32279
  }
31967
32280
  });
31968
- repoIndex.command("search").description("search Hub cloud by default (lexical/hybrid/semantic); use --local for checkout-only").argument("<query>", "path fragment, symbol, or meaning phrase").option("--limit <n>", "max hits", "20").option("--json", "machine-readable hits").option("--local", "search only the local projection (rebuild if missing)").option("--cloud", "force Hub cloud search (default when not --local)").option("--semantic", "semantic mode (Titan cosine over path+symbols embeddings)").option("--lexical", "lexical-only mode (paths/symbols)").option("--repo <owner/name>", "limit cloud search to one repo").action(async (query, o) => {
32281
+ async function runRepoIndexSearchCommand(query, o, defaultMode) {
31969
32282
  try {
31970
32283
  const limit = Math.max(1, Math.min(100, Number(o.limit ?? 20) || 20));
31971
32284
  const useLocal = o.local === true && o.cloud !== true;
@@ -31988,7 +32301,7 @@ repoIndex.command("search").description("search Hub cloud by default (lexical/hy
31988
32301
  }
31989
32302
  return;
31990
32303
  }
31991
- const mode = o.semantic ? "semantic" : o.lexical ? "lexical" : "hybrid";
32304
+ const mode = o.semantic ? "semantic" : o.lexical ? "lexical" : defaultMode;
31992
32305
  const cfg = await loadConfig();
31993
32306
  const res = await searchRepoIndexCloud(query, { mode, limit, repo: o.repo }, registryClientDeps(cfg));
31994
32307
  if (!res.ok) return await failGraceful(res.error);
@@ -32007,7 +32320,13 @@ repoIndex.command("search").description("search Hub cloud by default (lexical/hy
32007
32320
  } catch (e) {
32008
32321
  return await failGraceful(e.message);
32009
32322
  }
32010
- });
32323
+ }
32324
+ var REPO_INDEX_SEARCH_WHEN = 'beats grep for cross-repo questions, "where does X happen", and unknown filenames; grep wins inside a known checkout';
32325
+ function repoIndexSearchOptions(cmd) {
32326
+ return cmd.option("--limit <n>", "max hits", "20").option("--json", "machine-readable hits").option("--local", "search only the local projection (rebuild if missing)").option("--cloud", "force Hub cloud search (default when not --local)").option("--semantic", "semantic mode (Titan cosine over path+symbols embeddings)").option("--lexical", "lexical-only mode (paths/symbols)").option("--repo <owner/name>", "limit cloud search to one repo");
32327
+ }
32328
+ repoIndexSearchOptions(repoIndex.command("search").description(`search Hub cloud by default (lexical/hybrid/semantic); use --local for checkout-only \u2014 ${REPO_INDEX_SEARCH_WHEN}`).argument("<query>", "path fragment, symbol, or meaning phrase")).action(async (query, o) => runRepoIndexSearchCommand(query, o, "hybrid"));
32329
+ repoIndexSearchOptions(program2.command("find").description(`estate code search across every registered repo (repo-index search, semantic by default) \u2014 ${REPO_INDEX_SEARCH_WHEN}`).argument("<query>", "path fragment, symbol, or meaning phrase")).action(async (query, o) => runRepoIndexSearchCommand(query, o, "semantic"));
32011
32330
  repoIndex.command("status").description("show local and/or cloud repo-index status").option("--json", "machine-readable status").option("--cloud", "query Hub cloud status (estate or --repo)").option("--repo <owner/name>", "cloud status for one repo").action(async (o) => {
32012
32331
  try {
32013
32332
  if (o.cloud || o.repo) {
@@ -32059,11 +32378,10 @@ repoIndex.command("status").description("show local and/or cloud repo-index stat
32059
32378
  return await failGraceful(e.message);
32060
32379
  }
32061
32380
  });
32062
- repoIndex.command("health").description("post-deploy health gate: status + golden lexical/semantic queries (Hub#4149)").option("--live", "hit Hub cloud (status + searches); omit for offline golden-shape check").option("--golden <path>", "golden queries JSON (default: cli/testdata/repo-index-golden-queries.json)").option("--json", "machine-readable findings").action(async (o) => {
32381
+ repoIndex.command("health").description("post-deploy health gate: status + golden lexical/semantic queries (Hub#4149)").option("--live", "hit Hub cloud (status + searches); omit for offline golden-shape check").option("--golden <path>", "golden queries JSON (default: the suite bundled with the CLI, so the gate runs from any repo)").option("--json", "machine-readable findings").action(async (o) => {
32063
32382
  try {
32064
- const root = await repoRoot();
32065
- const goldenPath = o.golden || defaultGoldenPath(root);
32066
- const golden = loadGoldenSuite(goldenPath);
32383
+ const goldenPath = o.golden || "bundled repo-index-golden-queries.json";
32384
+ const golden = o.golden ? loadGoldenSuite(o.golden) : defaultGoldenSuite();
32067
32385
  const result = o.live ? await runRepoIndexHealth({
32068
32386
  golden,
32069
32387
  live: {
@@ -33266,11 +33584,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
33266
33584
  }
33267
33585
  });
33268
33586
  async function listCiWorkflowPaths(cwd = process.cwd()) {
33269
- const wfDir = (0, import_node_path38.join)(cwd, ".github", "workflows");
33587
+ const wfDir = (0, import_node_path37.join)(cwd, ".github", "workflows");
33270
33588
  if (!(0, import_node_fs39.existsSync)(wfDir)) return [];
33271
33589
  return (0, import_node_fs39.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
33272
33590
  try {
33273
- return workflowReportsPrChecks((0, import_node_fs39.readFileSync)((0, import_node_path38.join)(wfDir, name), "utf8"));
33591
+ return workflowReportsPrChecks((0, import_node_fs39.readFileSync)((0, import_node_path37.join)(wfDir, name), "utf8"));
33274
33592
  } catch {
33275
33593
  return true;
33276
33594
  }
@@ -33302,16 +33620,16 @@ function ciAuditDeps() {
33302
33620
  // gate re-seed step is skipped gracefully rather than failing mid-run.
33303
33621
  readSeedFile: (path2) => {
33304
33622
  if (!root) return null;
33305
- const fullPath = (0, import_node_path38.join)(root, path2);
33623
+ const fullPath = (0, import_node_path37.join)(root, path2);
33306
33624
  return (0, import_node_fs39.existsSync)(fullPath) ? (0, import_node_fs39.readFileSync)(fullPath, "utf8") : null;
33307
33625
  }
33308
33626
  };
33309
33627
  }
33310
33628
  function hubRoot() {
33311
- const fromPkg = (0, import_node_path38.join)(__dirname, "..", "..");
33629
+ const fromPkg = (0, import_node_path37.join)(__dirname, "..", "..");
33312
33630
  const marker = "skills/bootstrap/seeds/manifest.json";
33313
- if ((0, import_node_fs39.existsSync)((0, import_node_path38.join)(fromPkg, marker))) return fromPkg;
33314
- if ((0, import_node_fs39.existsSync)((0, import_node_path38.join)(process.cwd(), marker))) return process.cwd();
33631
+ if ((0, import_node_fs39.existsSync)((0, import_node_path37.join)(fromPkg, marker))) return fromPkg;
33632
+ if ((0, import_node_fs39.existsSync)((0, import_node_path37.join)(process.cwd(), marker))) return process.cwd();
33315
33633
  return null;
33316
33634
  }
33317
33635
  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) => {
@@ -34158,7 +34476,7 @@ function directoryBytes(path2) {
34158
34476
  return 0;
34159
34477
  }
34160
34478
  for (const entry of entries) {
34161
- const child2 = (0, import_node_path38.join)(path2, entry.name);
34479
+ const child2 = (0, import_node_path37.join)(path2, entry.name);
34162
34480
  if (entry.isDirectory()) total += directoryBytes(child2);
34163
34481
  else {
34164
34482
  try {
@@ -34188,7 +34506,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
34188
34506
  dirBytes,
34189
34507
  listStagingDirs: (root) => (0, import_node_fs39.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
34190
34508
  try {
34191
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path38.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs39.statSync)(p).mtimeMs) };
34509
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path37.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs39.statSync)(p).mtimeMs) };
34192
34510
  } catch {
34193
34511
  return { name: d.name, mtimeMs: Date.now() };
34194
34512
  }
@@ -34202,7 +34520,7 @@ function stagingApplyFsGuard(configRoot) {
34202
34520
  return {
34203
34521
  referencedPaths: () => readInstalledPluginRefs(configRoot),
34204
34522
  mtimeMs: (name) => {
34205
- const p = (0, import_node_path38.join)(stagingRoot, name);
34523
+ const p = (0, import_node_path37.join)(stagingRoot, name);
34206
34524
  if (!(0, import_node_fs39.existsSync)(p)) return null;
34207
34525
  try {
34208
34526
  return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs39.statSync)(q).mtimeMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.96.0",
3
+ "version": "3.97.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",