@mutmutco/cli 3.94.0 → 3.95.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 +1468 -374
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3416,8 +3416,8 @@ var program = new Command();
3416
3416
 
3417
3417
  // src/index.ts
3418
3418
  var import_promises11 = require("node:fs/promises");
3419
- var import_node_fs36 = require("node:fs");
3420
- var import_node_child_process16 = require("node:child_process");
3419
+ var import_node_fs39 = require("node:fs");
3420
+ var import_node_child_process18 = require("node:child_process");
3421
3421
 
3422
3422
  // src/cli-shared.ts
3423
3423
  var import_node_child_process3 = require("node:child_process");
@@ -6993,11 +6993,13 @@ function commandLadderHint() {
6993
6993
  }
6994
6994
  lines.push("[mmi] Schedules doctrine (#3228): scheduled jobs + LLM crons go through the Hub registry \u2014 eight-field header \u2192 PR \u2192 `mmi-cli org schedules register`; LLM crons ONLY via the harbour (llm: yes \u21D2 executor cursor-agent). Never hand-build an unregistered cron. Opt out per repo: `mmi-cli org schedules mode self-managed`.");
6995
6995
  lines.push('[mmi] Registering \u2260 armed (#3281): `org schedules register` writes SCHEDULE# rows; the fleet-clock reconciler arms them within ~15 min. Report "registered successfully \u2014 arms on the next fleet-clock tick", never "armed"/"live". The success JSON carries an `arming` block. Before that window an absent clock means "not reconciled yet", NOT "will never fire" \u2014 verify with `mmi-cli org schedules` after it.');
6996
+ lines.push("[mmi] Current-state doors (#4120): prefer `org schedules`, `runtime box list`, `org project get`, `board`/`status`/`next`, `docs index` over markdown inventories or janitor prose \u2014 see docs/Architecture/compute-at-read.md.");
6997
+ lines.push("[mmi] Estate structure search (#4133): `mmi-cli repo-index search <q>` hits Hub cloud (lexical/hybrid; `--semantic` for meaning). Pointer hits only \u2014 never invent wiki prose or trust living `docs/**` current-state. Local rebuild is optional (`--local`); laptops do not index the estate.");
6996
6998
  return lines.join("\n");
6997
6999
  }
6998
7000
 
6999
7001
  // src/index.ts
7000
- var import_node_path35 = require("node:path");
7002
+ var import_node_path38 = require("node:path");
7001
7003
 
7002
7004
  // src/merge-ci-policy.ts
7003
7005
  function resolveMergeCiPolicy(input) {
@@ -13319,7 +13321,7 @@ async function executeWaveLand(plan, deps, opts = { preserveWorktree: true }) {
13319
13321
  }
13320
13322
 
13321
13323
  // src/index.ts
13322
- var import_node_os14 = require("node:os");
13324
+ var import_node_os15 = require("node:os");
13323
13325
 
13324
13326
  // src/board.ts
13325
13327
  var import_node_child_process9 = require("node:child_process");
@@ -15105,9 +15107,9 @@ function parseSecretsUseArgv(tail) {
15105
15107
  const flags = {};
15106
15108
  const keys = [];
15107
15109
  const firstSep = tail.indexOf("--");
15108
- const sep2 = firstSep !== -1 && separatorIsOurs(tail.slice(0, firstSep)) ? firstSep : -1;
15109
- const head = sep2 === -1 ? tail : tail.slice(0, sep2);
15110
- let command = sep2 === -1 ? [] : tail.slice(sep2 + 1).slice();
15110
+ const sep3 = firstSep !== -1 && separatorIsOurs(tail.slice(0, firstSep)) ? firstSep : -1;
15111
+ const head = sep3 === -1 ? tail : tail.slice(0, sep3);
15112
+ let command = sep3 === -1 ? [] : tail.slice(sep3 + 1).slice();
15111
15113
  for (let i = 0; i < head.length; ) {
15112
15114
  const tok = head[i];
15113
15115
  const eq = tok.indexOf("=");
@@ -17231,11 +17233,11 @@ var PRIMARY_GROUPS = [
17231
17233
  // `tests` sits beside `docs` deliberately: both are deterministic, repo-local gates a workflow
17232
17234
  // step invokes (`docs refs`, `tests policy`), not org-plane operations (#3605). `spawn policy`
17233
17235
  // joins them on the same footing (#3979).
17234
- ["Setup and support", ["bootstrap", "secrets", "docs", "tests", "spawn"]],
17236
+ ["Setup and support", ["bootstrap", "secrets", "docs", "repo-index", "tests", "spawn"]],
17235
17237
  ["Coordinate and improve", ["wave", "report", "skill-lesson"]]
17236
17238
  ];
17237
17239
  var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set(["org", "runtime", "plugin"]);
17238
- var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "tests", "spawn", "wave", "report", "skill-lesson"]);
17240
+ var SUPPORT_PRIMARY = /* @__PURE__ */ new Set(["doctor", "whoami", "commands", "explain", "docs", "repo-index", "tests", "spawn", "wave", "report", "skill-lesson"]);
17239
17241
  var TOP_LEVEL_ORDER = /* @__PURE__ */ new Map();
17240
17242
  var HELP_GROUP_ORDER = /* @__PURE__ */ new Map();
17241
17243
  var topLevelPosition = 0;
@@ -17274,6 +17276,7 @@ var COMMAND_OWNERSHIP = {
17274
17276
  bootstrap: { module_owner: "cli/src/bootstrap-commands.ts", consumer: "repo-bootstrap" },
17275
17277
  secrets: { module_owner: "cli/src/secrets-commands.ts", consumer: "authenticated-operator" },
17276
17278
  docs: { module_owner: "cli/src/docs-index-command.ts", consumer: "repo-gates" },
17279
+ "repo-index": { module_owner: "cli/src/repo-index.ts", consumer: "agent-session" },
17277
17280
  tests: { module_owner: "cli/src/test-policy-core.ts", consumer: "repo-gates" },
17278
17281
  spawn: { module_owner: "cli/src/spawn-policy-core.ts", consumer: "repo-gates" },
17279
17282
  wave: { module_owner: "cli/src/wave-land.ts", consumer: "campaign-orchestrator" },
@@ -19298,10 +19301,666 @@ function renderAccessReport(report) {
19298
19301
  return lines.join("\n");
19299
19302
  }
19300
19303
 
19301
- // src/spawn-policy-core.ts
19304
+ // src/repo-index.ts
19305
+ var import_node_crypto4 = require("node:crypto");
19302
19306
  var import_node_child_process11 = require("node:child_process");
19303
19307
  var import_node_fs22 = require("node:fs");
19304
19308
  var import_node_path20 = require("node:path");
19309
+ var REPO_INDEX_SCHEMA = 1;
19310
+ var HARD_DENY = [
19311
+ /(^|\/)\.env(\.|$)/i,
19312
+ /(^|\/)\.env\./i,
19313
+ /credentials/i,
19314
+ /secrets?\.json$/i,
19315
+ /\.pem$/i,
19316
+ /\.p12$/i,
19317
+ /\.key$/i,
19318
+ /(^|\/)id_rsa/i,
19319
+ /(^|\/)id_ed25519/i,
19320
+ /\.keystore$/i
19321
+ ];
19322
+ var INDEXABLE_EXT = /* @__PURE__ */ new Set([
19323
+ ".ts",
19324
+ ".tsx",
19325
+ ".js",
19326
+ ".jsx",
19327
+ ".mjs",
19328
+ ".cjs",
19329
+ ".py",
19330
+ ".go",
19331
+ ".rs",
19332
+ ".java",
19333
+ ".kt",
19334
+ ".md",
19335
+ ".yml",
19336
+ ".yaml",
19337
+ ".json",
19338
+ ".toml",
19339
+ ".sh",
19340
+ ".ps1",
19341
+ ".css",
19342
+ ".html"
19343
+ ]);
19344
+ var SYMBOL_RE = /^(?:export\s+)?(?:async\s+)?(?:function|class|const|let|var|type|interface|enum)\s+([A-Za-z_$][\w$]*)/gm;
19345
+ function repoIndexStorePath(cwd) {
19346
+ return repoRuntimeStatePath(cwd, "repo-index", "index.json");
19347
+ }
19348
+ function isHardDeniedPath(relPosix) {
19349
+ return HARD_DENY.some((re) => re.test(relPosix));
19350
+ }
19351
+ function isIndexablePath(relPosix) {
19352
+ if (isHardDeniedPath(relPosix)) return false;
19353
+ if (relPosix.startsWith(".git/")) return false;
19354
+ if (relPosix.includes("node_modules/")) return false;
19355
+ if (relPosix.includes("dist/")) return false;
19356
+ const dot = relPosix.lastIndexOf(".");
19357
+ if (dot < 0) return false;
19358
+ return INDEXABLE_EXT.has(relPosix.slice(dot).toLowerCase());
19359
+ }
19360
+ function extractSymbols(source) {
19361
+ const out = /* @__PURE__ */ new Set();
19362
+ SYMBOL_RE.lastIndex = 0;
19363
+ let m;
19364
+ while ((m = SYMBOL_RE.exec(source)) !== null) {
19365
+ if (m[1]) out.add(m[1]);
19366
+ }
19367
+ return [...out].sort((a, b) => a.localeCompare(b));
19368
+ }
19369
+ var BLURB_CAP = 200;
19370
+ function extractModuleBlurb(source) {
19371
+ const lines = source.split(/\r?\n/);
19372
+ let i = 0;
19373
+ while (i < lines.length) {
19374
+ const t = lines[i].trim();
19375
+ if (!t || t.startsWith("#!") || /^['"]use (strict|client|server)['"]/.test(t)) {
19376
+ i++;
19377
+ continue;
19378
+ }
19379
+ break;
19380
+ }
19381
+ if (i >= lines.length) return void 0;
19382
+ const first = lines[i].trim();
19383
+ if (first.startsWith("/**")) {
19384
+ const same = first.replace(/^\/\*\*\s*/, "").replace(/\*\/\s*$/, "").replace(/^\*\s?/, "").trim();
19385
+ if (same && !same.startsWith("*") && same !== "/") return same.slice(0, BLURB_CAP);
19386
+ for (let j = i + 1; j < Math.min(i + 12, lines.length); j++) {
19387
+ const raw = lines[j].trim();
19388
+ if (raw.startsWith("*/")) break;
19389
+ const content = raw.replace(/^\*\s?/, "").trim();
19390
+ if (!content || content.startsWith("@")) continue;
19391
+ return content.slice(0, BLURB_CAP);
19392
+ }
19393
+ return void 0;
19394
+ }
19395
+ if (first.startsWith("//")) {
19396
+ const content = first.replace(/^\/\/\s?/, "").trim();
19397
+ return content ? content.slice(0, BLURB_CAP) : void 0;
19398
+ }
19399
+ if (first.startsWith("#")) {
19400
+ const content = first.replace(/^#+\s?/, "").trim();
19401
+ return content ? content.slice(0, BLURB_CAP) : void 0;
19402
+ }
19403
+ return void 0;
19404
+ }
19405
+ function extractReadmeSectionHeaders(markdown, limit = 6) {
19406
+ const out = [];
19407
+ for (const line of markdown.split(/\r?\n/)) {
19408
+ const m = /^(#{1,3})\s+(.+?)\s*$/.exec(line.trim());
19409
+ if (!m?.[2]) continue;
19410
+ const title = m[2].replace(/#+\s*$/, "").trim();
19411
+ if (!title) continue;
19412
+ out.push(title);
19413
+ if (out.length >= limit) break;
19414
+ }
19415
+ return out;
19416
+ }
19417
+ function loadReadmeHints(cwd, candidatePaths) {
19418
+ const hints = /* @__PURE__ */ new Map();
19419
+ const readmes = /* @__PURE__ */ new Set();
19420
+ if (candidatePaths.includes("README.md")) readmes.add("README.md");
19421
+ for (const p of candidatePaths) {
19422
+ const parts = p.split("/");
19423
+ if (parts.length === 2 && parts[1] === "README.md" && parts[0]) readmes.add(p);
19424
+ }
19425
+ for (const rel of readmes) {
19426
+ if (isHardDeniedPath(rel)) continue;
19427
+ const abs = (0, import_node_path20.join)(cwd, ...rel.split("/"));
19428
+ if (!(0, import_node_fs22.existsSync)(abs)) continue;
19429
+ let text;
19430
+ try {
19431
+ text = (0, import_node_fs22.readFileSync)(abs, "utf8");
19432
+ } catch {
19433
+ continue;
19434
+ }
19435
+ if (text.length > 2e5) continue;
19436
+ const headers = extractReadmeSectionHeaders(text);
19437
+ if (!headers.length) continue;
19438
+ const key = rel.includes("/") ? rel.split("/")[0] : "";
19439
+ hints.set(key, headers.join("; ").slice(0, BLURB_CAP));
19440
+ }
19441
+ return hints;
19442
+ }
19443
+ function toPosix(p) {
19444
+ return p.split(import_node_path20.sep).join("/");
19445
+ }
19446
+ function listCandidatePaths(cwd, exec = import_node_child_process11.execFileSync) {
19447
+ try {
19448
+ const out = exec("git", ["ls-files", "-z", "-c", "-o", "--exclude-standard"], {
19449
+ cwd,
19450
+ encoding: "utf8",
19451
+ maxBuffer: 64 * 1024 * 1024
19452
+ });
19453
+ return out.split("\0").filter(Boolean).map(toPosix);
19454
+ } catch {
19455
+ return [];
19456
+ }
19457
+ }
19458
+ function rebuildRepoIndex(cwd, repoSlug2) {
19459
+ const candidates = listCandidatePaths(cwd).filter(isIndexablePath);
19460
+ const ignored = defaultIsIgnored(cwd, candidates);
19461
+ const readmeHints = loadReadmeHints(cwd, candidates.filter((p) => !ignored.has(p)));
19462
+ const entries = [];
19463
+ for (const rel of candidates) {
19464
+ if (ignored.has(rel)) continue;
19465
+ if (isHardDeniedPath(rel)) continue;
19466
+ const abs = (0, import_node_path20.join)(cwd, ...rel.split("/"));
19467
+ if (!(0, import_node_fs22.existsSync)(abs)) continue;
19468
+ let text;
19469
+ try {
19470
+ text = (0, import_node_fs22.readFileSync)(abs, "utf8");
19471
+ } catch {
19472
+ continue;
19473
+ }
19474
+ if (text.length > 15e5) continue;
19475
+ const hash = (0, import_node_crypto4.createHash)("sha256").update(text).digest("hex").slice(0, 16);
19476
+ const symbols = extractSymbols(text);
19477
+ const docBlurb = extractModuleBlurb(text);
19478
+ const top = rel.includes("/") ? rel.split("/")[0] : "";
19479
+ const hint = docBlurb ?? (top ? readmeHints.get(top) : void 0) ?? readmeHints.get("");
19480
+ entries.push({
19481
+ path: rel,
19482
+ hash,
19483
+ symbols,
19484
+ ...hint ? { blurb: hint.slice(0, BLURB_CAP) } : {}
19485
+ });
19486
+ }
19487
+ entries.sort((a, b) => a.path.localeCompare(b.path));
19488
+ const projection = {
19489
+ schema: REPO_INDEX_SCHEMA,
19490
+ repo: repoSlug2,
19491
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
19492
+ entries
19493
+ };
19494
+ const store = repoIndexStorePath(cwd);
19495
+ (0, import_node_fs22.mkdirSync)((0, import_node_path20.dirname)(store), { recursive: true });
19496
+ (0, import_node_fs22.writeFileSync)(store, `${JSON.stringify(projection, null, 2)}
19497
+ `, "utf8");
19498
+ return projection;
19499
+ }
19500
+ function loadRepoIndex(cwd) {
19501
+ const store = repoIndexStorePath(cwd);
19502
+ if (!(0, import_node_fs22.existsSync)(store)) return null;
19503
+ try {
19504
+ const raw = JSON.parse((0, import_node_fs22.readFileSync)(store, "utf8"));
19505
+ if (raw?.schema !== REPO_INDEX_SCHEMA || !Array.isArray(raw.entries)) return null;
19506
+ return raw;
19507
+ } catch {
19508
+ return null;
19509
+ }
19510
+ }
19511
+ function repoIndexStatus(cwd) {
19512
+ const path2 = repoIndexStorePath(cwd);
19513
+ const idx = loadRepoIndex(cwd);
19514
+ if (!idx) return { present: false, path: path2 };
19515
+ const symbolCount = idx.entries.reduce((n, e) => n + e.symbols.length, 0);
19516
+ return {
19517
+ present: true,
19518
+ path: path2,
19519
+ schema: idx.schema,
19520
+ repo: idx.repo,
19521
+ builtAt: idx.builtAt,
19522
+ fileCount: idx.entries.length,
19523
+ symbolCount
19524
+ };
19525
+ }
19526
+ function searchRepoIndex(idx, query, limit = 20) {
19527
+ const q = query.trim().toLowerCase();
19528
+ if (!q) return [];
19529
+ const hits = [];
19530
+ for (const e of idx.entries) {
19531
+ const pathLower = e.path.toLowerCase();
19532
+ if (pathLower.includes(q) || pathLower.endsWith(`/${q}`) || pathLower === q) {
19533
+ hits.push({
19534
+ repo: idx.repo,
19535
+ path: e.path,
19536
+ kind: "path",
19537
+ why: pathLower === q || pathLower.endsWith(`/${q}`) ? "exact path segment" : "path substring",
19538
+ score: pathLower === q ? 1 : pathLower.endsWith(`/${q}`) ? 0.95 : 0.7
19539
+ });
19540
+ }
19541
+ for (const sym of e.symbols) {
19542
+ const sl = sym.toLowerCase();
19543
+ if (sl === q || sl.startsWith(q)) {
19544
+ hits.push({
19545
+ repo: idx.repo,
19546
+ path: e.path,
19547
+ kind: "symbol",
19548
+ symbol: sym,
19549
+ why: sl === q ? "exact symbol" : "symbol prefix",
19550
+ score: sl === q ? 1 : 0.85
19551
+ });
19552
+ }
19553
+ }
19554
+ }
19555
+ hits.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
19556
+ const seen = /* @__PURE__ */ new Set();
19557
+ const out = [];
19558
+ for (const h of hits) {
19559
+ const key = `${h.kind}:${h.path}:${h.symbol ?? ""}`;
19560
+ if (seen.has(key)) continue;
19561
+ seen.add(key);
19562
+ out.push(h);
19563
+ if (out.length >= limit) break;
19564
+ }
19565
+ return out;
19566
+ }
19567
+ function inferRepoSlug(cwd, exec = import_node_child_process11.execFileSync) {
19568
+ try {
19569
+ const url = String(exec("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" })).trim();
19570
+ const m = /[:/]([^/]+\/[^/]+?)(?:\.git)?$/.exec(url);
19571
+ if (m?.[1]) return m[1];
19572
+ } catch {
19573
+ }
19574
+ return (0, import_node_path20.basename)(cwd) || "local";
19575
+ }
19576
+
19577
+ // src/repo-index-cloud-client.ts
19578
+ var RETRY_ATTEMPTS2 = 3;
19579
+ async function publishRepoIndexCloud(payload, deps) {
19580
+ if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured" };
19581
+ const token = await deps.token();
19582
+ if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)" };
19583
+ try {
19584
+ const res = await fetchWithRetry(
19585
+ deps.fetch ?? fetch,
19586
+ `${deps.baseUrl.replace(/\/$/, "")}/repo-index/publish`,
19587
+ {
19588
+ method: "POST",
19589
+ headers: {
19590
+ ...clientVersionHeaders(),
19591
+ Authorization: `Bearer ${token}`,
19592
+ "content-type": "application/json"
19593
+ },
19594
+ body: JSON.stringify(payload)
19595
+ },
19596
+ {
19597
+ attempts: RETRY_ATTEMPTS2,
19598
+ // Embed publish can take longer than a registry read.
19599
+ timeoutMs: payload.embed ? 12e4 : deps.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS,
19600
+ sleep: deps.retrySleep
19601
+ }
19602
+ );
19603
+ if (res.status === 426) return { ok: false, error: upgradeRequiredError(res, await res.json().catch(() => null)), status: 426 };
19604
+ const body = await res.json().catch(() => ({}));
19605
+ if (!res.ok) {
19606
+ return { ok: false, error: typeof body.error === "string" ? body.error : `publish HTTP ${res.status}`, status: res.status };
19607
+ }
19608
+ return { ok: true, body };
19609
+ } catch (e) {
19610
+ return { ok: false, error: e.message };
19611
+ }
19612
+ }
19613
+ async function searchRepoIndexCloud(query, opts, deps) {
19614
+ if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured" };
19615
+ const token = await deps.token();
19616
+ if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)" };
19617
+ const params = new URLSearchParams({
19618
+ q: query,
19619
+ mode: opts.mode ?? "hybrid",
19620
+ limit: String(opts.limit ?? 20)
19621
+ });
19622
+ if (opts.repo) params.set("repo", opts.repo);
19623
+ try {
19624
+ const res = await fetchWithRetry(
19625
+ deps.fetch ?? fetch,
19626
+ `${deps.baseUrl.replace(/\/$/, "")}/repo-index/search?${params}`,
19627
+ { method: "GET", headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` } },
19628
+ { attempts: RETRY_ATTEMPTS2, timeoutMs: opts.mode === "semantic" || opts.mode === "hybrid" ? 6e4 : deps.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS, sleep: deps.retrySleep }
19629
+ );
19630
+ if (res.status === 426) return { ok: false, error: upgradeRequiredError(res, await res.json().catch(() => null)), status: 426 };
19631
+ const body = await res.json().catch(() => ({}));
19632
+ if (!res.ok) return { ok: false, error: body.error ?? `search HTTP ${res.status}`, status: res.status };
19633
+ return {
19634
+ ok: true,
19635
+ query: body.query ?? query,
19636
+ mode: body.mode ?? opts.mode ?? "hybrid",
19637
+ hits: Array.isArray(body.hits) ? body.hits : [],
19638
+ note: body.note
19639
+ };
19640
+ } catch (e) {
19641
+ return { ok: false, error: e.message };
19642
+ }
19643
+ }
19644
+ function isRepoIndexStatusError(st) {
19645
+ return typeof st === "object" && st !== null && st.ok === false && typeof st.error === "string" && typeof st.code === "string";
19646
+ }
19647
+ function explainRepoIndexStatusError(err) {
19648
+ switch (err.code) {
19649
+ case "deploy-lag":
19650
+ return "repo-index status: the Hub API did not recognize /repo-index/status (HTTP 404). The deployed Hub predates the estate index routes \u2014 not an auth error. Ship a Hub release from development that includes /repo-index/*, then run `repo-index-reconcile` (or `mmi-cli repo-index sync-estate`) so projections exist.";
19651
+ case "auth":
19652
+ return `repo-index status: ${err.error} \u2014 run \`gh auth login\` / refresh the Hub session.`;
19653
+ case "config":
19654
+ return `repo-index status: ${err.error}`;
19655
+ default:
19656
+ return `repo-index status: ${err.error}`;
19657
+ }
19658
+ }
19659
+ async function statusRepoIndexCloud(repo, deps) {
19660
+ if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured", code: "config" };
19661
+ const token = await deps.token();
19662
+ if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)", code: "auth" };
19663
+ const params = new URLSearchParams();
19664
+ if (repo) params.set("repo", repo);
19665
+ const q = params.toString();
19666
+ try {
19667
+ const res = await fetchWithRetry(
19668
+ deps.fetch ?? fetch,
19669
+ `${deps.baseUrl.replace(/\/$/, "")}/repo-index/status${q ? `?${q}` : ""}`,
19670
+ { method: "GET", headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` } },
19671
+ { attempts: RETRY_ATTEMPTS2, timeoutMs: deps.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS, sleep: deps.retrySleep }
19672
+ );
19673
+ if (res.status === 401 || res.status === 403) {
19674
+ return { ok: false, error: `status HTTP ${res.status}`, code: "auth", status: res.status };
19675
+ }
19676
+ if (res.status === 404) {
19677
+ return {
19678
+ ok: false,
19679
+ error: "the Hub API did not recognize /repo-index/status (HTTP 404) \u2014 deployed Hub predates estate index",
19680
+ code: "deploy-lag",
19681
+ status: 404
19682
+ };
19683
+ }
19684
+ if (!res.ok) return { ok: false, error: `status HTTP ${res.status}`, code: "http", status: res.status };
19685
+ return await res.json();
19686
+ } catch (e) {
19687
+ return { ok: false, error: e.message, code: "network" };
19688
+ }
19689
+ }
19690
+ async function gcRepoIndexCloud(deps) {
19691
+ if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured" };
19692
+ const token = await deps.token();
19693
+ if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)" };
19694
+ try {
19695
+ const res = await fetchWithRetry(
19696
+ deps.fetch ?? fetch,
19697
+ `${deps.baseUrl.replace(/\/$/, "")}/repo-index/gc`,
19698
+ {
19699
+ method: "POST",
19700
+ headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}`, "content-type": "application/json" },
19701
+ body: "{}"
19702
+ },
19703
+ { attempts: RETRY_ATTEMPTS2, timeoutMs: deps.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS, sleep: deps.retrySleep }
19704
+ );
19705
+ const body = await res.json().catch(() => ({}));
19706
+ if (!res.ok) return { ok: false, error: body.error ?? `gc HTTP ${res.status}` };
19707
+ return { ok: true, removed: body.removed ?? [] };
19708
+ } catch (e) {
19709
+ return { ok: false, error: e.message };
19710
+ }
19711
+ }
19712
+
19713
+ // src/repo-index-sync.ts
19714
+ var import_node_fs23 = require("node:fs");
19715
+ var import_node_os9 = require("node:os");
19716
+ var import_node_path21 = require("node:path");
19717
+ var import_node_child_process12 = require("node:child_process");
19718
+ var MAX_EMBED_BACKFILL_ROUNDS = 40;
19719
+ function normalizeRepo(raw) {
19720
+ const t = raw.trim().replace(/\.git$/, "");
19721
+ if (t.includes("/")) return t;
19722
+ return `mutmutco/${t}`;
19723
+ }
19724
+ function rosterRepos(projects) {
19725
+ const set = /* @__PURE__ */ new Set();
19726
+ for (const p of projects) {
19727
+ if (p.class === "content") continue;
19728
+ for (const r of p.repos ?? []) set.add(normalizeRepo(r));
19729
+ }
19730
+ return [...set].sort((a, b) => a.localeCompare(b));
19731
+ }
19732
+ function shallowClone(repo, dest, token) {
19733
+ (0, import_node_child_process12.execFileSync)(
19734
+ "git",
19735
+ ["-c", `http.extraHeader=Authorization: Bearer ${token}`, "clone", "--depth", "1", "--single-branch", `https://github.com/${repo}.git`, dest],
19736
+ { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
19737
+ );
19738
+ }
19739
+ async function syncEstateRepoIndex(opts) {
19740
+ const projects = await fetchProjectsList(opts.deps);
19741
+ if (!projects) {
19742
+ return { ok: false, published: [], failed: [{ repo: "*", error: "could not read /projects/list" }], skipped: [] };
19743
+ }
19744
+ let repos = rosterRepos(projects);
19745
+ if (opts.repo) {
19746
+ const want = normalizeRepo(opts.repo);
19747
+ repos = repos.filter((r) => r.toLowerCase() === want.toLowerCase());
19748
+ if (repos.length === 0) {
19749
+ return { ok: false, published: [], failed: [{ repo: want, error: "not on registry code roster" }], skipped: [] };
19750
+ }
19751
+ }
19752
+ const published = [];
19753
+ const failed = [];
19754
+ const skipped = [];
19755
+ for (const repo of repos) {
19756
+ const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path21.join)((0, import_node_os9.tmpdir)(), "mmi-repo-index-"));
19757
+ try {
19758
+ shallowClone(repo, dir, opts.githubToken);
19759
+ const built = rebuildRepoIndex(dir, repo);
19760
+ let pub = await publishRepoIndexCloud(
19761
+ {
19762
+ repo,
19763
+ builtAt: built.builtAt,
19764
+ entries: built.entries,
19765
+ embed: opts.embed === true
19766
+ },
19767
+ opts.deps
19768
+ );
19769
+ if (!pub.ok) {
19770
+ failed.push({ repo, error: pub.error });
19771
+ continue;
19772
+ }
19773
+ let rounds = 1;
19774
+ while (opts.embed === true && pub.ok && pub.body.embTruncated === true && rounds < MAX_EMBED_BACKFILL_ROUNDS) {
19775
+ pub = await publishRepoIndexCloud(
19776
+ {
19777
+ repo,
19778
+ builtAt: built.builtAt,
19779
+ entries: built.entries,
19780
+ embed: true
19781
+ },
19782
+ opts.deps
19783
+ );
19784
+ if (!pub.ok) {
19785
+ failed.push({ repo, error: pub.error });
19786
+ break;
19787
+ }
19788
+ rounds++;
19789
+ }
19790
+ if (!pub.ok) continue;
19791
+ const fileCount = Number(pub.body.fileCount ?? built.entries.length);
19792
+ const embCount = typeof pub.body.embCount === "number" ? pub.body.embCount : void 0;
19793
+ const embGap = typeof pub.body.embGap === "number" ? pub.body.embGap : typeof embCount === "number" ? Math.max(0, fileCount - embCount) : void 0;
19794
+ published.push({
19795
+ repo,
19796
+ fileCount,
19797
+ embCount,
19798
+ embGap,
19799
+ embTruncated: pub.body.embTruncated === true,
19800
+ embedRounds: opts.embed ? rounds : void 0
19801
+ });
19802
+ } catch (e) {
19803
+ failed.push({ repo, error: e.message });
19804
+ } finally {
19805
+ try {
19806
+ (0, import_node_fs23.rmSync)(dir, { recursive: true, force: true });
19807
+ } catch {
19808
+ }
19809
+ }
19810
+ }
19811
+ return { ok: failed.length === 0, published, failed, skipped };
19812
+ }
19813
+
19814
+ // src/repo-index-health.ts
19815
+ var import_node_fs24 = require("node:fs");
19816
+ var import_node_path22 = require("node:path");
19817
+ function defaultGoldenPath(repoRoot2) {
19818
+ return (0, import_node_path22.join)(repoRoot2, "cli", "testdata", "repo-index-golden-queries.json");
19819
+ }
19820
+ function loadGoldenSuite(path2) {
19821
+ const raw = JSON.parse((0, import_node_fs24.readFileSync)(path2, "utf8"));
19822
+ if (!raw || raw.schema !== 1 || !Array.isArray(raw.queries)) {
19823
+ throw new Error(`invalid golden suite at ${path2}`);
19824
+ }
19825
+ return raw;
19826
+ }
19827
+ function evaluateQueryHits(query, hits) {
19828
+ if (!hits.length) {
19829
+ return { ok: false, code: "empty-hits", detail: `${query.id}: no hits for ${JSON.stringify(query.q)}` };
19830
+ }
19831
+ const top = hits[0];
19832
+ const minScore = query.expect.minScore ?? 0;
19833
+ if (typeof top.score === "number" && top.score < minScore) {
19834
+ return {
19835
+ ok: false,
19836
+ code: "low-score",
19837
+ detail: `${query.id}: top score ${top.score} < ${minScore}`
19838
+ };
19839
+ }
19840
+ if (query.expect.topRepos?.length) {
19841
+ const repo = String(top.repo ?? "");
19842
+ if (!query.expect.topRepos.some((r) => r.toLowerCase() === repo.toLowerCase())) {
19843
+ return {
19844
+ ok: false,
19845
+ code: "wrong-repo",
19846
+ detail: `${query.id}: top repo ${JSON.stringify(repo)} not in ${JSON.stringify(query.expect.topRepos)}`
19847
+ };
19848
+ }
19849
+ }
19850
+ if (query.expect.topPaths?.length) {
19851
+ const path2 = String(top.path ?? "");
19852
+ if (!query.expect.topPaths.some((p) => path2 === p || path2.endsWith(p) || path2.includes(p))) {
19853
+ return {
19854
+ ok: false,
19855
+ code: "wrong-path",
19856
+ detail: `${query.id}: top path ${JSON.stringify(path2)} not in ${JSON.stringify(query.expect.topPaths)}`
19857
+ };
19858
+ }
19859
+ }
19860
+ return { ok: true, code: "ok", detail: `${query.id}: top hit accepted` };
19861
+ }
19862
+ function evaluateCloudStatus(status, golden) {
19863
+ const findings = [];
19864
+ const count = typeof status.count === "number" ? status.count : Array.isArray(status.repos) ? status.repos.length : null;
19865
+ if (count === 0 || Array.isArray(status.repos) && status.repos.length === 0 && status.present !== true) {
19866
+ findings.push({ ok: false, code: "projections-empty", detail: "cloud repo-index has zero projections" });
19867
+ }
19868
+ const repos = [];
19869
+ if (Array.isArray(status.repos)) {
19870
+ for (const r of status.repos) {
19871
+ if (r && typeof r === "object") repos.push(r);
19872
+ }
19873
+ } else if (status.present === true && typeof status.repo === "string") {
19874
+ repos.push(status);
19875
+ }
19876
+ const stale = repos.filter((r) => r.stale === true);
19877
+ if (stale.length) {
19878
+ findings.push({
19879
+ ok: false,
19880
+ code: "stale-projections",
19881
+ detail: `${stale.length} stale projection(s) (threshold ${golden.staleThresholdHours}h): ${stale.slice(0, 5).map((r) => r.repo).join(", ")}`
19882
+ });
19883
+ }
19884
+ const hub = repos.find((r) => r.repo?.toLowerCase() === golden.hubRepo.toLowerCase()) ?? (typeof status.repo === "string" && status.repo.toLowerCase() === golden.hubRepo.toLowerCase() ? status : void 0);
19885
+ if (!hub || hub.present === false) {
19886
+ findings.push({ ok: false, code: "hub-missing", detail: `${golden.hubRepo} projection missing` });
19887
+ } else {
19888
+ const fileCount = Number(hub.fileCount ?? 0);
19889
+ const embCount = Number(hub.embCount ?? 0);
19890
+ if (fileCount <= 0) {
19891
+ findings.push({ ok: false, code: "hub-empty", detail: `${golden.hubRepo} has fileCount=${fileCount}` });
19892
+ }
19893
+ const denom = Math.min(fileCount, golden.maxSyncEmbeds);
19894
+ const coverage = denom > 0 ? embCount / denom : 0;
19895
+ if (fileCount > 0 && coverage < golden.hubEmbMinCoverage) {
19896
+ findings.push({
19897
+ ok: false,
19898
+ code: "hub-emb-gap",
19899
+ detail: `${golden.hubRepo} embCoverage=${coverage.toFixed(2)} (emb=${embCount} files=${fileCount} cap=${golden.maxSyncEmbeds}) < ${golden.hubEmbMinCoverage}`
19900
+ });
19901
+ }
19902
+ }
19903
+ if (!findings.length) {
19904
+ findings.push({ ok: true, code: "status-ok", detail: `status healthy (${repos.length || count || 0} repo projection(s))` });
19905
+ }
19906
+ return findings;
19907
+ }
19908
+ async function runRepoIndexHealth(opts) {
19909
+ const findings = [];
19910
+ if (opts.live) {
19911
+ const st = await opts.live.status();
19912
+ if (st.ok === false && typeof st.error === "string") {
19913
+ findings.push({ ok: false, code: "status-error", detail: st.error });
19914
+ } else {
19915
+ findings.push(...evaluateCloudStatus(st, opts.golden));
19916
+ }
19917
+ for (const q of opts.golden.queries) {
19918
+ const res = await opts.live.search(q.q, q.mode);
19919
+ if (!res.ok) {
19920
+ const semantic503 = q.mode === "semantic" && (res.status === 503 || /semantic unavailable|503/i.test(res.error));
19921
+ findings.push({
19922
+ ok: false,
19923
+ code: semantic503 ? "semantic-503" : "search-error",
19924
+ detail: `${q.id}: ${res.error}`
19925
+ });
19926
+ continue;
19927
+ }
19928
+ findings.push(evaluateQueryHits(q, res.hits));
19929
+ }
19930
+ } else if (opts.fixtures) {
19931
+ for (const q of opts.golden.queries) {
19932
+ const fx = opts.fixtures[q.id];
19933
+ if (!fx) {
19934
+ findings.push({ ok: false, code: "fixture-missing", detail: `${q.id}: no fixture` });
19935
+ continue;
19936
+ }
19937
+ if (fx.error) {
19938
+ const semantic503 = q.mode === "semantic" && (fx.status === 503 || /semantic unavailable|503/i.test(fx.error));
19939
+ findings.push({
19940
+ ok: false,
19941
+ code: semantic503 ? "semantic-503" : "search-error",
19942
+ detail: `${q.id}: ${fx.error}`
19943
+ });
19944
+ continue;
19945
+ }
19946
+ findings.push(evaluateQueryHits(q, fx.hits ?? []));
19947
+ }
19948
+ } else {
19949
+ for (const q of opts.golden.queries) {
19950
+ if (!q.id || !q.q || !q.mode) {
19951
+ findings.push({ ok: false, code: "golden-invalid", detail: `query missing id/q/mode` });
19952
+ } else {
19953
+ findings.push({ ok: true, code: "golden-ok", detail: `${q.id}: shape ok` });
19954
+ }
19955
+ }
19956
+ }
19957
+ return { ok: findings.every((f) => f.ok), findings };
19958
+ }
19959
+
19960
+ // src/spawn-policy-core.ts
19961
+ var import_node_child_process13 = require("node:child_process");
19962
+ var import_node_fs25 = require("node:fs");
19963
+ var import_node_path23 = require("node:path");
19305
19964
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
19306
19965
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
19307
19966
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -19370,7 +20029,7 @@ function findViolationsInSource(raw) {
19370
20029
  return found;
19371
20030
  }
19372
20031
  function policedFiles(root) {
19373
- const r = (0, import_node_child_process11.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
20032
+ const r = (0, import_node_child_process13.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
19374
20033
  cwd: root,
19375
20034
  encoding: "utf8",
19376
20035
  windowsHide: true,
@@ -19387,7 +20046,7 @@ function runSpawnPolicy(root) {
19387
20046
  for (const file of files) {
19388
20047
  let raw;
19389
20048
  try {
19390
- raw = (0, import_node_fs22.readFileSync)((0, import_node_path20.join)(root, file), "utf8");
20049
+ raw = (0, import_node_fs25.readFileSync)((0, import_node_path23.join)(root, file), "utf8");
19391
20050
  } catch {
19392
20051
  continue;
19393
20052
  }
@@ -19404,9 +20063,9 @@ function runSpawnPolicy(root) {
19404
20063
  }
19405
20064
 
19406
20065
  // src/test-policy-core.ts
19407
- var import_node_child_process12 = require("node:child_process");
19408
- var import_node_fs23 = require("node:fs");
19409
- var import_node_path21 = require("node:path");
20066
+ var import_node_child_process14 = require("node:child_process");
20067
+ var import_node_fs26 = require("node:fs");
20068
+ var import_node_path24 = require("node:path");
19410
20069
  var POLICY_FILE = "test-policy.json";
19411
20070
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
19412
20071
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -19459,7 +20118,7 @@ function isTestPath(path2) {
19459
20118
  return TEST_RE.test(path2) || PY_TEST_RE.test(path2);
19460
20119
  }
19461
20120
  function loadPolicy(root, readFile9 = readFileOrNull2) {
19462
- const raw = readFile9((0, import_node_path21.join)(root, POLICY_FILE));
20121
+ const raw = readFile9((0, import_node_path24.join)(root, POLICY_FILE));
19463
20122
  if (raw == null) return { mandatory: [], declared: false };
19464
20123
  try {
19465
20124
  return { ...JSON.parse(raw), declared: true };
@@ -19469,7 +20128,7 @@ function loadPolicy(root, readFile9 = readFileOrNull2) {
19469
20128
  }
19470
20129
  function readFileOrNull2(path2) {
19471
20130
  try {
19472
- return (0, import_node_fs23.readFileSync)(path2, "utf8");
20131
+ return (0, import_node_fs26.readFileSync)(path2, "utf8");
19473
20132
  } catch {
19474
20133
  return null;
19475
20134
  }
@@ -19496,12 +20155,12 @@ function classify(changed, policy, present = () => false) {
19496
20155
  const removedProtected = [...removed].filter((p) => protectedBy.has(p)).map((p) => ({ path: p, why: protectedBy.get(p) ?? "" }));
19497
20156
  return { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected };
19498
20157
  }
19499
- function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
19500
- return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path21.join)(root, p)));
20158
+ function unresolvedProtectedEntries(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
20159
+ return (policy.protected ?? []).map((p) => p.path).filter((p) => !exists((0, import_node_path24.join)(root, p)));
19501
20160
  }
19502
- function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs23.existsSync)(path2)) {
20161
+ function unresolvedSatisfiers(policy, root, exists = (path2) => (0, import_node_fs26.existsSync)(path2)) {
19503
20162
  const declared = (policy.mandatory ?? []).flatMap((m) => m.satisfiedBy ?? []);
19504
- return [...new Set(declared)].filter((p) => !exists((0, import_node_path21.join)(root, p)));
20163
+ return [...new Set(declared)].filter((p) => !exists((0, import_node_path24.join)(root, p)));
19505
20164
  }
19506
20165
  function evaluate(changed, policy, present = () => false) {
19507
20166
  const { mandatoryHits, untestedHits, testChanges, addedTests, removedProtected } = classify(changed, policy, present);
@@ -19534,14 +20193,14 @@ function evaluate(changed, policy, present = () => false) {
19534
20193
  return findings;
19535
20194
  }
19536
20195
  function git(args, cwd) {
19537
- return (0, import_node_child_process12.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
20196
+ return (0, import_node_child_process14.execFileSync)("git", args, { windowsHide: true, cwd, encoding: "utf8", maxBuffer: 32 * 1024 * 1024 });
19538
20197
  }
19539
20198
  var COAUTHOR_KEY = "Co-authored-by";
19540
20199
  var GH_MESSAGE_SEPARATOR = /^-{5,}$/;
19541
20200
  var LIFTED_KEYS = new RegExp(`^(?:${TRAILER_KEY}|${COAUTHOR_KEY}):`, "i");
19542
20201
  function parseTrailers(message, cwd) {
19543
20202
  try {
19544
- return (0, import_node_child_process12.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
20203
+ return (0, import_node_child_process14.execFileSync)("git", ["interpret-trailers", "--parse", "--unfold"], {
19545
20204
  windowsHide: true,
19546
20205
  cwd,
19547
20206
  input: message,
@@ -19683,13 +20342,13 @@ function changedFilesSince(base, cwd) {
19683
20342
  }
19684
20343
  function runTestPolicy(root, deps = {}) {
19685
20344
  const policy = deps.policy ?? loadPolicy(root);
19686
- const exists = deps.exists ?? ((path2) => (0, import_node_fs23.existsSync)(path2));
20345
+ const exists = deps.exists ?? ((path2) => (0, import_node_fs26.existsSync)(path2));
19687
20346
  const counts = { mandatoryCount: (policy.mandatory ?? []).length, protectedCount: (policy.protected ?? []).length };
19688
20347
  const base = deps.changed ? "(injected)" : resolveBase(root, deps.base);
19689
20348
  const refusal = deps.changed ? null : untrustworthyRange(root, base);
19690
20349
  const changed = deps.changed ?? (refusal ? [] : changedFilesSince(base, root));
19691
20350
  const lookup = deps.override !== void 0 ? { override: deps.override, refusals: [] } : !deps.changed && !refusal ? readOverride(base, root) : { override: null, refusals: [] };
19692
- const present = (path2) => exists((0, import_node_path21.join)(root, path2));
20351
+ const present = (path2) => exists((0, import_node_path24.join)(root, path2));
19693
20352
  const removedByThisDiff = removedPaths(changed);
19694
20353
  const staleFindings = [];
19695
20354
  const unresolved = unresolvedProtectedEntries(policy, root, exists).filter((p) => !removedByThisDiff.has(p));
@@ -19845,8 +20504,8 @@ function docsAuditStatus(fetch2, opts) {
19845
20504
  }
19846
20505
 
19847
20506
  // src/project-info-sync.ts
19848
- var import_node_fs24 = require("node:fs");
19849
- var import_node_path22 = require("node:path");
20507
+ var import_node_fs27 = require("node:fs");
20508
+ var import_node_path25 = require("node:path");
19850
20509
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
19851
20510
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
19852
20511
  projectV2 { id }
@@ -19891,14 +20550,14 @@ function sharedName(entries, fallback) {
19891
20550
  }
19892
20551
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19893
20552
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
19894
- const readmePath = (0, import_node_path22.join)(repoRoot2, "README.md");
19895
- if (!(0, import_node_fs24.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
20553
+ const readmePath = (0, import_node_path25.join)(repoRoot2, "README.md");
20554
+ if (!(0, import_node_fs27.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
19896
20555
  const entries = entriesFor(project2, projects);
19897
20556
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
19898
20557
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
19899
20558
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
19900
20559
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
19901
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs24.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
20560
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs27.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
19902
20561
  const lines = [
19903
20562
  `# ${projectName}`,
19904
20563
  "",
@@ -19917,8 +20576,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
19917
20576
  const targetBase = `https://github.com/${targetRepo2}`;
19918
20577
  const targetBranch = branchFor(targetRepo2, projects);
19919
20578
  const orgDocs = [
19920
- (0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
19921
- (0, import_node_fs24.existsSync)((0, import_node_path22.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
20579
+ (0, import_node_fs27.existsSync)((0, import_node_path25.join)(repoRoot2, "docs", "org-readme.md")) ? `- [Org identity](${targetBase}/blob/${targetBranch}/docs/org-readme.md)` : "",
20580
+ (0, import_node_fs27.existsSync)((0, import_node_path25.join)(repoRoot2, "docs", "org-architecture.md")) ? `- [Org architecture](${targetBase}/blob/${targetBranch}/docs/org-architecture.md)` : ""
19922
20581
  ].filter(Boolean);
19923
20582
  if (orgDocs.length) lines.push("", "## Organisation docs", "", ...orgDocs);
19924
20583
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines.join("\n")}
@@ -20777,9 +21436,9 @@ function writeError(res) {
20777
21436
  }
20778
21437
 
20779
21438
  // src/secrets-commands.ts
20780
- var import_node_fs25 = require("node:fs");
20781
- var import_node_path23 = require("node:path");
20782
- var import_node_os9 = require("node:os");
21439
+ var import_node_fs28 = require("node:fs");
21440
+ var import_node_path26 = require("node:path");
21441
+ var import_node_os10 = require("node:os");
20783
21442
 
20784
21443
  // src/project-runtime.ts
20785
21444
  function hasRuntimeSecretContract(contract) {
@@ -20902,18 +21561,18 @@ function collectMap(value, previous = []) {
20902
21561
  return [...previous, value];
20903
21562
  }
20904
21563
  async function decryptRailsCredentials(input) {
20905
- const appDir = (0, import_node_path23.resolve)(input.appDir ?? process.cwd());
21564
+ const appDir = (0, import_node_path26.resolve)(input.appDir ?? process.cwd());
20906
21565
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
20907
21566
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
20908
- const credentialsPath = (0, import_node_path23.resolve)(appDir, credentialsFile);
20909
- const masterKeyPath = (0, import_node_path23.resolve)(appDir, masterKeyFile);
21567
+ const credentialsPath = (0, import_node_path26.resolve)(appDir, credentialsFile);
21568
+ const masterKeyPath = (0, import_node_path26.resolve)(appDir, masterKeyFile);
20910
21569
  const env = {
20911
21570
  ...process.env,
20912
21571
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
20913
21572
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
20914
21573
  };
20915
- if ((0, import_node_fs25.existsSync)(masterKeyPath)) {
20916
- env.RAILS_MASTER_KEY = (0, import_node_fs25.readFileSync)(masterKeyPath, "utf8").trim();
21574
+ if ((0, import_node_fs28.existsSync)(masterKeyPath)) {
21575
+ env.RAILS_MASTER_KEY = (0, import_node_fs28.readFileSync)(masterKeyPath, "utf8").trim();
20917
21576
  }
20918
21577
  const script = [
20919
21578
  'require "json"',
@@ -20923,9 +21582,9 @@ async function decryptRailsCredentials(input) {
20923
21582
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
20924
21583
  "puts JSON.generate(config.config)"
20925
21584
  ].join("\n");
20926
- const scriptDir = (0, import_node_fs25.mkdtempSync)((0, import_node_path23.join)((0, import_node_os9.tmpdir)(), "mmi-rails-decrypt-"));
20927
- const scriptPath = (0, import_node_path23.join)(scriptDir, "decrypt.rb");
20928
- (0, import_node_fs25.writeFileSync)(scriptPath, script, "utf8");
21585
+ const scriptDir = (0, import_node_fs28.mkdtempSync)((0, import_node_path26.join)((0, import_node_os10.tmpdir)(), "mmi-rails-decrypt-"));
21586
+ const scriptPath = (0, import_node_path26.join)(scriptDir, "decrypt.rb");
21587
+ (0, import_node_fs28.writeFileSync)(scriptPath, script, "utf8");
20929
21588
  try {
20930
21589
  const args = ["exec", "ruby", scriptPath];
20931
21590
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -20937,7 +21596,7 @@ async function decryptRailsCredentials(input) {
20937
21596
  });
20938
21597
  return JSON.parse(stdout);
20939
21598
  } finally {
20940
- (0, import_node_fs25.rmSync)(scriptDir, { recursive: true, force: true });
21599
+ (0, import_node_fs28.rmSync)(scriptDir, { recursive: true, force: true });
20941
21600
  }
20942
21601
  }
20943
21602
  async function readSecretStdin() {
@@ -21027,7 +21686,7 @@ function registerSecretsCommands(program3) {
21027
21686
  let body;
21028
21687
  if (o.file) {
21029
21688
  try {
21030
- body = (0, import_node_fs25.readFileSync)((0, import_node_path23.resolve)(o.file), "utf8");
21689
+ body = (0, import_node_fs28.readFileSync)((0, import_node_path26.resolve)(o.file), "utf8");
21031
21690
  } catch (e) {
21032
21691
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
21033
21692
  }
@@ -21132,7 +21791,7 @@ function registerSecretsCommands(program3) {
21132
21791
  {
21133
21792
  ...d,
21134
21793
  decryptRailsCredentials,
21135
- removeFile: (path2) => (0, import_node_fs25.unlinkSync)((0, import_node_path23.resolve)(o.appDir ?? process.cwd(), path2))
21794
+ removeFile: (path2) => (0, import_node_fs28.unlinkSync)((0, import_node_path26.resolve)(o.appDir ?? process.cwd(), path2))
21136
21795
  },
21137
21796
  {
21138
21797
  repo: o.repo,
@@ -21177,7 +21836,7 @@ function registerSecretsCommands(program3) {
21177
21836
  }
21178
21837
 
21179
21838
  // src/app-actor.ts
21180
- var import_node_crypto4 = require("node:crypto");
21839
+ var import_node_crypto5 = require("node:crypto");
21181
21840
  var APP_ACTOR_ENV = "MMI_ACTOR";
21182
21841
  var APP_VAULT_REPO = "mutmutco/MMI-Hub";
21183
21842
  var APP_VAULT_KEYS = ["GITHUB_APP_ID", "GITHUB_APP_INSTALLATION_ID", "GITHUB_APP_PRIVATE_KEY"];
@@ -21221,7 +21880,7 @@ function mintAppJwt(appId, privateKeyPem, nowSec) {
21221
21880
  exp: now + APP_JWT_TTL_S,
21222
21881
  iss: appId
21223
21882
  }));
21224
- const signer = (0, import_node_crypto4.createSign)("RSA-SHA256");
21883
+ const signer = (0, import_node_crypto5.createSign)("RSA-SHA256");
21225
21884
  signer.update(`${header}.${payload}`);
21226
21885
  return `${header}.${payload}.${signer.sign(privateKeyPem, "base64url")}`;
21227
21886
  }
@@ -21296,7 +21955,7 @@ async function activateAppActor(commandPath3, env, mint) {
21296
21955
  }
21297
21956
 
21298
21957
  // src/box-commands.ts
21299
- var import_node_fs26 = require("node:fs");
21958
+ var import_node_fs29 = require("node:fs");
21300
21959
 
21301
21960
  // src/box.ts
21302
21961
  var BOX_KEYS = {
@@ -21499,7 +22158,7 @@ function registerBoxCommands(program3) {
21499
22158
  }
21500
22159
  if (o.json) console.log(JSON.stringify({ box: found, incomplete }, null, 2));
21501
22160
  else if (o.ssh && o.script) {
21502
- (0, import_node_fs26.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
22161
+ (0, import_node_fs29.writeFileSync)(o.script, sshRecipeScript(found), "utf8");
21503
22162
  console.log(`wrote ${o.script} \u2014 run: bash "${o.script}"`);
21504
22163
  } else if (o.ssh) console.log(`${formatSshRecipe(found)}
21505
22164
  ${SSH_RECIPE_AGENT_NOTE}`);
@@ -21514,7 +22173,7 @@ ${SSH_RECIPE_AGENT_NOTE}`);
21514
22173
 
21515
22174
  // src/schedules-commands.ts
21516
22175
  var import_promises4 = require("node:fs/promises");
21517
- var import_node_child_process13 = require("node:child_process");
22176
+ var import_node_child_process15 = require("node:child_process");
21518
22177
  var import_node_util7 = require("node:util");
21519
22178
 
21520
22179
  // src/schedules.ts
@@ -21638,9 +22297,9 @@ function extractWorkflowCrons(yamlText) {
21638
22297
  function workflowEntry(repo, workflowPath, yamlText) {
21639
22298
  const crons = extractWorkflowCrons(yamlText);
21640
22299
  if (!crons.length) return null;
21641
- const basename4 = workflowPath.split("/").pop() ?? workflowPath;
22300
+ const basename5 = workflowPath.split("/").pop() ?? workflowPath;
21642
22301
  return {
21643
- name: `${repo}/${basename4.replace(/\.ya?ml$/, "")}`,
22302
+ name: `${repo}/${basename5.replace(/\.ya?ml$/, "")}`,
21644
22303
  cadence: crons.join(" + "),
21645
22304
  executor: "github-actions",
21646
22305
  llm: llmFromHeader(yamlText),
@@ -21753,6 +22412,9 @@ var DECLARED_ENTRIES = [
21753
22412
  // unavailable exactly when it is needed. This one is root cron on the box: no checkout, no node, and
21754
22413
  // a no-op below 90% disk, which is why 15 minutes is affordable.
21755
22414
  { name: "mmi-runner-hygiene-oob (mmi-runner)", cadence: "*/15 * * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-runner-hygiene \u2192 /opt/mmi-control/runner-hygiene-oob.sh; acts only at >=90% disk (verify: mmi-cli runtime box get mmi-runner --ssh)" },
22415
+ // #4118: Listening + systemd-active ≠ GitHub online after acquirejob 503. restart.conf cannot see
22416
+ // it; an Actions workflow on mmi-live cannot heal it when half the fleet is offline. Root cron.
22417
+ { name: "mmi-runner-online-reconcile (mmi-runner)", cadence: "*/5 * * * *", executor: "box cron.d", llm: "no", resolved: "declared", source: "/etc/cron.d/mmi-runner-online \u2192 /opt/mmi-control/runner-online-reconcile.sh; API-offline + _diag acquirejob, max 2 restarts/tick (verify: mmi-cli runtime box get mmi-runner --ssh)" },
21756
22418
  { name: "zuber-bake", cadence: "*:05/30 (every 30 min)", executor: "zuber systemd timer", llm: "no", resolved: "declared", source: "zuber-bake.timer \u2192 rolling overlay bake, metro tier, next ~6h window (verify over ssh)" },
21757
22419
  { name: "zuber-bake-full", cadence: "00:30 UTC (03:30 TRT, daily)", executor: "zuber systemd timer", llm: "no", resolved: "declared", source: "zuber-bake-full.timer \u2192 full 24h overlay rebuild, all 81 il (verify over ssh)" }
21758
22420
  ];
@@ -21948,7 +22610,7 @@ function assembleReconciliation(githubEntries2, readRepos, registry2, activeWork
21948
22610
  }
21949
22611
 
21950
22612
  // src/schedules-commands.ts
21951
- var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process13.execFile);
22613
+ var execFileP5 = (0, import_node_util7.promisify)(import_node_child_process15.execFile);
21952
22614
  var AWS_REGION = "eu-central-1";
21953
22615
  var AWS_TIMEOUT_MS = 3e4;
21954
22616
  var AWS_RETRY_DELAY_MS = 1500;
@@ -21982,8 +22644,8 @@ async function repoWorkflowEntries(client, repo) {
21982
22644
  );
21983
22645
  if (typeof contents?.content !== "string" || contents.encoding !== "base64") continue;
21984
22646
  const text = Buffer.from(contents.content, "base64").toString("utf8");
21985
- const basename4 = wf.path.split("/").pop() ?? wf.path;
21986
- const name = `${repo}/${basename4.replace(/\.ya?ml$/, "")}`;
22647
+ const basename5 = wf.path.split("/").pop() ?? wf.path;
22648
+ const name = `${repo}/${basename5.replace(/\.ya?ml$/, "")}`;
21987
22649
  workflows.push({ name, yamlText: text });
21988
22650
  const entry = workflowEntry(repo, wf.path, text);
21989
22651
  if (entry) entries.push(entry);
@@ -22114,7 +22776,7 @@ function reportDrift(drift) {
22114
22776
  for (const d of drift) console.error(`org schedules: DRIFT \u2014 ${d}`);
22115
22777
  }
22116
22778
  function registerSchedulesCommands(program3) {
22117
- const schedules = program3.command("schedules").description("the org schedules notebook \u2014 every armed cron, timer, and scheduled LLM lane, resolved live (jerv side lives in jerv-cli schedules)").option("--json", "machine-readable output (consumed by jerv-cli schedules --all)").option("--doc <path>", "splice the generated inventory into the given doc between the schedules:inventory markers (docs/schedules.md)").action(async (o) => {
22779
+ const schedules = program3.command("schedules").description("the org schedules notebook \u2014 every armed cron, timer, and scheduled LLM lane, resolved live (jerv side lives in jerv-cli schedules)").option("--json", "machine-readable output (consumed by jerv-cli schedules --all)").option("--doc <path>", "optional snapshot only \u2014 splice inventory between schedules:inventory markers; live `org schedules` / --json remains SSOT (Hub#4120)").action(async (o) => {
22118
22780
  try {
22119
22781
  const { entries, incomplete, drift, reconciliation } = await fetchNotebook();
22120
22782
  if (o.doc) {
@@ -22185,7 +22847,7 @@ function registerSchedulesCommands(program3) {
22185
22847
 
22186
22848
  // src/file-lock.ts
22187
22849
  var import_promises5 = require("node:fs/promises");
22188
- var import_node_path24 = require("node:path");
22850
+ var import_node_path27 = require("node:path");
22189
22851
  var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
22190
22852
  var IMMEDIATE_RETRY_BUDGET = 3;
22191
22853
  var FileLockBusyError = class extends Error {
@@ -22270,7 +22932,7 @@ async function releaseFileLock(lockPath, guard) {
22270
22932
  }
22271
22933
  async function withFileLock(lockPath, opts, fn) {
22272
22934
  const resolved = resolveFileLockOpts(opts);
22273
- await (0, import_promises5.mkdir)((0, import_node_path24.dirname)(lockPath), { recursive: true }).catch(() => void 0);
22935
+ await (0, import_promises5.mkdir)((0, import_node_path27.dirname)(lockPath), { recursive: true }).catch(() => void 0);
22274
22936
  const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
22275
22937
  try {
22276
22938
  return await fn();
@@ -22281,7 +22943,7 @@ async function withFileLock(lockPath, opts, fn) {
22281
22943
 
22282
22944
  // src/schedules-lift-command.ts
22283
22945
  var import_promises6 = require("node:fs/promises");
22284
- var import_node_path25 = require("node:path");
22946
+ var import_node_path28 = require("node:path");
22285
22947
 
22286
22948
  // src/schedules-lift.ts
22287
22949
  var SCHEDULE_HEADER_FIELDS = ["schedule", "what", "owner", "cadence", "executor", "llm", "output", "breaks", "kill"];
@@ -22303,8 +22965,8 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
22303
22965
  const crons = extractWorkflowCrons(yamlText);
22304
22966
  const header = parseScheduleHeader(yamlText);
22305
22967
  if (!crons.length && !header.schedule) return null;
22306
- const basename4 = workflowPath.split("/").pop() ?? workflowPath;
22307
- const expectedId = `${repo}/${basename4.replace(/\.ya?ml$/, "")}`;
22968
+ const basename5 = workflowPath.split("/").pop() ?? workflowPath;
22969
+ const expectedId = `${repo}/${basename5.replace(/\.ya?ml$/, "")}`;
22308
22970
  const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
22309
22971
  if (missing.length) {
22310
22972
  throw new Error(`schedules register: ${expectedId} (${workflowPath}) is a scheduled workflow but is missing the eight-field entry-template header field(s): ${missing.join(", ")} \u2014 see docs/schedules.md "The entry template".`);
@@ -22387,7 +23049,7 @@ async function readWorkflowFiles(dir) {
22387
23049
  const files = [];
22388
23050
  for (const name of names.sort()) {
22389
23051
  if (!/\.ya?ml$/.test(name)) continue;
22390
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path25.join)(dir, name), "utf8") });
23052
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises6.readFile)((0, import_node_path28.join)(dir, name), "utf8") });
22391
23053
  }
22392
23054
  return files;
22393
23055
  }
@@ -22929,9 +23591,9 @@ function registerQueryCommands(program3) {
22929
23591
  }
22930
23592
 
22931
23593
  // src/bootstrap-commands.ts
22932
- var import_node_fs27 = require("node:fs");
22933
- var import_node_os10 = require("node:os");
22934
- var import_node_path26 = require("node:path");
23594
+ var import_node_fs30 = require("node:fs");
23595
+ var import_node_os11 = require("node:os");
23596
+ var import_node_path29 = require("node:path");
22935
23597
 
22936
23598
  // src/bootstrap-drift.ts
22937
23599
  function byteComparableSeeds(manifest, cls) {
@@ -23644,13 +24306,13 @@ function registerBootstrapCommands(program3) {
23644
24306
  client: defaultGitHubClient(),
23645
24307
  projectMeta: meta,
23646
24308
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
23647
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs27.existsSync)(path2) ? (0, import_node_fs27.readFileSync)(path2, "utf8") : null,
24309
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs30.existsSync)(path2) ? (0, import_node_fs30.readFileSync)(path2, "utf8") : null,
23648
24310
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
23649
24311
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
23650
24312
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
23651
24313
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
23652
24314
  // sanction, which is the pre-#3664 behaviour.
23653
- sanctionedAdmins: (0, import_node_fs27.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs27.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
24315
+ sanctionedAdmins: (0, import_node_fs30.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs30.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
23654
24316
  requiredGcpApis: (() => {
23655
24317
  const v = meta?.requiredGcpApis;
23656
24318
  if (Array.isArray(v)) return v;
@@ -23703,12 +24365,12 @@ function registerBootstrapCommands(program3) {
23703
24365
  bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
23704
24366
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
23705
24367
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23706
- if (!(0, import_node_fs27.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
23707
- const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
24368
+ if (!(0, import_node_fs30.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
24369
+ const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
23708
24370
  const hubContents = /* @__PURE__ */ new Map();
23709
24371
  for (const s of manifest.seeds) {
23710
24372
  if (s.ownership !== "org" || s.source !== "self") continue;
23711
- hubContents.set(s.target, (0, import_node_fs27.existsSync)(s.target) ? (0, import_node_fs27.readFileSync)(s.target, "utf8") : null);
24373
+ hubContents.set(s.target, (0, import_node_fs30.existsSync)(s.target) ? (0, import_node_fs30.readFileSync)(s.target, "utf8") : null);
23712
24374
  }
23713
24375
  let targets;
23714
24376
  let classOf = (_repo) => "deployable";
@@ -23785,8 +24447,8 @@ function registerBootstrapCommands(program3) {
23785
24447
  return fail(`bootstrap apply: ${e.message}`);
23786
24448
  }
23787
24449
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
23788
- if (!(0, import_node_fs27.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
23789
- const manifest = loadBootstrapSeeds((0, import_node_fs27.readFileSync)(manifestPath, "utf8"));
24450
+ if (!(0, import_node_fs30.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
24451
+ const manifest = loadBootstrapSeeds((0, import_node_fs30.readFileSync)(manifestPath, "utf8"));
23790
24452
  const baseBranch = o.class === "content" ? "main" : "development";
23791
24453
  const slug = parsedRepo.slug;
23792
24454
  const onlyTarget = o.only.trim();
@@ -23797,16 +24459,16 @@ function registerBootstrapCommands(program3) {
23797
24459
  ${known}`);
23798
24460
  }
23799
24461
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
23800
- const readFile9 = (p) => (0, import_node_fs27.existsSync)(p) ? (0, import_node_fs27.readFileSync)(p, "utf8") : null;
24462
+ const readFile9 = (p) => (0, import_node_fs30.existsSync)(p) ? (0, import_node_fs30.readFileSync)(p, "utf8") : null;
23801
24463
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
23802
24464
  const putSeed = async (target, content, ref, sha) => {
23803
- const tmp = (0, import_node_path26.join)((0, import_node_os10.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
23804
- (0, import_node_fs27.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
24465
+ 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`);
24466
+ (0, import_node_fs30.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
23805
24467
  try {
23806
24468
  await gh(contentPutInputArgs(repo, target, tmp));
23807
24469
  } finally {
23808
24470
  try {
23809
- (0, import_node_fs27.unlinkSync)(tmp);
24471
+ (0, import_node_fs30.unlinkSync)(tmp);
23810
24472
  } catch {
23811
24473
  }
23812
24474
  }
@@ -24063,12 +24725,12 @@ LIVE apply to ${repo}:
24063
24725
  }
24064
24726
 
24065
24727
  // src/stage-commands.ts
24066
- var import_node_fs29 = require("node:fs");
24067
- var import_node_path28 = require("node:path");
24728
+ var import_node_fs32 = require("node:fs");
24729
+ var import_node_path31 = require("node:path");
24068
24730
 
24069
24731
  // src/port-registry.ts
24070
- var import_node_fs28 = require("node:fs");
24071
- var import_node_path27 = require("node:path");
24732
+ var import_node_fs31 = require("node:fs");
24733
+ var import_node_path30 = require("node:path");
24072
24734
 
24073
24735
  // ../infra/port-geometry.mjs
24074
24736
  var PORT_BLOCK = 100;
@@ -24082,8 +24744,8 @@ function nextPortBlock(registry2) {
24082
24744
  return [base, base + PORT_SPAN];
24083
24745
  }
24084
24746
  function loadPortRegistry(path2) {
24085
- if (!(0, import_node_fs28.existsSync)(path2)) return {};
24086
- const raw = JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8"));
24747
+ if (!(0, import_node_fs31.existsSync)(path2)) return {};
24748
+ const raw = JSON.parse((0, import_node_fs31.readFileSync)(path2, "utf8"));
24087
24749
  const out = {};
24088
24750
  for (const [key, value] of Object.entries(raw)) {
24089
24751
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -24097,9 +24759,9 @@ function ensurePortRange(repo, path2) {
24097
24759
  const existing = registry2[repo];
24098
24760
  if (existing) return existing;
24099
24761
  const range = nextPortBlock(registry2);
24100
- const raw = (0, import_node_fs28.existsSync)(path2) ? JSON.parse((0, import_node_fs28.readFileSync)(path2, "utf8")) : {};
24762
+ const raw = (0, import_node_fs31.existsSync)(path2) ? JSON.parse((0, import_node_fs31.readFileSync)(path2, "utf8")) : {};
24101
24763
  raw[repo] = range;
24102
- (0, import_node_fs28.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
24764
+ (0, import_node_fs31.writeFileSync)(path2, JSON.stringify(raw, null, 2) + "\n", "utf8");
24103
24765
  return range;
24104
24766
  }
24105
24767
  function portCursorSeed(registry2) {
@@ -24121,22 +24783,22 @@ function existingPortRange(repo, registry2) {
24121
24783
  return registry2[repo] ?? null;
24122
24784
  }
24123
24785
  function portRangeInfraAt(root, source) {
24124
- const registryPath = (0, import_node_path27.join)(root, "infra", "port-ranges.json");
24125
- const ddbScriptPath = (0, import_node_path27.join)(root, "infra", "port-ddb.mjs");
24126
- if (!(0, import_node_fs28.existsSync)(registryPath) || !(0, import_node_fs28.existsSync)(ddbScriptPath)) return null;
24786
+ const registryPath = (0, import_node_path30.join)(root, "infra", "port-ranges.json");
24787
+ const ddbScriptPath = (0, import_node_path30.join)(root, "infra", "port-ddb.mjs");
24788
+ if (!(0, import_node_fs31.existsSync)(registryPath) || !(0, import_node_fs31.existsSync)(ddbScriptPath)) return null;
24127
24789
  return { root, source, registryPath, ddbScriptPath };
24128
24790
  }
24129
24791
  function resolvePortRangeInfra(cwd, packageDir) {
24130
24792
  const direct = portRangeInfraAt(cwd, "cwd");
24131
24793
  if (direct) return direct;
24132
- for (let dir = cwd; ; dir = (0, import_node_path27.dirname)(dir)) {
24133
- const sibling = portRangeInfraAt((0, import_node_path27.join)(dir, "MMI-Hub"), "sibling-hub");
24794
+ for (let dir = cwd; ; dir = (0, import_node_path30.dirname)(dir)) {
24795
+ const sibling = portRangeInfraAt((0, import_node_path30.join)(dir, "MMI-Hub"), "sibling-hub");
24134
24796
  if (sibling) return sibling;
24135
- const parent = (0, import_node_path27.dirname)(dir);
24797
+ const parent = (0, import_node_path30.dirname)(dir);
24136
24798
  if (parent === dir) break;
24137
24799
  }
24138
24800
  if (packageDir) {
24139
- const pkgRoot = (0, import_node_path27.join)(packageDir, "..", "..");
24801
+ const pkgRoot = (0, import_node_path30.join)(packageDir, "..", "..");
24140
24802
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
24141
24803
  if (pkgFrom) return pkgFrom;
24142
24804
  }
@@ -24330,8 +24992,8 @@ function registerStageCommands(program3) {
24330
24992
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
24331
24993
  return decideStage({
24332
24994
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
24333
- hasCompose: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), "docker-compose.yml")),
24334
- hasEnvExample: (0, import_node_fs29.existsSync)((0, import_node_path28.join)(process.cwd(), ".env.example"))
24995
+ hasCompose: (0, import_node_fs32.existsSync)((0, import_node_path31.join)(process.cwd(), "docker-compose.yml")),
24996
+ hasEnvExample: (0, import_node_fs32.existsSync)((0, import_node_path31.join)(process.cwd(), ".env.example"))
24335
24997
  });
24336
24998
  }
24337
24999
  async function fetchStageVaultEnvMerge() {
@@ -24769,11 +25431,11 @@ function registerBoardCommands(program3) {
24769
25431
  }
24770
25432
 
24771
25433
  // src/merge-cleanup.ts
24772
- var import_node_fs30 = require("node:fs");
25434
+ var import_node_fs33 = require("node:fs");
24773
25435
  var import_promises8 = require("node:fs/promises");
24774
- var import_node_path30 = require("node:path");
24775
- var import_node_os11 = require("node:os");
24776
- var import_node_child_process14 = require("node:child_process");
25436
+ var import_node_path33 = require("node:path");
25437
+ var import_node_os12 = require("node:os");
25438
+ var import_node_child_process16 = require("node:child_process");
24777
25439
 
24778
25440
  // src/board-advance.ts
24779
25441
  function repoOf2(ref) {
@@ -24859,7 +25521,7 @@ function boardAdvanceFailureMessage(result) {
24859
25521
 
24860
25522
  // src/deferred-registry-store.ts
24861
25523
  var import_promises7 = require("node:fs/promises");
24862
- var import_node_path29 = require("node:path");
25524
+ var import_node_path32 = require("node:path");
24863
25525
  var sleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
24864
25526
  async function atomicWrite(target, contents) {
24865
25527
  const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
@@ -24910,12 +25572,12 @@ function makeDeferredWorktreeStore(registryPath, lockOpts = {}) {
24910
25572
  },
24911
25573
  // Standalone atomic write — THROWS on failure (no best-effort swallow, #2846).
24912
25574
  write: async (entries) => {
24913
- await (0, import_promises7.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
25575
+ await (0, import_promises7.mkdir)((0, import_node_path32.dirname)(registryPath), { recursive: true });
24914
25576
  await atomicWrite(registryPath, serializeDeferredWorktrees(entries));
24915
25577
  },
24916
25578
  // Serialized read-modify-write under the repo-wide lock (#2846).
24917
25579
  update: async (mutate) => {
24918
- await (0, import_promises7.mkdir)((0, import_node_path29.dirname)(registryPath), { recursive: true });
25580
+ await (0, import_promises7.mkdir)((0, import_node_path32.dirname)(registryPath), { recursive: true });
24919
25581
  const deadline = Date.now() + opts.maxWaitMs;
24920
25582
  for (; ; ) {
24921
25583
  const guard = await acquireLock(lockPath, opts, deadline);
@@ -25063,7 +25725,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
25063
25725
  );
25064
25726
  const repoRoot2 = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
25065
25727
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
25066
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path30.dirname)((0, import_node_path30.dirname)(worktreeGitRoot)) : repoRoot2;
25728
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path33.dirname)((0, import_node_path33.dirname)(worktreeGitRoot)) : repoRoot2;
25067
25729
  const gcActor = describeActor({ env: process.env, surface: detectSurface(process.env), cwd: process.cwd() });
25068
25730
  const owners = readWorktreeOwners(primaryRepoRoot);
25069
25731
  const removalNow = Date.now();
@@ -25094,7 +25756,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
25094
25756
  const cleanup = await cleanupPrMergeLocalBranch(branch.branch, {
25095
25757
  beforeWorktrees,
25096
25758
  startingPath: branch.worktreePath,
25097
- pathExists: (p) => (0, import_node_fs30.existsSync)(p),
25759
+ pathExists: (p) => (0, import_node_fs33.existsSync)(p),
25098
25760
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
25099
25761
  teardownWorktreeStage,
25100
25762
  deferredStore,
@@ -25122,7 +25784,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
25122
25784
  let removalAttempted = false;
25123
25785
  try {
25124
25786
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
25125
- realpath: (path2) => (0, import_node_fs30.realpathSync)(path2)
25787
+ realpath: (path2) => (0, import_node_fs33.realpathSync)(path2)
25126
25788
  });
25127
25789
  if (!cleanupTarget.ok) {
25128
25790
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -25208,13 +25870,13 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh) {
25208
25870
  const commits = JSON.parse(raw).commits ?? [];
25209
25871
  const body = squashBodyWithOverride(commits.map((c) => ({ headline: c.messageHeadline ?? "", body: c.messageBody ?? "" })), process.cwd());
25210
25872
  if (!body) return void 0;
25211
- const dir = (0, import_node_fs30.mkdtempSync)((0, import_node_path30.join)((0, import_node_os11.tmpdir)(), "mmi-squash-body-"));
25212
- const path2 = (0, import_node_path30.join)(dir, "body.txt");
25213
- (0, import_node_fs30.writeFileSync)(path2, `${body}
25873
+ const dir = (0, import_node_fs33.mkdtempSync)((0, import_node_path33.join)((0, import_node_os12.tmpdir)(), "mmi-squash-body-"));
25874
+ const path2 = (0, import_node_path33.join)(dir, "body.txt");
25875
+ (0, import_node_fs33.writeFileSync)(path2, `${body}
25214
25876
  `, "utf8");
25215
25877
  return { path: path2, cleanup: () => {
25216
25878
  try {
25217
- (0, import_node_fs30.rmSync)(dir, { recursive: true, force: true });
25879
+ (0, import_node_fs33.rmSync)(dir, { recursive: true, force: true });
25218
25880
  } catch {
25219
25881
  }
25220
25882
  } };
@@ -25322,7 +25984,7 @@ async function remoteBranchExists2(branch, options = {}) {
25322
25984
  }
25323
25985
  var COMPOSE_TIMEOUT_MS = 12e4;
25324
25986
  function spawnDeferredGcSweep() {
25325
- spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process14.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
25987
+ spawnDetachedSelf(["worktree", "gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
25326
25988
  }
25327
25989
  async function createDeferredWorktreeStore() {
25328
25990
  try {
@@ -25336,13 +25998,13 @@ var realWorktreeDirRemover = {
25336
25998
  probe: (p) => {
25337
25999
  let st;
25338
26000
  try {
25339
- st = (0, import_node_fs30.lstatSync)(p);
26001
+ st = (0, import_node_fs33.lstatSync)(p);
25340
26002
  } catch {
25341
26003
  return null;
25342
26004
  }
25343
26005
  if (st.isSymbolicLink()) return "link";
25344
26006
  try {
25345
- (0, import_node_fs30.readlinkSync)(p);
26007
+ (0, import_node_fs33.readlinkSync)(p);
25346
26008
  return "link";
25347
26009
  } catch {
25348
26010
  }
@@ -25350,7 +26012,7 @@ var realWorktreeDirRemover = {
25350
26012
  },
25351
26013
  readdir: (p) => {
25352
26014
  try {
25353
- return (0, import_node_fs30.readdirSync)(p);
26015
+ return (0, import_node_fs33.readdirSync)(p);
25354
26016
  } catch {
25355
26017
  return [];
25356
26018
  }
@@ -25359,9 +26021,9 @@ var realWorktreeDirRemover = {
25359
26021
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
25360
26022
  detachLink: (p) => {
25361
26023
  try {
25362
- (0, import_node_fs30.rmdirSync)(p);
26024
+ (0, import_node_fs33.rmdirSync)(p);
25363
26025
  } catch {
25364
- (0, import_node_fs30.unlinkSync)(p);
26026
+ (0, import_node_fs33.unlinkSync)(p);
25365
26027
  }
25366
26028
  },
25367
26029
  removeTree: (p) => (0, import_promises8.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
@@ -25394,9 +26056,9 @@ async function worktreeHasStageState(worktreePath) {
25394
26056
  }
25395
26057
  }
25396
26058
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
25397
- if (!(0, import_node_fs30.existsSync)(statePath)) return false;
26059
+ if (!(0, import_node_fs33.existsSync)(statePath)) return false;
25398
26060
  try {
25399
- const state = JSON.parse((0, import_node_fs30.readFileSync)(statePath, "utf8"));
26061
+ const state = JSON.parse((0, import_node_fs33.readFileSync)(statePath, "utf8"));
25400
26062
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
25401
26063
  return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
25402
26064
  } catch {
@@ -25556,24 +26218,48 @@ var RUNNER_INFRA_ANNOTATION_TITLES = /* @__PURE__ */ new Set([
25556
26218
  CI_DISK_ANNOTATION_TITLE,
25557
26219
  CI_TOOLCHAIN_ANNOTATION_TITLE
25558
26220
  ]);
26221
+ var RUNNER_INFRA_ANNOTATION_MESSAGE_PATTERNS = [
26222
+ /not acquired by Runner/i,
26223
+ /Service Unavailable/i,
26224
+ /Failed to resolve action download info/i
26225
+ ];
26226
+ var RUNNER_INFRA_PRESTART_CONCLUSIONS = /* @__PURE__ */ new Set([
26227
+ "cancelled",
26228
+ "startup_failure"
26229
+ ]);
25559
26230
  function isErrorAnnotation(a) {
25560
26231
  const level = a.annotation_level?.toLowerCase();
25561
26232
  return level === "failure" || level === "error";
25562
26233
  }
26234
+ function isRunnerInfraAnnotation(a) {
26235
+ const title = a.title?.trim() ?? "";
26236
+ if (RUNNER_INFRA_ANNOTATION_TITLES.has(title)) return true;
26237
+ const text = `${title}
26238
+ ${a.message?.trim() ?? ""}`;
26239
+ return RUNNER_INFRA_ANNOTATION_MESSAGE_PATTERNS.some((re) => re.test(text));
26240
+ }
25563
26241
  function classifyFailedChecks(failing) {
25564
26242
  const infraFailures = [];
25565
26243
  const otherFailures = [];
25566
26244
  for (const check of failing) {
25567
26245
  const errors = check.annotations.filter(isErrorAnnotation);
25568
- const allInfra = errors.length > 0 && errors.every((a) => RUNNER_INFRA_ANNOTATION_TITLES.has(a.title?.trim() ?? ""));
25569
- (allInfra ? infraFailures : otherFailures).push(check.name);
26246
+ if (errors.length > 0) {
26247
+ const allInfra = errors.every((a) => isRunnerInfraAnnotation(a));
26248
+ (allInfra ? infraFailures : otherFailures).push(check.name);
26249
+ continue;
26250
+ }
26251
+ if (RUNNER_INFRA_PRESTART_CONCLUSIONS.has(check.conclusion?.trim() ?? "")) {
26252
+ infraFailures.push(check.name);
26253
+ continue;
26254
+ }
26255
+ otherFailures.push(check.name);
25570
26256
  }
25571
26257
  if (infraFailures.length && !otherFailures.length) {
25572
26258
  return {
25573
26259
  cause: "runner-infra",
25574
26260
  infraFailures,
25575
26261
  otherFailures,
25576
- reason: `${infraFailures.length} check(s) failed on RUNNER INFRASTRUCTURE, not your diff: ${infraFailures.join(", ")}. No test failed \u2014 the shared runner hit a wall-clock budget kill, ran out of disk, or was missing a toolchain (the check annotation names which). Re-run once the runner drains/heals (gh run rerun --failed); do not debug the diff.`
26262
+ reason: `${infraFailures.length} check(s) failed on RUNNER INFRASTRUCTURE, not your diff: ${infraFailures.join(", ")}. No test failed \u2014 the shared runner hit a wall-clock budget kill, ran out of disk, was missing a toolchain, could not acquire an mmi-live lane, or Actions returned Service Unavailable before checkout (the check annotation / conclusion names which). Re-run once the runner drains/heals (gh run rerun --failed); do not debug the diff.`
25577
26263
  };
25578
26264
  }
25579
26265
  return {
@@ -25601,6 +26287,7 @@ async function diagnoseFailedRestChecks(prNumber, repo, gh = defaultGhApi) {
25601
26287
  if (!failing.length) return null;
25602
26288
  const annotated = await Promise.all(failing.map(async (run) => ({
25603
26289
  name: run.name ?? `check-run ${run.id}`,
26290
+ conclusion: run.conclusion ?? null,
25604
26291
  annotations: JSON.parse(await gh([`repos/${repo}/check-runs/${run.id}/annotations`]))
25605
26292
  })));
25606
26293
  return classifyFailedChecks(annotated);
@@ -25702,9 +26389,9 @@ async function checkDocsIndexAtHead(opts, deps) {
25702
26389
  }
25703
26390
 
25704
26391
  // src/worktree-lifecycle-commands.ts
25705
- var import_node_fs31 = require("node:fs");
26392
+ var import_node_fs34 = require("node:fs");
25706
26393
  var import_promises9 = require("node:fs/promises");
25707
- var import_node_path31 = require("node:path");
26394
+ var import_node_path34 = require("node:path");
25708
26395
  var GH_TIMEOUT_MS = 2e4;
25709
26396
  var DEFAULT_BASE = "origin/development";
25710
26397
  var DEFAULT_REMOTE = "origin";
@@ -25850,7 +26537,7 @@ function classifyStaleLeaks(input) {
25850
26537
  var defaultOrphanDirScanDeps = {
25851
26538
  listDirs: (root) => {
25852
26539
  try {
25853
- return (0, import_node_fs31.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path31.join)(root, e.name));
26540
+ return (0, import_node_fs34.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => (0, import_node_path34.join)(root, e.name));
25854
26541
  } catch {
25855
26542
  return [];
25856
26543
  }
@@ -26003,13 +26690,13 @@ function registerWorktreeCommands(program3) {
26003
26690
  const detached = headBorn && !symbolicBranch;
26004
26691
  const branch = symbolicBranch || (detached ? "HEAD" : "");
26005
26692
  if (!wtPath || !branch) return fail("worktree land: not inside a git worktree");
26006
- const gitFile = (0, import_node_path31.join)(wtPath, ".git");
26007
- const isLinked = (0, import_node_fs31.existsSync)(gitFile) && (0, import_node_fs31.statSync)(gitFile).isFile();
26693
+ const gitFile = (0, import_node_path34.join)(wtPath, ".git");
26694
+ const isLinked = (0, import_node_fs34.existsSync)(gitFile) && (0, import_node_fs34.statSync)(gitFile).isFile();
26008
26695
  if (apply && !isLinked) {
26009
26696
  return fail("worktree land: run from inside the linked worktree you want to land (this is the primary checkout)");
26010
26697
  }
26011
26698
  const commonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
26012
- const primaryCheckout = commonDir ? (0, import_node_path31.dirname)(commonDir) : wtPath;
26699
+ const primaryCheckout = commonDir ? (0, import_node_path34.dirname)(commonDir) : wtPath;
26013
26700
  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);
26014
26701
  const orphan = classifyOrphanedWorktree({
26015
26702
  branch,
@@ -26220,10 +26907,10 @@ async function gatherWorktreeContext() {
26220
26907
  if (s) stages.push({ path: wt.path, port: s.port });
26221
26908
  }
26222
26909
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot2);
26223
- const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path31.dirname)((0, import_node_path31.dirname)(worktreeGitRoot)) : repoRoot2;
26910
+ const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path34.dirname)((0, import_node_path34.dirname)(worktreeGitRoot)) : repoRoot2;
26224
26911
  const wtRoot = siblingMmiWorktreesRoot(primaryRepoRoot);
26225
26912
  let orphanDirs = [];
26226
- if ((0, import_node_fs31.existsSync)(wtRoot)) {
26913
+ if ((0, import_node_fs34.existsSync)(wtRoot)) {
26227
26914
  orphanDirs = scanOrphanDirs(wtRoot, worktreeGitRoot, {
26228
26915
  ...defaultOrphanDirScanDeps,
26229
26916
  listDirs: (root) => worktreeScanDirs(root, primaryRepoRoot, defaultOrphanDirScanDeps.listDirs, isRepoCheckoutDir)
@@ -26249,8 +26936,8 @@ ${err.stderr ?? ""}`;
26249
26936
  }
26250
26937
 
26251
26938
  // src/issue-commands.ts
26252
- var import_node_fs32 = require("node:fs");
26253
- var import_node_crypto5 = require("node:crypto");
26939
+ var import_node_fs35 = require("node:fs");
26940
+ var import_node_crypto6 = require("node:crypto");
26254
26941
  var ghRunner = async (args, timeoutMs) => (await execFileP2("gh", args, { timeout: timeoutMs })).stdout;
26255
26942
  var ReparentConflictError = class extends Error {
26256
26943
  constructor(message, payload) {
@@ -26267,7 +26954,7 @@ async function editIssue(client, options, deps = {}) {
26267
26954
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
26268
26955
  const patch = {};
26269
26956
  let bodyChanged = false;
26270
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs32.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
26957
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs35.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
26271
26958
  if (options.titleFile !== void 0) {
26272
26959
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
26273
26960
  } else if (options.title !== void 0) {
@@ -26500,7 +27187,7 @@ function rowIdempotencyKey(batchKey, spec) {
26500
27187
  const identity = `${spec.type}
26501
27188
  ${spec.title.trim()}
26502
27189
  ${spec.body ?? ""}`;
26503
- const hash = (0, import_node_crypto5.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
27190
+ const hash = (0, import_node_crypto6.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
26504
27191
  return `${batchKey}:${hash}`;
26505
27192
  }
26506
27193
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -26830,7 +27517,7 @@ function extendCreateCommand(issue2, batchAttach) {
26830
27517
  if (opts.batch) {
26831
27518
  let specs;
26832
27519
  try {
26833
- const raw = (0, import_node_fs32.readFileSync)(opts.batch, "utf8");
27520
+ const raw = (0, import_node_fs35.readFileSync)(opts.batch, "utf8");
26834
27521
  specs = JSON.parse(raw);
26835
27522
  if (!Array.isArray(specs)) throw new Error("batch file must contain a JSON array");
26836
27523
  } catch (e) {
@@ -26904,8 +27591,8 @@ ${lines}`, {
26904
27591
  }
26905
27592
 
26906
27593
  // src/train-commands.ts
26907
- var import_node_fs33 = require("node:fs");
26908
- var import_node_path32 = require("node:path");
27594
+ var import_node_fs36 = require("node:fs");
27595
+ var import_node_path35 = require("node:path");
26909
27596
 
26910
27597
  // src/train-status.ts
26911
27598
  function buildTrainStatusReport(input) {
@@ -26945,7 +27632,7 @@ function formatTrainStatus(r) {
26945
27632
  // src/train-commands.ts
26946
27633
  function readRepoVersion() {
26947
27634
  try {
26948
- return JSON.parse((0, import_node_fs33.readFileSync)((0, import_node_path32.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
27635
+ return JSON.parse((0, import_node_fs36.readFileSync)((0, import_node_path35.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
26949
27636
  } catch {
26950
27637
  return void 0;
26951
27638
  }
@@ -27091,9 +27778,9 @@ function registerDeployCommands(program3) {
27091
27778
  }
27092
27779
 
27093
27780
  // src/discovery-commands.ts
27094
- var import_node_fs34 = require("node:fs");
27095
- var import_node_os12 = require("node:os");
27096
- var import_node_path33 = require("node:path");
27781
+ var import_node_fs37 = require("node:fs");
27782
+ var import_node_os13 = require("node:os");
27783
+ var import_node_path36 = require("node:path");
27097
27784
  var GC_GH_TIMEOUT_MS3 = 2e4;
27098
27785
  async function collectStatus() {
27099
27786
  const repo = await resolveRepo();
@@ -27269,10 +27956,10 @@ async function collectOnboardStatus() {
27269
27956
  else if (top) nextCommand = `mmi-cli board claim ${top.number} # ${top.title}`;
27270
27957
  else nextCommand = "mmi-cli board read \u2014 no claimable items found";
27271
27958
  }
27272
- const home = (0, import_node_os12.homedir)();
27959
+ const home = (0, import_node_os13.homedir)();
27273
27960
  const plugin = onboardPluginGate({
27274
- readKnown: () => readFileSyncSafe((0, import_node_path33.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs34.readFileSync),
27275
- readSettings: () => readFileSyncSafe((0, import_node_path33.join)(home, ".claude", "settings.json"), import_node_fs34.readFileSync)
27961
+ readKnown: () => readFileSyncSafe((0, import_node_path36.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs37.readFileSync),
27962
+ readSettings: () => readFileSyncSafe((0, import_node_path36.join)(home, ".claude", "settings.json"), import_node_fs37.readFileSync)
27276
27963
  });
27277
27964
  return { track, board, registry: registry2, secrets, plugin, nextCommand };
27278
27965
  }
@@ -27335,6 +28022,7 @@ var LOOP_PLAYBOOKS = {
27335
28022
  title: "Agent",
27336
28023
  steps: [
27337
28024
  { label: "Orient in the current repository", command: "mmi-cli onboard" },
28025
+ { label: "Structure search via Hub cloud (paths/symbols/meaning) before grepping docs/", command: "mmi-cli repo-index search <q>" },
27338
28026
  { label: "Read the next board item", command: "mmi-cli board read" },
27339
28027
  { label: "Claim it and create an isolated worktree", command: "mmi-cli worktree create <issue-number> --claim --from origin/development" },
27340
28028
  { label: "Build and test in the touched package", command: "npm test && npm run build" },
@@ -29224,6 +29912,102 @@ function checkDocsIndex(probe) {
29224
29912
  verbose: evidence
29225
29913
  };
29226
29914
  }
29915
+ function checkRepoIndexCloud(probe) {
29916
+ if (!probe) return null;
29917
+ const evidence = [
29918
+ probe.repo ? `repo: ${probe.repo}` : "repo: (estate)",
29919
+ probe.builtAt ? `builtAt: ${probe.builtAt}` : "builtAt: n/a",
29920
+ typeof probe.fileCount === "number" ? `fileCount: ${probe.fileCount}` : "fileCount: n/a",
29921
+ typeof probe.embCount === "number" ? `embCount: ${probe.embCount}` : "embCount: n/a",
29922
+ typeof probe.localPresent === "boolean" ? `local cache: ${probe.localPresent ? "present" : "absent"}` : "local cache: n/a"
29923
+ ];
29924
+ switch (probe.kind) {
29925
+ case "healthy":
29926
+ return {
29927
+ ok: true,
29928
+ id: "repo-index",
29929
+ label: "repo-index",
29930
+ detail: probe.detail ?? `cloud ok${typeof probe.fileCount === "number" ? ` \u2014 ${probe.fileCount} files` : ""}`,
29931
+ verbose: evidence
29932
+ };
29933
+ case "command-absent":
29934
+ return {
29935
+ ok: false,
29936
+ id: "repo-index",
29937
+ label: "repo-index",
29938
+ detail: "this mmi-cli build has no repo-index command",
29939
+ fix: "run `mmi-cli doctor` to heal the global CLI, or `npm install -g @mutmutco/cli`",
29940
+ verbose: evidence
29941
+ };
29942
+ case "deploy-lag":
29943
+ return {
29944
+ ok: false,
29945
+ id: "repo-index",
29946
+ label: "repo-index",
29947
+ detail: "Hub /repo-index/* not deployed yet (HTTP 404)",
29948
+ fix: "ship a Hub release carrying estate index routes, then run repo-index-reconcile",
29949
+ reportOnly: true,
29950
+ verbose: evidence
29951
+ };
29952
+ case "missing-projection":
29953
+ return {
29954
+ ok: false,
29955
+ id: "repo-index",
29956
+ label: "repo-index",
29957
+ detail: probe.detail ?? "no cloud projection for this repo",
29958
+ fix: "run harbour repo-index-reconcile or `mmi-cli repo-index sync-estate --repo <owner/name>`",
29959
+ verbose: evidence
29960
+ };
29961
+ case "stale":
29962
+ return {
29963
+ ok: false,
29964
+ id: "repo-index",
29965
+ label: "repo-index",
29966
+ detail: probe.detail ?? "cloud projection looks stale vs recent pushes",
29967
+ fix: "trigger `repo-index-reconcile` (workflow_dispatch) or wait for the 6h harbour tick",
29968
+ verbose: evidence
29969
+ };
29970
+ case "auth":
29971
+ return {
29972
+ ok: false,
29973
+ id: "repo-index",
29974
+ label: "repo-index",
29975
+ detail: "Hub session missing or unauthorized for repo-index",
29976
+ fix: "run `gh auth login` and ensure a Hub session exists (`mmi-cli whoami`)",
29977
+ verbose: evidence
29978
+ };
29979
+ case "config":
29980
+ return {
29981
+ ok: false,
29982
+ id: "repo-index",
29983
+ label: "repo-index",
29984
+ detail: probe.detail ?? "Hub API URL not configured",
29985
+ fix: "run `mmi-cli doctor` to repair Hub wiring",
29986
+ verbose: evidence
29987
+ };
29988
+ case "network":
29989
+ return {
29990
+ ok: false,
29991
+ id: "repo-index",
29992
+ label: "repo-index",
29993
+ detail: probe.detail ?? "could not reach Hub repo-index",
29994
+ fix: "retry when the network is up; structure search stays fail-soft",
29995
+ reportOnly: true,
29996
+ verbose: evidence
29997
+ };
29998
+ case "local-advisory":
29999
+ return {
30000
+ ok: true,
30001
+ id: "repo-index",
30002
+ label: "repo-index",
30003
+ detail: probe.detail ?? "local cache absent (cloud search does not need it)",
30004
+ warn: true,
30005
+ verbose: evidence
30006
+ };
30007
+ default:
30008
+ return null;
30009
+ }
30010
+ }
29227
30011
  function planGitignore(current) {
29228
30012
  const { content, changed } = upsertManagedGitignoreBlock(current);
29229
30013
  return changed ? { ok: false, content } : { ok: true };
@@ -29413,6 +30197,36 @@ async function runDoctorClean(opts, io, deps) {
29413
30197
  const docs2 = checkDocsIndex(probe);
29414
30198
  if (docs2) emitNow(docs2);
29415
30199
  }
30200
+ async function runRepoIndexRow() {
30201
+ if (lane.preflight && !opts.self && !lane.full) {
30202
+ emitNow({
30203
+ ok: true,
30204
+ id: "repo-index",
30205
+ label: "repo-index",
30206
+ detail: "command present on this CLI (cloud probe skipped on --preflight)",
30207
+ verbose: ["preflight lane: no Hub network"]
30208
+ });
30209
+ return;
30210
+ }
30211
+ let probe;
30212
+ try {
30213
+ probe = await deps.repoIndexCloudState(await deps.repoRoot());
30214
+ } catch (e) {
30215
+ const message = e instanceof Error ? e.message : String(e);
30216
+ emitNow({
30217
+ ok: false,
30218
+ id: "repo-index",
30219
+ label: "repo-index",
30220
+ detail: `could not be read \u2014 ${message}`,
30221
+ fix: "repair Hub session / network, then re-run doctor",
30222
+ reportOnly: true,
30223
+ verbose: [`probe threw: ${message}`]
30224
+ });
30225
+ return;
30226
+ }
30227
+ const row = checkRepoIndexCloud(probe);
30228
+ if (row) emitNow(row);
30229
+ }
29416
30230
  async function runHousekeeperRows() {
29417
30231
  const repoRoot2 = await deps.repoRoot();
29418
30232
  try {
@@ -29486,6 +30300,8 @@ async function runDoctorClean(opts, io, deps) {
29486
30300
  // A `docs/` tree walk plus one batched `git check-ignore` — cheap next to the two rows below it, but a
29487
30301
  // disk walk and a subprocess all the same, so full lane only (#4091).
29488
30302
  { id: "docs-index", when: isOrgRepo && lane.full, run: runDocsIndexRow },
30303
+ // Hub cloud probe (short timeout). Full lane + `--self`; `--preflight` may emit command-absent only (#4156).
30304
+ { id: "repo-index", when: isOrgRepo && (lane.full || Boolean(opts.self) || lane.preflight), run: runRepoIndexRow },
29489
30305
  // The two most expensive things in this file — a real `git fetch` plus train-branch fast-forward,
29490
30306
  // and a `gh`-backed gc sweep with a 20s timeout — so org repos on the full lane only (#3485).
29491
30307
  { id: "train-branches", when: isOrgRepo && lane.full, run: runTrainSyncRow },
@@ -29543,18 +30359,18 @@ function parseOriginRepo(remoteUrl) {
29543
30359
  return `${match[1]}/${match[2]}`;
29544
30360
  }
29545
30361
  function ghHostsConfigPath(env, platform2) {
29546
- const sep2 = platform2 === "win32" ? "\\" : "/";
29547
- const join31 = (...parts) => parts.join(sep2);
30362
+ const sep3 = platform2 === "win32" ? "\\" : "/";
30363
+ const join34 = (...parts) => parts.join(sep3);
29548
30364
  const explicit = env.GH_CONFIG_DIR?.trim();
29549
- if (explicit) return join31(explicit, "hosts.yml");
30365
+ if (explicit) return join34(explicit, "hosts.yml");
29550
30366
  if (platform2 === "win32") {
29551
30367
  const appData = (env.AppData ?? env.APPDATA)?.trim();
29552
- return appData ? join31(appData, "GitHub CLI", "hosts.yml") : void 0;
30368
+ return appData ? join34(appData, "GitHub CLI", "hosts.yml") : void 0;
29553
30369
  }
29554
30370
  const xdg = env.XDG_CONFIG_HOME?.trim();
29555
- if (xdg) return join31(xdg, "gh", "hosts.yml");
30371
+ if (xdg) return join34(xdg, "gh", "hosts.yml");
29556
30372
  const home = env.HOME?.trim();
29557
- return home ? join31(home, ".config", "gh", "hosts.yml") : void 0;
30373
+ return home ? join34(home, ".config", "gh", "hosts.yml") : void 0;
29558
30374
  }
29559
30375
  function parseGhHostsAccounts(yaml, host = "github.com") {
29560
30376
  let hostIndent = null;
@@ -29604,17 +30420,17 @@ function ghAccountCaveat(announcedLogin, accounts) {
29604
30420
  }
29605
30421
 
29606
30422
  // src/doctor-io.ts
29607
- var import_node_fs35 = require("node:fs");
29608
- var import_node_os13 = require("node:os");
29609
- var import_node_path34 = require("node:path");
29610
- var import_node_child_process15 = require("node:child_process");
30423
+ var import_node_fs38 = require("node:fs");
30424
+ var import_node_os14 = require("node:os");
30425
+ var import_node_path37 = require("node:path");
30426
+ var import_node_child_process17 = require("node:child_process");
29611
30427
  var import_node_util8 = require("node:util");
29612
- var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process15.execFile);
30428
+ var execFileP6 = (0, import_node_util8.promisify)(import_node_child_process17.execFile);
29613
30429
  var MMI_PLUGIN_ID2 = "mmi@mutmutco";
29614
30430
  function installedClaudePluginVersion() {
29615
30431
  try {
29616
30432
  const file = JSON.parse(
29617
- (0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
30433
+ (0, import_node_fs38.readFileSync)((0, import_node_path37.join)((0, import_node_os14.homedir)(), ".claude", "plugins", "installed_plugins.json"), "utf8")
29618
30434
  );
29619
30435
  const versions = (file.plugins?.[MMI_PLUGIN_ID2] ?? []).map((r) => r.version).filter((v) => Boolean(v));
29620
30436
  if (versions.length === 0) return void 0;
@@ -29625,7 +30441,7 @@ function installedClaudePluginVersion() {
29625
30441
  }
29626
30442
  function manifestVersion(path2) {
29627
30443
  try {
29628
- const manifest = JSON.parse((0, import_node_fs35.readFileSync)(path2, "utf8"));
30444
+ const manifest = JSON.parse((0, import_node_fs38.readFileSync)(path2, "utf8"));
29629
30445
  return typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : void 0;
29630
30446
  } catch {
29631
30447
  return void 0;
@@ -29635,27 +30451,27 @@ function installedSurfacePluginVersion(surface) {
29635
30451
  const token = surfaceToken(surface);
29636
30452
  if (token === "kilo") {
29637
30453
  try {
29638
- const stamp = (0, import_node_fs35.readFileSync)((0, import_node_path34.join)((0, import_node_os13.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
30454
+ const stamp = (0, import_node_fs38.readFileSync)((0, import_node_path37.join)((0, import_node_os14.homedir)(), ".kilo", ".mmi-kilo-version"), "utf8").trim();
29639
30455
  return stamp || void 0;
29640
30456
  } catch {
29641
30457
  return void 0;
29642
30458
  }
29643
30459
  }
29644
30460
  if (token === "cursor") {
29645
- return manifestVersion((0, import_node_path34.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
30461
+ return manifestVersion((0, import_node_path37.join)(cursorLocalPluginRoot(), ".cursor-plugin", "plugin.json"));
29646
30462
  }
29647
30463
  if (token === "kimi") {
29648
- return manifestVersion((0, import_node_path34.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
30464
+ return manifestVersion((0, import_node_path37.join)(surfaceConfigRoot(surface), "plugins", "managed", "mmi", ".kimi-plugin", "plugin.json"));
29649
30465
  }
29650
30466
  if (token === "claude") return installedClaudePluginVersion();
29651
30467
  if (token !== "codex") return void 0;
29652
30468
  try {
29653
- const raw = process.platform === "win32" ? (0, import_node_child_process15.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
30469
+ const raw = process.platform === "win32" ? (0, import_node_child_process17.execFileSync)("cmd.exe", ["/c", "codex", "plugin", "list", "--json"], {
29654
30470
  encoding: "utf8",
29655
30471
  stdio: ["ignore", "pipe", "ignore"],
29656
30472
  timeout: 15e3,
29657
30473
  windowsHide: true
29658
- }) : (0, import_node_child_process15.execFileSync)("codex", ["plugin", "list", "--json"], {
30474
+ }) : (0, import_node_child_process17.execFileSync)("codex", ["plugin", "list", "--json"], {
29659
30475
  encoding: "utf8",
29660
30476
  stdio: ["ignore", "pipe", "ignore"],
29661
30477
  timeout: 15e3,
@@ -29673,7 +30489,7 @@ function installedActivePluginVersion(surface = detectSurface(process.env)) {
29673
30489
  }
29674
30490
  function worktreeRootSync() {
29675
30491
  try {
29676
- const out = (0, import_node_child_process15.execFileSync)("git", ["rev-parse", "--show-toplevel"], { windowsHide: true, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
30492
+ const out = (0, import_node_child_process17.execFileSync)("git", ["rev-parse", "--show-toplevel"], { windowsHide: true, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
29677
30493
  let root = out.endsWith("\n") ? out.slice(0, -1) : out;
29678
30494
  if (process.platform === "win32" && root.endsWith("\r")) root = root.slice(0, -1);
29679
30495
  return root || null;
@@ -29683,13 +30499,13 @@ function worktreeRootSync() {
29683
30499
  }
29684
30500
  var gitignorePath = () => {
29685
30501
  const root = worktreeRootSync();
29686
- return root === null ? null : (0, import_node_path34.join)(root, ".gitignore");
30502
+ return root === null ? null : (0, import_node_path37.join)(root, ".gitignore");
29687
30503
  };
29688
30504
  function readGitignore() {
29689
30505
  const path2 = gitignorePath();
29690
30506
  if (path2 === null) return null;
29691
30507
  try {
29692
- return (0, import_node_fs35.readFileSync)(path2, "utf8");
30508
+ return (0, import_node_fs38.readFileSync)(path2, "utf8");
29693
30509
  } catch {
29694
30510
  return null;
29695
30511
  }
@@ -29698,7 +30514,7 @@ function writeGitignore(content) {
29698
30514
  const path2 = gitignorePath();
29699
30515
  if (path2 === null) return false;
29700
30516
  try {
29701
- (0, import_node_fs35.writeFileSync)(path2, content, "utf8");
30517
+ (0, import_node_fs38.writeFileSync)(path2, content, "utf8");
29702
30518
  return true;
29703
30519
  } catch {
29704
30520
  return false;
@@ -29722,7 +30538,7 @@ async function repoRoot() {
29722
30538
  }
29723
30539
  function hasRepoLocalWorktrees() {
29724
30540
  const root = worktreeRootSync();
29725
- return root !== null && (0, import_node_fs35.existsSync)((0, import_node_path34.join)(root, ".worktrees"));
30541
+ return root !== null && (0, import_node_fs38.existsSync)((0, import_node_path37.join)(root, ".worktrees"));
29726
30542
  }
29727
30543
 
29728
30544
  // src/index.ts
@@ -29758,8 +30574,8 @@ ${r.stderr ?? ""}`).catch(() => "");
29758
30574
  function ghMultiAccountCaveat(announcedLogin) {
29759
30575
  try {
29760
30576
  const hostsPath = ghHostsConfigPath(process.env, process.platform);
29761
- if (!hostsPath || !(0, import_node_fs36.existsSync)(hostsPath)) return void 0;
29762
- return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs36.readFileSync)(hostsPath, "utf8")));
30577
+ if (!hostsPath || !(0, import_node_fs39.existsSync)(hostsPath)) return void 0;
30578
+ return ghAccountCaveat(announcedLogin, parseGhHostsAccounts((0, import_node_fs39.readFileSync)(hostsPath, "utf8")));
29763
30579
  } catch {
29764
30580
  return void 0;
29765
30581
  }
@@ -29767,20 +30583,20 @@ function ghMultiAccountCaveat(announcedLogin) {
29767
30583
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
29768
30584
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
29769
30585
  function envHealLockPath(home) {
29770
- return (0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
30586
+ return (0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
29771
30587
  }
29772
30588
  async function withEnvHealLock(what, run) {
29773
30589
  try {
29774
30590
  return await withFileLock(
29775
- envHealLockPath((0, import_node_os14.homedir)()),
30591
+ envHealLockPath((0, import_node_os15.homedir)()),
29776
30592
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
29777
30593
  run
29778
30594
  );
29779
30595
  } catch (e) {
29780
30596
  if (e instanceof FileLockBusyError) {
29781
- return { ok: false, skipped: true, detail: `${what} skipped \u2014 ${e.message}` };
30597
+ return { ok: false, skipped: true, detail: `${what} skipped ? ${e.message}` };
29782
30598
  }
29783
- return { ok: false, detail: `${what} could not take the env-heal lock \u2014 ${e.message}` };
30599
+ return { ok: false, detail: `${what} could not take the env-heal lock ? ${e.message}` };
29784
30600
  }
29785
30601
  }
29786
30602
  function throttledReleasedVersion() {
@@ -29836,13 +30652,13 @@ function mmiDoctorDeps(opts = {}) {
29836
30652
  pluginTrustState: () => codexHookTrustState(),
29837
30653
  releasedVersion: throttled ? throttled.read : fetchNpmReleasedVersion,
29838
30654
  releasedVersionNote: throttled ? throttled.note : void 0,
29839
- // #3272: the --apply self-heal for a stale running CLI — npm global, shadows any plugin shim (#2879).
30655
+ // #3272: the --apply self-heal for a stale running CLI ? npm global, shadows any plugin shim (#2879).
29840
30656
  // #3489: serialised. Both heals mutate machine-global state (the npm global prefix; the Claude
29841
30657
  // marketplace clone + plugin cache) and neither is idempotent under concurrency. They share ONE lock
29842
30658
  // rather than one each, because they are not independent: the plugin heal reinstalls a bundle that
29843
30659
  // carries a CLI shim, so a concurrent npm global install races the same PATH surface.
29844
30660
  updateCli: (target, onStep) => withEnvHealLock("npm global CLI self-update", () => npmSelfUpdateCli(target, onStep)),
29845
- // #3282: the --apply self-heal for a stale/unresolved Claude plugin — the same marketplace reinstall
30661
+ // #3282: the --apply self-heal for a stale/unresolved Claude plugin ? the same marketplace reinstall
29846
30662
  // `mmi-cli plugin heal` drives. Takes effect on the next Claude reload, so the row asks for a restart.
29847
30663
  healPlugin: (onStep) => {
29848
30664
  const surface = detectSurface(process.env);
@@ -29860,7 +30676,7 @@ function mmiDoctorDeps(opts = {}) {
29860
30676
  // #2759: same fetch+ff-only sync SessionStart runs, wired here too so an on-demand `mmi-cli doctor`
29861
30677
  // self-heals a checkout a long-running session left stale.
29862
30678
  syncTrain: () => syncLocalTrainBranches(execFileGitRun),
29863
- // #2903: doctor DETECTS stale cached plugin versions and defers the delete to `plugin-prune` — it never
30679
+ // #2903: doctor DETECTS stale cached plugin versions and defers the delete to `plugin-prune` ? it never
29864
30680
  // writes to the harness-owned cache itself. Same plan builder the verb uses, so the two never disagree.
29865
30681
  // No `withBytes` here: doctor runs on EVERY SessionStart, and sizing means recursively stat'ing every
29866
30682
  // stale version tree. The count comes from one cheap readdir; `plugin-prune` reports the MB.
@@ -29869,7 +30685,7 @@ function mmiDoctorDeps(opts = {}) {
29869
30685
  const configRoot = surfaceConfigRoot(surface);
29870
30686
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
29871
30687
  const plan = buildPluginCachePlan(
29872
- (0, import_node_os14.homedir)(),
30688
+ (0, import_node_os15.homedir)(),
29873
30689
  running,
29874
30690
  pluginCacheFsDeps(configRoot, () => 0),
29875
30691
  { configRoot, includeStaging: surface !== "codex" }
@@ -29883,24 +30699,24 @@ function mmiDoctorDeps(opts = {}) {
29883
30699
  };
29884
30700
  },
29885
30701
  // #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
29886
- // A local record read — cheap enough for every lane, including the banner.
30702
+ // A local record read ? cheap enough for every lane, including the banner.
29887
30703
  sessionPayload: () => readSessionPayload(process.cwd()),
29888
- // #3485 items 9 and 8: why the MMI plugin has never printed an "updated — please restart" notice, and
30704
+ // #3485 items 9 and 8: why the MMI plugin has never printed an "updated ? please restart" notice, and
29889
30705
  // which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
29890
30706
  marketplaceRows: () => {
29891
30707
  try {
29892
30708
  if (detectSurface(process.env) === "codex") return [];
29893
- const home = (0, import_node_os14.homedir)();
30709
+ const home = (0, import_node_os15.homedir)();
29894
30710
  const rows = marketplaceRows(
29895
30711
  MMI_MARKETPLACE_NAME,
29896
- readFileSyncSafe((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs36.readFileSync),
29897
- readFileSyncSafe((0, import_node_path35.join)(home, ".claude", "settings.json"), import_node_fs36.readFileSync),
30712
+ readFileSyncSafe((0, import_node_path38.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs39.readFileSync),
30713
+ readFileSyncSafe((0, import_node_path38.join)(home, ".claude", "settings.json"), import_node_fs39.readFileSync),
29898
30714
  // #3974: this CLI heals these rows, so report mode names the doctor run rather than the hand
29899
- // edit. Unconditional — it is a fact about mmi-cli, not about the lane this run is on.
30715
+ // edit. Unconditional ? it is a fact about mmi-cli, not about the lane this run is on.
29900
30716
  true
29901
30717
  );
29902
30718
  const pending = readMarketplacePinPending(
29903
- (0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
30719
+ (0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"),
29904
30720
  MMI_MARKETPLACE_NAME
29905
30721
  );
29906
30722
  if (!pending) return rows;
@@ -29911,7 +30727,7 @@ function mmiDoctorDeps(opts = {}) {
29911
30727
  ...rest,
29912
30728
  ok: true,
29913
30729
  warn: true,
29914
- detail: `pending restart \u2014 doctor pinned main at ${pending.at}; restart Claude Code to load it`,
30730
+ detail: `pending restart ? doctor pinned main at ${pending.at}; restart Claude Code to load it`,
29915
30731
  verbose: [...row.verbose ?? [], "known_marketplaces.json was rewritten by the running Claude host after the verified doctor write"]
29916
30732
  };
29917
30733
  });
@@ -29919,16 +30735,16 @@ function mmiDoctorDeps(opts = {}) {
29919
30735
  return [];
29920
30736
  }
29921
30737
  },
29922
- // #3974: the heal behind those rows, under --apply only. Same Codex carve-out as the rows above —
30738
+ // #3974: the heal behind those rows, under --apply only. Same Codex carve-out as the rows above ?
29923
30739
  // Codex keeps its registration in config.toml, so there is no ~/.claude registration to pin.
29924
30740
  healMarketplacePins: () => {
29925
30741
  try {
29926
30742
  if (detectSurface(process.env) === "codex") return void 0;
29927
- const home = (0, import_node_os14.homedir)();
30743
+ const home = (0, import_node_os15.homedir)();
29928
30744
  const names = [MMI_MARKETPLACE_NAME];
29929
- const result = applyOrgMarketplacePins((0, import_node_path35.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
30745
+ const result = applyOrgMarketplacePins((0, import_node_path38.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), names);
29930
30746
  if (result?.wrote) {
29931
- writeMarketplacePinPending((0, import_node_path35.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
30747
+ writeMarketplacePinPending((0, import_node_path38.join)(home, ".claude", "plugins", ".mmi-marketplace-pin-pending.json"), names);
29932
30748
  }
29933
30749
  return result;
29934
30750
  } catch {
@@ -29936,20 +30752,73 @@ function mmiDoctorDeps(opts = {}) {
29936
30752
  }
29937
30753
  },
29938
30754
  // #4091: the same `docs index --check` comparison the required CI gate runs, in-process. Rooted at the
29939
- // repo root doctor already resolved — never `process.cwd()`, which would have `mmi-cli doctor` run from
30755
+ // repo root doctor already resolved ? never `process.cwd()`, which would have `mmi-cli doctor` run from
29940
30756
  // `cli/` measure a `cli/docs/` tree that does not exist.
29941
30757
  //
29942
30758
  // `existsSync` on `docs/index.md` is the adoption gate, and it sits UNDER the table's org-repo gate
29943
30759
  // rather than replacing it: a missing index is drift by construction, so without this a repo that never
29944
30760
  // adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
29945
- // get a permanent ✗ demanding an artifact it never asked for.
30761
+ // get a permanent ? demanding an artifact it never asked for.
29946
30762
  docsIndexState: (root) => {
29947
- if (!(0, import_node_fs36.existsSync)((0, import_node_path35.join)(root, DOCS_INDEX_PATH))) return void 0;
30763
+ if (!(0, import_node_fs39.existsSync)((0, import_node_path38.join)(root, DOCS_INDEX_PATH))) return void 0;
29948
30764
  const real = createDocsIndexDeps(root);
29949
30765
  let docs2;
29950
30766
  const listDocs = () => docs2 ??= real.listDocs();
29951
30767
  const drift = docsIndex({ ...real, listDocs }, { check: true }).drift;
29952
30768
  return { drift, docCount: listDocs().length };
30769
+ },
30770
+ // #4156: short-timeout Hub status for the current repo. Fail-soft ? never throws to doctor.
30771
+ repoIndexCloudState: async (root) => {
30772
+ const local = repoIndexStatus(root);
30773
+ try {
30774
+ const cfg = await loadConfig();
30775
+ const repo = inferRepoSlug(root);
30776
+ const st = await statusRepoIndexCloud(repo, {
30777
+ ...registryClientDeps(cfg),
30778
+ timeoutMs: 4e3
30779
+ });
30780
+ if (isRepoIndexStatusError(st)) {
30781
+ if (st.code === "deploy-lag") return { kind: "deploy-lag", repo, localPresent: local.present };
30782
+ if (st.code === "auth") return { kind: "auth", repo, localPresent: local.present };
30783
+ if (st.code === "config") return { kind: "config", repo, detail: st.error, localPresent: local.present };
30784
+ return { kind: "network", repo, detail: st.error, localPresent: local.present };
30785
+ }
30786
+ const present = st.present;
30787
+ const builtAt = typeof st.builtAt === "string" ? st.builtAt : void 0;
30788
+ const fileCount = typeof st.fileCount === "number" ? st.fileCount : void 0;
30789
+ const embCount = typeof st.embCount === "number" ? st.embCount : void 0;
30790
+ if (present === false) {
30791
+ return { kind: "missing-projection", repo, localPresent: local.present, fileCount: 0, embCount: 0 };
30792
+ }
30793
+ if (builtAt) {
30794
+ const ageMs = Date.now() - Date.parse(builtAt);
30795
+ if (Number.isFinite(ageMs) && ageMs > 48 * 60 * 60 * 1e3) {
30796
+ return {
30797
+ kind: "stale",
30798
+ repo,
30799
+ builtAt,
30800
+ fileCount,
30801
+ embCount,
30802
+ localPresent: local.present,
30803
+ detail: `projection builtAt ${builtAt} (>48h old)`
30804
+ };
30805
+ }
30806
+ }
30807
+ return {
30808
+ kind: "healthy",
30809
+ repo: typeof st.repo === "string" ? st.repo : repo,
30810
+ builtAt,
30811
+ fileCount,
30812
+ embCount,
30813
+ localPresent: local.present
30814
+ };
30815
+ } catch (e) {
30816
+ return {
30817
+ kind: "network",
30818
+ detail: e instanceof Error ? e.message : String(e),
30819
+ localPresent: local.present
30820
+ };
30821
+ }
29953
30822
  }
29954
30823
  };
29955
30824
  }
@@ -29997,7 +30866,7 @@ function argvWantsJson2() {
29997
30866
  var unknownFlagJsonHandled = false;
29998
30867
  var PARSE_HINT_SENTINEL = "@@mmi-parse-hint@@";
29999
30868
  var DISCOVERY_HINT = "run `mmi-cli commands` for the agentic-coding map, `mmi-cli explain <command>` for detail, or `mmi-cli commands --json --primary` for machine discovery";
30000
- var STALE_HINT = `(${DISCOVERY_HINT}). If you expected this command to exist, your installed mmi-cli may be stale \u2014 update it (see: mmi-cli doctor)`;
30869
+ var STALE_HINT = `(${DISCOVERY_HINT}). If you expected this command to exist, your installed mmi-cli may be stale ? update it (see: mmi-cli doctor)`;
30001
30870
  var lastParseErrorKind = "other";
30002
30871
  var lastUnknownCommand;
30003
30872
  function classifyParseError(plain) {
@@ -30063,7 +30932,7 @@ function resolveParseHint() {
30063
30932
  const path2 = commandPath2(cmd);
30064
30933
  return [
30065
30934
  `Usage: mmi-cli ${path2} ${cmd.usage()}`,
30066
- "This command exists and your mmi-cli is not the problem \u2014 the ARGUMENTS did not parse.",
30935
+ "This command exists and your mmi-cli is not the problem ? the ARGUMENTS did not parse.",
30067
30936
  `Run \`mmi-cli ${path2} --help\` for its signature, \`mmi-cli explain ${path2}\` for detail, or \`mmi-cli commands\` for the full map.`
30068
30937
  ].join("\n");
30069
30938
  }
@@ -30124,7 +30993,7 @@ function envelopeAwareWriteErr(str) {
30124
30993
  process.stderr.write(str);
30125
30994
  }
30126
30995
  var program2 = new Command();
30127
- program2.name("mmi-cli").description("MMI Future Hub CLI \u2014 the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
30996
+ program2.name("mmi-cli").description("MMI Future Hub CLI ? the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
30128
30997
  function appActorDeps() {
30129
30998
  return {
30130
30999
  fetchSecret: async (key) => fetchSecretValue(makeSecretsDeps(await loadConfig()), key, { repo: APP_VAULT_REPO }),
@@ -30156,19 +31025,19 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
30156
31025
  });
30157
31026
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
30158
31027
  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) => {
30159
- const path2 = (0, import_node_path35.join)(process.cwd(), ".gitignore");
30160
- const current = (0, import_node_fs36.existsSync)(path2) ? (0, import_node_fs36.readFileSync)(path2, "utf8") : null;
31028
+ const path2 = (0, import_node_path38.join)(process.cwd(), ".gitignore");
31029
+ const current = (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null;
30161
31030
  const plan = planManagedGitignore(current);
30162
31031
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
30163
31032
  if (opts.json) {
30164
- if (opts.write && plan.changed) (0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
31033
+ if (opts.write && plan.changed) (0, import_node_fs39.writeFileSync)(path2, plan.content, "utf8");
30165
31034
  console.log(JSON.stringify(plan, null, 2));
30166
31035
  if (!opts.write && plan.changed) process.exitCode = 1;
30167
31036
  return;
30168
31037
  }
30169
31038
  if (opts.write) {
30170
31039
  if (plan.changed) {
30171
- (0, import_node_fs36.writeFileSync)(path2, plan.content, "utf8");
31040
+ (0, import_node_fs39.writeFileSync)(path2, plan.content, "utf8");
30172
31041
  console.log(`mmi-cli org rules gitignore: updated .gitignore (${drift})`);
30173
31042
  } else {
30174
31043
  console.log("mmi-cli org rules gitignore: up to date");
@@ -30176,7 +31045,7 @@ rules.command("gitignore").option("--write", "upsert the managed block into .git
30176
31045
  return;
30177
31046
  }
30178
31047
  if (plan.changed) {
30179
- console.error(`mmi-cli org rules gitignore: managed block drift (${drift}) \u2014 run \`mmi-cli org rules gitignore --write\` and commit`);
31048
+ console.error(`mmi-cli org rules gitignore: managed block drift (${drift}) ? run \`mmi-cli org rules gitignore --write\` and commit`);
30180
31049
  process.exitCode = 1;
30181
31050
  } else {
30182
31051
  console.log("mmi-cli org rules gitignore: up to date");
@@ -30282,7 +31151,7 @@ wave.command("land").description("serial merge open PRs to development with reba
30282
31151
  });
30283
31152
  var gcCmd = program2.command("gc").description("dry-run cleanup for merged/closed PR local+remote branches, linked worktrees, stale tracking refs, and worktree metadata (--scratch: safe local scratch + confirmed-synced plans)");
30284
31153
  var DEFERRED_SWEEP_HARD_TIMEOUT_MS = 12e4;
30285
- gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree removals with backoff (#1932) \u2014 SessionStart + pr land spawn this detached").option("--quiet", "silent when registry empty or all cleared").option("--json", "machine-readable output").action(async (o) => {
31154
+ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree removals with backoff (#1932) ? SessionStart + pr land spawn this detached").option("--quiet", "silent when registry empty or all cleared").option("--json", "machine-readable output").action(async (o) => {
30286
31155
  await runWithSweepWatchdog(async () => {
30287
31156
  try {
30288
31157
  const deferredStore = await createDeferredWorktreeStore();
@@ -30290,7 +31159,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
30290
31159
  const result = await sweepDeferredWorktreesWithRetry(
30291
31160
  deferredStore,
30292
31161
  // #2841: `-c core.fsmonitor=false` stops a git fsmonitor daemon from inheriting this detached
30293
- // worker's stdio pipe (the likely Windows hang — execFile then never sees EOF and never resolves).
31162
+ // worker's stdio pipe (the likely Windows hang ? execFile then never sees EOF and never resolves).
30294
31163
  worktreeRemoveDeps(async (args) => (await execFileP2("git", ["-c", "core.fsmonitor=false", ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
30295
31164
  { removalContext }
30296
31165
  );
@@ -30305,11 +31174,11 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
30305
31174
  fail(`worktree gc sweep-deferred: ${e.message}`);
30306
31175
  }
30307
31176
  }, DEFERRED_SWEEP_HARD_TIMEOUT_MS, () => {
30308
- console.error("worktree gc sweep-deferred: exceeded hard timeout \u2014 exiting so the detached worker cannot leak (#2841)");
31177
+ console.error("worktree gc sweep-deferred: exceeded hard timeout ? exiting so the detached worker cannot leak (#2841)");
30309
31178
  process.exit(process.exitCode ?? 0);
30310
31179
  });
30311
31180
  });
30312
- gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to inspect per state", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
31181
+ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to inspect per state", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
30313
31182
  if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
30314
31183
  if (o.scratch) {
30315
31184
  try {
@@ -30325,11 +31194,11 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
30325
31194
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree gc: --limit must be a positive integer");
30326
31195
  let root;
30327
31196
  if (o.root !== void 0) {
30328
- root = (0, import_node_path35.resolve)(o.root);
30329
- if (!(0, import_node_fs36.existsSync)(root) || !(0, import_node_fs36.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
31197
+ root = (0, import_node_path38.resolve)(o.root);
31198
+ 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`);
30330
31199
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
30331
31200
  if (isPathUnderDirectory(gcRepoRoot, root)) {
30332
- return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
31201
+ return fail(`worktree gc: --root ${root} contains this checkout ? name a worktrees root, not the repo or an ancestor of it`);
30333
31202
  }
30334
31203
  }
30335
31204
  try {
@@ -30343,7 +31212,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
30343
31212
  const removalContext = await currentWorktreeRemovalContext("worktree gc", o.force);
30344
31213
  await sweepDeferredWorktrees(
30345
31214
  deferredStore,
30346
- // #2841: same `-c core.fsmonitor=false` guard as the detached `gc sweep-deferred` worker — keep a
31215
+ // #2841: same `-c core.fsmonitor=false` guard as the detached `gc sweep-deferred` worker ? keep a
30347
31216
  // git fsmonitor daemon from inheriting this sweep's stdio pipe and wedging the git call on Windows.
30348
31217
  worktreeRemoveDeps(async (args) => (await execFileP2("git", ["-c", "core.fsmonitor=false", ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
30349
31218
  removalContext
@@ -30368,11 +31237,11 @@ var WORKTREE_SETUP_LOCK_TTL_MS = 10 * 6e4;
30368
31237
  function runWorktreeInstall(command, cwd, quiet, opts) {
30369
31238
  const stdio = quiet ? "ignore" : "inherit";
30370
31239
  return new Promise((resolve5, reject) => {
30371
- const child2 = opts?.shell ? (0, import_node_child_process16.spawn)(command, { cwd, stdio, windowsHide: true, shell: true }) : (() => {
31240
+ const child2 = opts?.shell ? (0, import_node_child_process18.spawn)(command, { cwd, stdio, windowsHide: true, shell: true }) : (() => {
30372
31241
  const [bin, ...args] = command.split(" ");
30373
31242
  const file = isWin2 ? "cmd.exe" : bin;
30374
31243
  const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
30375
- return (0, import_node_child_process16.spawn)(file, spawnArgs, { cwd, stdio, windowsHide: true });
31244
+ return (0, import_node_child_process18.spawn)(file, spawnArgs, { cwd, stdio, windowsHide: true });
30376
31245
  })();
30377
31246
  const timer = setTimeout(() => {
30378
31247
  try {
@@ -30405,14 +31274,14 @@ async function currentWorktreeRemovalContext(command, force) {
30405
31274
  };
30406
31275
  }
30407
31276
  async function unprovenWorktreeReason(wtPath, repoRoot2) {
30408
- if (!(0, import_node_fs36.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
31277
+ if (!(0, import_node_fs39.existsSync)(wtPath)) return `${wtPath} does not exist on disk`;
30409
31278
  const porcelain = (await execFileP2("git", ["-C", repoRoot2, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout;
30410
31279
  const registered = parseWorktreePorcelainEntries(porcelain);
30411
31280
  if (!registered.length) {
30412
31281
  return `'git worktree list' could not be read from ${repoRoot2}, so ${wtPath} could not be proven registered`;
30413
31282
  }
30414
31283
  if (!registered.some((w) => samePath(w.path, wtPath))) {
30415
- return `${wtPath} exists on disk but 'git worktree list' does not name it \u2014 its git metadata is gone, so it is not a usable worktree`;
31284
+ return `${wtPath} exists on disk but 'git worktree list' does not name it ? its git metadata is gone, so it is not a usable worktree`;
30416
31285
  }
30417
31286
  return void 0;
30418
31287
  }
@@ -30427,26 +31296,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
30427
31296
  function acquireWorktreeSetupLock(worktreeRoot) {
30428
31297
  const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
30429
31298
  const take = () => {
30430
- const fd = (0, import_node_fs36.openSync)(lockPath, "wx");
31299
+ const fd = (0, import_node_fs39.openSync)(lockPath, "wx");
30431
31300
  try {
30432
- (0, import_node_fs36.writeSync)(fd, String(Date.now()));
31301
+ (0, import_node_fs39.writeSync)(fd, String(Date.now()));
30433
31302
  } finally {
30434
- (0, import_node_fs36.closeSync)(fd);
31303
+ (0, import_node_fs39.closeSync)(fd);
30435
31304
  }
30436
31305
  return () => {
30437
31306
  try {
30438
- (0, import_node_fs36.rmSync)(lockPath, { force: true });
31307
+ (0, import_node_fs39.rmSync)(lockPath, { force: true });
30439
31308
  } catch {
30440
31309
  }
30441
31310
  };
30442
31311
  };
30443
31312
  try {
30444
- (0, import_node_fs36.mkdirSync)((0, import_node_path35.dirname)(lockPath), { recursive: true });
31313
+ (0, import_node_fs39.mkdirSync)((0, import_node_path38.dirname)(lockPath), { recursive: true });
30445
31314
  return take();
30446
31315
  } catch {
30447
31316
  try {
30448
- if (Date.now() - (0, import_node_fs36.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
30449
- (0, import_node_fs36.rmSync)(lockPath, { force: true });
31317
+ if (Date.now() - (0, import_node_fs39.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
31318
+ (0, import_node_fs39.rmSync)(lockPath, { force: true });
30450
31319
  return take();
30451
31320
  }
30452
31321
  } catch {
@@ -30454,15 +31323,15 @@ function acquireWorktreeSetupLock(worktreeRoot) {
30454
31323
  return null;
30455
31324
  }
30456
31325
  }
30457
- var worktree = program2.command("worktree").description("self-provisioning worktrees \u2014 install deps + copy local-only config");
31326
+ var worktree = program2.command("worktree").description("self-provisioning worktrees ? install deps + copy local-only config");
30458
31327
  withExamples(mutating(
30459
- worktree.command("create <branch-or-issue>").description("create a worktree from a base ref and provision it (install deps + copy local-only config); an issue ref derives the <issue>-<slug> branch, and --claim claims its board item (#2996, formerly worktree new)").option("--from <ref>", "base ref to branch from", "origin/development").option("--base <ref>", "alias of --from (git's own word for the base ref)").option("--path <path>", "worktree path (default: ../mmi-worktrees/<RepoName>/<branch> \u2014 authoritative; the <RepoName> segment is what makes ownership provable from the path, #3471. gc/list read this root in BOTH the nested and the legacy flat shape)").option("--remote <name>", "remote to fetch when --from is a <remote>/<branch> ref", "origin").option("--slug <slug>", "issue-ref form: override the slug derived from the issue title").option("--claim", "issue-ref form: claim the issue's board item after provisioning").option("--for <login>", "with --claim: assign the board item to this login instead of @me"),
31328
+ worktree.command("create <branch-or-issue>").description("create a worktree from a base ref and provision it (install deps + copy local-only config); an issue ref derives the <issue>-<slug> branch, and --claim claims its board item (#2996, formerly worktree new)").option("--from <ref>", "base ref to branch from", "origin/development").option("--base <ref>", "alias of --from (git's own word for the base ref)").option("--path <path>", "worktree path (default: ../mmi-worktrees/<RepoName>/<branch> ? authoritative; the <RepoName> segment is what makes ownership provable from the path, #3471. gc/list read this root in BOTH the nested and the legacy flat shape)").option("--remote <name>", "remote to fetch when --from is a <remote>/<branch> ref", "origin").option("--slug <slug>", "issue-ref form: override the slug derived from the issue title").option("--claim", "issue-ref form: claim the issue's board item after provisioning").option("--for <login>", "with --claim: assign the board item to this login instead of @me"),
30460
31329
  worktreeCreatePlan
30461
31330
  ).action(async (target, o, cmd) => {
30462
31331
  let step = "resolve the branch name";
30463
31332
  try {
30464
31333
  if (o.base !== void 0 && cmd.getOptionValueSource("from") === "cli" && o.base !== o.from) {
30465
- return fail(`worktree create: --from and --base are the same option (--base is an alias); got --from ${o.from} and --base ${o.base} \u2014 pass one`);
31334
+ return fail(`worktree create: --from and --base are the same option (--base is an alias); got --from ${o.from} and --base ${o.base} ? pass one`);
30466
31335
  }
30467
31336
  const fromRef = o.base ?? o.from;
30468
31337
  const issueForm = isIssueRef(target);
@@ -30475,7 +31344,7 @@ withExamples(mutating(
30475
31344
  const slug = o.slug ?? await fetchIssueTitle(selector.repo, selector.number).then((t) => t ? slugifyIssueTitle(t) : "");
30476
31345
  branch = buildNewBranchName(selector.number, slug ?? "");
30477
31346
  if (PROTECTED_BRANCHES2.has(branch)) {
30478
- return fail(`worktree create: generated branch name '${branch}' collides with a protected train branch \u2014 pass a branch name instead`);
31347
+ return fail(`worktree create: generated branch name '${branch}' collides with a protected train branch ? pass a branch name instead`);
30479
31348
  }
30480
31349
  } else if (o.claim || o.for) {
30481
31350
  return fail("worktree create: --claim/--for need an issue-ref argument (e.g. worktree create 2687 --claim)");
@@ -30499,7 +31368,7 @@ withExamples(mutating(
30499
31368
  if (preferRemote && await revParseRef(preferRemote)) base = preferRemote;
30500
31369
  if (!o.json) {
30501
31370
  const baseSha = (await revParseRef(base))?.slice(0, 12) ?? "unresolved";
30502
- const localOnly = preferRemote && base !== preferRemote ? ` (no ${preferRemote} \u2014 local ref)` : "";
31371
+ const localOnly = preferRemote && base !== preferRemote ? ` (no ${preferRemote} ? local ref)` : "";
30503
31372
  console.error(` base ${base} ${baseSha}${localOnly}`);
30504
31373
  }
30505
31374
  const registered = parseWorktreePorcelainEntries((await execFileP2(
@@ -30515,7 +31384,7 @@ withExamples(mutating(
30515
31384
  return fail(`worktree create: ${wtPath} is already registered for '${exact.branch ?? "detached HEAD"}', not '${branch}'`);
30516
31385
  }
30517
31386
  const status = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
30518
- if (status) return fail(`worktree create: refusing to resume ${wtPath} \u2014 it has uncommitted changes`);
31387
+ if (status) return fail(`worktree create: refusing to resume ${wtPath} ? it has uncommitted changes`);
30519
31388
  const head = await revParseRef(`refs/heads/${branch}`);
30520
31389
  const baseOid = await revParseRef(base);
30521
31390
  if (!head || !baseOid) return fail(`worktree create: could not prove '${branch}' and '${base}' before resume`);
@@ -30525,7 +31394,7 @@ withExamples(mutating(
30525
31394
  { timeout: GIT_TIMEOUT_MS }
30526
31395
  ).then(() => true).catch(() => false);
30527
31396
  if (!canFastForward) {
30528
- return fail(`worktree create: refusing to resume '${branch}' \u2014 it has local commits or diverges from ${base}`);
31397
+ return fail(`worktree create: refusing to resume '${branch}' ? it has local commits or diverges from ${base}`);
30529
31398
  }
30530
31399
  await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
30531
31400
  resumed = true;
@@ -30598,11 +31467,11 @@ withExamples(mutating(
30598
31467
  console.log(` installed: ${report.installed.map((i) => i.dir || ".").join(", ") || "none"}`);
30599
31468
  console.log(` copied: ${report.copied.join(", ") || "none"}`);
30600
31469
  if (issueForm && o.claim && selector) {
30601
- if (claimError) console.error(` warning: board claim failed (${claimError}) \u2014 worktree is ready, finish the claim manually`);
31470
+ if (claimError) console.error(` warning: board claim failed (${claimError}) ? worktree is ready, finish the claim manually`);
30602
31471
  else console.log(` board item ${selector.repo}#${selector.number} claimed`);
30603
31472
  }
30604
31473
  } catch (e) {
30605
- fail(`worktree create: failed at step '${step}' \u2014 ${e.message}`);
31474
+ fail(`worktree create: failed at step '${step}' ? ${e.message}`);
30606
31475
  }
30607
31476
  }), [
30608
31477
  "mmi-cli worktree create fix/gc-sweep-2687",
@@ -30613,7 +31482,7 @@ worktree.command("setup [path]").description("provision an existing worktree (in
30613
31482
  const root = path2 ?? process.cwd();
30614
31483
  const release = acquireWorktreeSetupLock(root);
30615
31484
  if (!release) {
30616
- if (!o.quiet && !o.json) console.log("worktree setup: another provision is in progress \u2014 skipping");
31485
+ if (!o.quiet && !o.json) console.log("worktree setup: another provision is in progress ? skipping");
30617
31486
  return;
30618
31487
  }
30619
31488
  try {
@@ -30633,7 +31502,7 @@ worktree.command("setup [path]").description("provision an existing worktree (in
30633
31502
  release();
30634
31503
  }
30635
31504
  });
30636
- worktree.command("events").description("who created and who removed this repo's worktrees \u2014 the append-only attribution log (#3580)").option("--limit <n>", "most recent events to show", "50").option("--json", "machine-readable output").action(async (o) => {
31505
+ worktree.command("events").description("who created and who removed this repo's worktrees ? the append-only attribution log (#3580)").option("--limit <n>", "most recent events to show", "50").option("--json", "machine-readable output").action(async (o) => {
30637
31506
  const limit = Number.parseInt(o.limit, 10);
30638
31507
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree events: --limit must be a positive integer");
30639
31508
  const primaryRoot = await primaryCheckoutRoot(process.cwd()) ?? process.cwd();
@@ -30651,11 +31520,11 @@ async function attachToProject(issueNumber, repo, priority) {
30651
31520
  try {
30652
31521
  cfg = await loadConfigForRepo(targetRepo2);
30653
31522
  } catch (e) {
30654
- console.error(`issue create: board attach skipped \u2014 ${e.message}`);
31523
+ console.error(`issue create: board attach skipped ? ${e.message}`);
30655
31524
  return { onBoard: false };
30656
31525
  }
30657
31526
  if (!cfg.projectId) {
30658
- console.error(`issue create: board attach skipped \u2014 no Hub registry board META for ${targetRepo2 ?? "current repo"}; run \`mmi-cli org project get ${targetRepo2 ?? "<owner/repo>"}\` and backfill board coords`);
31527
+ console.error(`issue create: board attach skipped ? no Hub registry board META for ${targetRepo2 ?? "current repo"}; run \`mmi-cli org project get ${targetRepo2 ?? "<owner/repo>"}\` and backfill board coords`);
30659
31528
  return { onBoard: false };
30660
31529
  }
30661
31530
  try {
@@ -30701,7 +31570,7 @@ function scheduleRelatedDiscovery(o) {
30701
31570
  try {
30702
31571
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body, "--fail-soft"];
30703
31572
  if (o.repo) args.push("--repo", o.repo);
30704
- spawnDetachedSelf(args, { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
31573
+ spawnDetachedSelf(args, { spawn: import_node_child_process18.spawn, execPath: process.execPath, scriptPath: process.argv[1] }, { cwd: process.cwd() });
30705
31574
  } catch {
30706
31575
  }
30707
31576
  }
@@ -30710,14 +31579,236 @@ registerEdgeCommands(program2);
30710
31579
  registerBoxCommands(program2);
30711
31580
  registerSchedulesCommands(program2);
30712
31581
  registerSchedulesLiftCommand(program2);
30713
- var docs = program2.command("docs").description("generated docs surfaces \u2014 the routing index (org knowledge layer)");
30714
- docs.command("index").description("regenerate docs/index.md from the docs/ tree (--write, the default) or fail on drift (--check) \u2014 the generated routing index, never hand-maintained").option("--check", "compare against the committed docs/index.md and exit 1 on drift; never write").option("--write", "regenerate docs/index.md when it has drifted (the default)").action(async (o) => {
31582
+ var repoIndex = program2.command("repo-index").description("Hub cloud + local pointer index ? paths/symbols only, never wiki prose (Hub#4133)");
31583
+ repoIndex.command("rebuild").description("walk the checkout (git ls-files + gitignore/secrets walls), rebuild the local pointer index, drop orphans").option("--json", "machine-readable projection summary").action(async (o) => {
31584
+ try {
31585
+ const root = await repoRoot();
31586
+ const repo = inferRepoSlug(root);
31587
+ const built = rebuildRepoIndex(root, repo);
31588
+ const symbolCount = built.entries.reduce((n, e) => n + e.symbols.length, 0);
31589
+ if (o.json) {
31590
+ consoleIo.log(JSON.stringify({
31591
+ ok: true,
31592
+ repo: built.repo,
31593
+ builtAt: built.builtAt,
31594
+ fileCount: built.entries.length,
31595
+ symbolCount,
31596
+ store: repoIndexStatus(root).path
31597
+ }, null, 2));
31598
+ return;
31599
+ }
31600
+ console.log(`repo-index: rebuilt ${built.repo} ? ${built.entries.length} files, ${symbolCount} symbols`);
31601
+ } catch (e) {
31602
+ await failGraceful(e.message);
31603
+ }
31604
+ });
31605
+ repoIndex.command("publish").description("publish this checkout's projection to Hub cloud (rebuilds locally first; estate indexer also runs on a schedule)").option("--embed", "request Titan file embeddings on the Hub (capped per publish)").option("--json", "machine-readable result").action(async (o) => {
31606
+ try {
31607
+ const root = await repoRoot();
31608
+ const repo = inferRepoSlug(root);
31609
+ const built = rebuildRepoIndex(root, repo);
31610
+ const cfg = await loadConfig();
31611
+ const res = await publishRepoIndexCloud(
31612
+ { repo, builtAt: built.builtAt, entries: built.entries, embed: o.embed === true },
31613
+ registryClientDeps(cfg)
31614
+ );
31615
+ if (!res.ok) return await failGraceful(res.error);
31616
+ if (o.json) {
31617
+ consoleIo.log(JSON.stringify(res.body, null, 2));
31618
+ return;
31619
+ }
31620
+ console.log(`repo-index: published ${res.body.repo} ? ${res.body.fileCount} files, emb=${res.body.embCount ?? 0}`);
31621
+ } catch (e) {
31622
+ return await failGraceful(e.message);
31623
+ }
31624
+ });
31625
+ 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) => {
31626
+ try {
31627
+ const limit = Math.max(1, Math.min(100, Number(o.limit ?? 20) || 20));
31628
+ const useLocal = o.local === true && o.cloud !== true;
31629
+ if (useLocal) {
31630
+ const root = await repoRoot();
31631
+ let idx = loadRepoIndex(root);
31632
+ if (!idx) idx = rebuildRepoIndex(root, inferRepoSlug(root));
31633
+ const hits = searchRepoIndex(idx, query, limit);
31634
+ if (o.json) {
31635
+ consoleIo.log(JSON.stringify({ ok: true, source: "local", repo: idx.repo, query, hits }, null, 2));
31636
+ return;
31637
+ }
31638
+ if (hits.length === 0) {
31639
+ console.log(`repo-index: no local hits for ${JSON.stringify(query)}`);
31640
+ return;
31641
+ }
31642
+ for (const h of hits) {
31643
+ const sym = h.symbol ? ` ${h.symbol}` : "";
31644
+ console.log(`${h.score.toFixed(2)} ${h.kind.padEnd(8)} ${h.path}${sym} (${h.why})`);
31645
+ }
31646
+ return;
31647
+ }
31648
+ const mode = o.semantic ? "semantic" : o.lexical ? "lexical" : "hybrid";
31649
+ const cfg = await loadConfig();
31650
+ const res = await searchRepoIndexCloud(query, { mode, limit, repo: o.repo }, registryClientDeps(cfg));
31651
+ if (!res.ok) return await failGraceful(res.error);
31652
+ if (o.json) {
31653
+ consoleIo.log(JSON.stringify({ source: "cloud", ...res }, null, 2));
31654
+ return;
31655
+ }
31656
+ if (res.hits.length === 0) {
31657
+ console.log(`repo-index: no cloud hits for ${JSON.stringify(query)} (${res.mode})`);
31658
+ return;
31659
+ }
31660
+ for (const h of res.hits) {
31661
+ const sym = h.symbol ? ` ${h.symbol}` : "";
31662
+ console.log(`${h.score.toFixed(2)} ${h.kind.padEnd(8)} ${h.repo} ${h.path}${sym} (${h.why})`);
31663
+ }
31664
+ } catch (e) {
31665
+ return await failGraceful(e.message);
31666
+ }
31667
+ });
31668
+ 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) => {
31669
+ try {
31670
+ if (o.cloud || o.repo) {
31671
+ const cfg = await loadConfig();
31672
+ const st2 = await statusRepoIndexCloud(o.repo, registryClientDeps(cfg));
31673
+ if (isRepoIndexStatusError(st2)) {
31674
+ if (o.json) {
31675
+ consoleIo.log(JSON.stringify(st2, null, 2));
31676
+ return await failGraceful(st2.error);
31677
+ }
31678
+ return await failGraceful(explainRepoIndexStatusError(st2));
31679
+ }
31680
+ if (o.json) {
31681
+ consoleIo.log(JSON.stringify(st2, null, 2));
31682
+ return;
31683
+ }
31684
+ if (st2.present === true && typeof st2.repo === "string") {
31685
+ const fileCount = Number(st2.fileCount ?? 0);
31686
+ const embCount = Number(st2.embCount ?? 0);
31687
+ const embGap = Number(st2.embGap ?? Math.max(0, fileCount - embCount));
31688
+ const stale = st2.staleBuiltAt === true ? " stale-builtAt" : "";
31689
+ console.log(
31690
+ `repo-index: cloud ${st2.repo} built ${st2.builtAt} ? ${fileCount} files, emb=${embCount}/${fileCount} gap=${embGap}${stale}`
31691
+ );
31692
+ return;
31693
+ }
31694
+ consoleIo.log(JSON.stringify(st2, null, 2));
31695
+ const count = typeof st2.count === "number" ? st2.count : void 0;
31696
+ const present = st2.present;
31697
+ if (count === 0 || present === false) {
31698
+ console.error(
31699
+ "repo-index: no cloud projection yet ? after Hub deploy, run harbour `repo-index-reconcile` or `mmi-cli repo-index sync-estate` (see docs/Guides/repo-index-runbook.md)."
31700
+ );
31701
+ }
31702
+ return;
31703
+ }
31704
+ const root = await repoRoot();
31705
+ const st = repoIndexStatus(root);
31706
+ if (o.json) {
31707
+ consoleIo.log(JSON.stringify(st, null, 2));
31708
+ return;
31709
+ }
31710
+ if (!st.present) {
31711
+ console.log(`repo-index: local missing ? cloud search needs no rebuild; optional \`repo-index rebuild\` for --local`);
31712
+ return;
31713
+ }
31714
+ console.log(`repo-index: local ${st.repo} built ${st.builtAt} ? ${st.fileCount} files, ${st.symbolCount} symbols`);
31715
+ } catch (e) {
31716
+ return await failGraceful(e.message);
31717
+ }
31718
+ });
31719
+ 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) => {
31720
+ try {
31721
+ const root = await repoRoot();
31722
+ const goldenPath = o.golden || defaultGoldenPath(root);
31723
+ const golden = loadGoldenSuite(goldenPath);
31724
+ const result = o.live ? await runRepoIndexHealth({
31725
+ golden,
31726
+ live: {
31727
+ status: async () => {
31728
+ const cfg = await loadConfig();
31729
+ const st = await statusRepoIndexCloud(void 0, registryClientDeps(cfg));
31730
+ if ("error" in st && st.ok === false) return { ok: false, error: st.error };
31731
+ return st;
31732
+ },
31733
+ search: async (q, mode) => {
31734
+ const cfg = await loadConfig();
31735
+ const res = await searchRepoIndexCloud(q, { mode, limit: 10 }, registryClientDeps(cfg));
31736
+ if (!res.ok) {
31737
+ return {
31738
+ ok: false,
31739
+ error: res.error,
31740
+ status: res.status
31741
+ };
31742
+ }
31743
+ return { ok: true, hits: res.hits };
31744
+ }
31745
+ }
31746
+ }) : await runRepoIndexHealth({ golden });
31747
+ if (o.json) {
31748
+ consoleIo.log(JSON.stringify({ ok: result.ok, findings: result.findings, golden: goldenPath }, null, 2));
31749
+ } else {
31750
+ for (const f of result.findings) {
31751
+ console.log(`${f.ok ? "ok" : "FAIL"} ${f.code} ${f.detail}`);
31752
+ }
31753
+ console.log(result.ok ? "repo-index health: PASS" : "repo-index health: FAIL");
31754
+ }
31755
+ if (!result.ok) process.exitCode = 1;
31756
+ } catch (e) {
31757
+ return await failGraceful(e.message);
31758
+ }
31759
+ });
31760
+ repoIndex.command("gc").description("remove cloud projections for repos no longer on the registry roster").option("--cloud", "required ? GC only applies to Hub cloud").option("--json", "machine-readable result").action(async (o) => {
31761
+ try {
31762
+ if (!o.cloud) return await failGraceful("repo-index gc requires --cloud");
31763
+ const cfg = await loadConfig();
31764
+ const res = await gcRepoIndexCloud(registryClientDeps(cfg));
31765
+ if (!res.ok) return await failGraceful(res.error);
31766
+ if (o.json) {
31767
+ consoleIo.log(JSON.stringify(res, null, 2));
31768
+ return;
31769
+ }
31770
+ console.log(`repo-index: gc removed ${res.removed.length} orphan projection(s)`);
31771
+ } catch (e) {
31772
+ return await failGraceful(e.message);
31773
+ }
31774
+ });
31775
+ repoIndex.command("sync-estate").description("Hub indexer: shallow-clone registry repos, rebuild, publish to cloud (CI / operator)").option("--repo <owner/name>", "limit to one registered repo").option("--embed", "request Titan embeddings on publish").option("--json", "machine-readable result").action(async (o) => {
31776
+ try {
31777
+ const cfg = await loadConfig();
31778
+ const gh = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
31779
+ if (!gh) await failGraceful("sync-estate needs GH_TOKEN or GITHUB_TOKEN with contents:read on target repos");
31780
+ const res = await syncEstateRepoIndex({
31781
+ deps: registryClientDeps(cfg),
31782
+ repo: o.repo,
31783
+ embed: o.embed === true,
31784
+ githubToken: gh
31785
+ });
31786
+ if (o.json) {
31787
+ consoleIo.log(JSON.stringify(res, null, 2));
31788
+ if (!res.ok) process.exitCode = 1;
31789
+ return;
31790
+ }
31791
+ for (const p of res.published) {
31792
+ const gap = p.embGap ?? Math.max(0, p.fileCount - (p.embCount ?? 0));
31793
+ const trunc = p.embTruncated ? " truncated" : "";
31794
+ console.log(`repo-index: published ${p.repo} ? ${p.fileCount} files emb=${p.embCount ?? 0}/${p.fileCount} gap=${gap}${trunc}`);
31795
+ }
31796
+ for (const f of res.failed) {
31797
+ console.error(`repo-index: FAILED ${f.repo}: ${f.error}`);
31798
+ }
31799
+ if (!res.ok) await failGraceful(`sync-estate incomplete (${res.failed.length} failed)`);
31800
+ } catch (e) {
31801
+ await failGraceful(e.message);
31802
+ }
31803
+ });
31804
+ var docs = program2.command("docs").description("generated docs surfaces ? the routing index (org knowledge layer)");
31805
+ docs.command("index").description("regenerate docs/index.md from the docs/ tree (--write, the default) or fail on drift (--check) ? the generated routing index, never hand-maintained").option("--check", "compare against the committed docs/index.md and exit 1 on drift; never write").option("--write", "regenerate docs/index.md when it has drifted (the default)").action(async (o) => {
30715
31806
  try {
30716
31807
  const root = await repoRoot();
30717
31808
  const result = docsIndex(createDocsIndexDeps(root), { check: Boolean(o.check) });
30718
31809
  if (o.check) {
30719
31810
  if (result.drift) {
30720
- return failGraceful(`docs index: ${DOCS_INDEX_PATH} is stale \u2014 run \`mmi-cli docs index --write\` and commit the result`);
31811
+ return failGraceful(`docs index: ${DOCS_INDEX_PATH} is stale ? run \`mmi-cli docs index --write\` and commit the result`);
30721
31812
  }
30722
31813
  console.log(`docs index: ${DOCS_INDEX_PATH} is current`);
30723
31814
  return;
@@ -30727,7 +31818,7 @@ docs.command("index").description("regenerate docs/index.md from the docs/ tree
30727
31818
  await failGraceful(e.message);
30728
31819
  }
30729
31820
  });
30730
- docs.command("refs").description("deterministic doc reference gate: every backticked repo path, relative .md link, `mmi-cli` command ref, and `<!-- pinned by \u2026 -->` comment across docs/** + README.md + architecture.md must resolve, or exit 1 (fleet-portable port of the Hub gate scripts/check-doc-refs.mjs, #3339)").option("--json", "machine-readable findings list: { ok, docCount, findings[], warnings[] }").action(async (o) => {
31821
+ docs.command("refs").description("deterministic doc reference gate: every backticked repo path, relative .md link, `mmi-cli` command ref, and `<!-- pinned by ? -->` comment across docs/** + README.md + architecture.md must resolve, or exit 1 (fleet-portable port of the Hub gate scripts/check-doc-refs.mjs, #3339)").option("--json", "machine-readable findings list: { ok, docCount, findings[], warnings[] }").action(async (o) => {
30731
31822
  try {
30732
31823
  const root = await repoRoot();
30733
31824
  const commandPaths = new Set(buildCommandManifest(program2).index.map((entry) => entry.path));
@@ -30751,7 +31842,7 @@ docs.command("refs").description("deterministic doc reference gate: every backti
30751
31842
  await failGraceful(e.message);
30752
31843
  }
30753
31844
  });
30754
- var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract \u2014 every child process must be unable to pop a console window");
31845
+ var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract ? every child process must be unable to pop a console window");
30755
31846
  spawnCmd.command("policy").description("enforce the windowsHide contract across this repo's tracked source: a child_process call must set windowsHide, or take its options from a named constant, a type annotation, or a forwarded caller bag that does. Waive a call the scan cannot classify with a `// windows-hide-exempt: <reason>` comment on the line above it (#3979)").option("--json", "machine-readable result: { ok, scannedCount, findings[] }").action(async (o) => {
30756
31847
  try {
30757
31848
  const root = await repoRoot();
@@ -30767,14 +31858,14 @@ spawnCmd.command("policy").description("enforce the windowsHide contract across
30767
31858
  }
30768
31859
  for (const f of result.findings) console.error(`spawn policy: ${f.detail}`);
30769
31860
  console.error(
30770
- "\nA process spawned without windowsHide allocates a console when it has none to inherit.\nWhere Windows Terminal is the default terminal, that console becomes a desktop window the\nhost never reaps \u2014 it outlives the process as an empty frame. Set windowsHide: true, or\nroute the call through a helper that does."
31861
+ "\nA process spawned without windowsHide allocates a console when it has none to inherit.\nWhere Windows Terminal is the default terminal, that console becomes a desktop window the\nhost never reaps ? it outlives the process as an empty frame. Set windowsHide: true, or\nroute the call through a helper that does."
30771
31862
  );
30772
31863
  process.exitCode = 1;
30773
31864
  } catch (e) {
30774
31865
  await failGraceful(e.message);
30775
31866
  }
30776
31867
  });
30777
- var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
31868
+ var tests = program2.command("tests").description("a repo's test-policy.json ? the opt-in test contract and its enforcement");
30778
31869
  tests.command("policy").description("enforce this repo's test-policy.json against the diff: a mandatory-zone change must carry a test, an unrequested new test file is refused, a `protected` test file may not be deleted or renamed away, and a `protected` entry naming a missing file is refused. Override any of them with a `Test-Policy-Override: <reason>` commit trailer (#3605)").option("--json", "machine-readable result: { ok, base, changedCount, findings[] }").option("--base <ref>", "comparison base (default: TEST_POLICY_BASE, then origin/development, then origin/main)").action(async (o) => {
30779
31870
  try {
30780
31871
  const root = await repoRoot();
@@ -30787,7 +31878,7 @@ tests.command("policy").description("enforce this repo's test-policy.json agains
30787
31878
  if (result.overriddenBy && result.ok) {
30788
31879
  const { sha, reason, kinds } = result.overriddenBy;
30789
31880
  const scope = (result.waived ?? []).map((f) => f.kind).join(", ") || "nothing";
30790
- console.log(`tests policy: overridden by ${sha.slice(0, 8)} (waiving ${scope}; scope ${kinds.join(", ")}) \u2014 ${reason}`);
31881
+ console.log(`tests policy: overridden by ${sha.slice(0, 8)} (waiving ${scope}; scope ${kinds.join(", ")}) ? ${reason}`);
30791
31882
  return;
30792
31883
  }
30793
31884
  if (result.ok) {
@@ -30802,7 +31893,7 @@ tests.command("policy").description("enforce this repo's test-policy.json agains
30802
31893
  await failGraceful(e.message);
30803
31894
  }
30804
31895
  });
30805
- var docsAudit = program2.command("docs-audit").description("the docs janitor verdict ledger \u2014 record a dated run verdict, or read the dead-man status back");
31896
+ var docsAudit = program2.command("docs-audit").description("the docs janitor verdict ledger ? record a dated run verdict, or read the dead-man status back");
30806
31897
  docsAudit.command("record").description("write a dated janitor verdict for one repo to the registry ledger (master-only server-side)").option("--repo <owner/name>", "the repo the verdict is for (default: the current repo)").option("--date <YYYY-MM-DD>", "the ISO day the run examined (default: today)").requiredOption("--sha-range <a..b>", "the git range the janitor read (e.g. <lastVerdictSha>..HEAD)").addOption(new Option("--outcome <kind>", "clean | refreshed | failed").makeOptionMandatory().choices(["clean", "refreshed", "failed"])).option("--count <n>", "docs refreshed (required when --outcome refreshed)").option("--reason <text>", "why the run failed (required when --outcome failed)").requiredOption("--checker-vendor <vendor>", "which vendor's model actually ran the check").action(async (o) => {
30807
31898
  try {
30808
31899
  const repo = o.repo ?? await currentRepoFullName();
@@ -30818,7 +31909,7 @@ docsAudit.command("record").description("write a dated janitor verdict for one r
30818
31909
  await failGraceful(e.message);
30819
31910
  }
30820
31911
  });
30821
- docsAudit.command("status").description("read the janitor dead-man verdict back for one repo \u2014 missing/stale/failed is RED; a not-yet-armed registry route is an informational exit 0").option("--repo <owner/name>", "the repo to check (default: the current repo)").option("--json", "machine-readable output \u2014 the discrete state rather than the sentence").action(async (o) => {
31912
+ docsAudit.command("status").description("read the janitor dead-man verdict back for one repo ? missing/stale/failed is RED; a not-yet-armed registry route is an informational exit 0").option("--repo <owner/name>", "the repo to check (default: the current repo)").option("--json", "machine-readable output ? the discrete state rather than the sentence").action(async (o) => {
30822
31913
  try {
30823
31914
  const repo = o.repo ?? await currentRepoFullName();
30824
31915
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -30851,7 +31942,7 @@ async function reportWrite(label, res) {
30851
31942
  }
30852
31943
  if (res.error) return failGraceful(`${label}: ${res.error}`);
30853
31944
  const detail = res.body?.error ?? "";
30854
- return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
31945
+ return failGraceful(`${label}: HTTP ${res.status}${detail ? ` ? ${detail}` : ""}`);
30855
31946
  }
30856
31947
  var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
30857
31948
  tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; project-admin own dev/rc, master main").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
@@ -30878,7 +31969,7 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
30878
31969
  printLine(o.json ? JSON.stringify(result, null, 2) : renderTenantControl(result));
30879
31970
  }
30880
31971
  if (result.conclusion === "failure") {
30881
- return failGraceful(`runtime tenant control ${stage} ${action}: ${result.category ?? "failed"} \u2014 ${result.note}`);
31972
+ return failGraceful(`runtime tenant control ${stage} ${action}: ${result.category ?? "failed"} ? ${result.note}`);
30882
31973
  }
30883
31974
  } catch (e) {
30884
31975
  return failGraceful(`runtime tenant control: ${e.message}`);
@@ -30887,7 +31978,7 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
30887
31978
  tenant.command("reconcile <owner/repo> <stage>").description("re-render this tenant stage's generated box assets from registry truth; project-admin own dev/rc, master main; watches by default").option("--watch", "wait for the tenant-reconcile.yml run (default)").option("--no-watch", "return after dispatch; do not redeploy until the reconcile run succeeds").option("--json", "machine-readable output").action(async (repo, stage, o) => {
30888
31979
  try {
30889
31980
  const result = await runTenantReconcile(trainApplyDeps(), { repo, stage, watch: o.watch });
30890
- printLine(o.json ? JSON.stringify(result, null, 2) : `${result.note}${result.runUrl ? ` \u2014 ${result.runUrl}` : ""}`);
31981
+ printLine(o.json ? JSON.stringify(result, null, 2) : `${result.note}${result.runUrl ? ` ? ${result.runUrl}` : ""}`);
30891
31982
  if (result.conclusion === "failure" || !result.dispatched) process.exitCode = 1;
30892
31983
  } catch (e) {
30893
31984
  return failGraceful(`runtime tenant reconcile: ${e.message}`);
@@ -30900,7 +31991,7 @@ tenant.command("status <owner/repo> <stage>").description("read tenant runtime r
30900
31991
  console.log(JSON.stringify(result, null, 2));
30901
31992
  if (result.publicProbe?.ok === false) process.exitCode = 1;
30902
31993
  });
30903
- tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the central tenant-deploy.yml for an already-promoted ref (no re-tag/merge); train-authority gated").option("--ref <ref>", "ref to deploy (defaults to the stage branch rc/main, or `development` for dev \u2014 the promoted/staging ref)").option("--watch", "block on the dispatched run and report its outcome (gh run watch --exit-status)").option("--json", "machine-readable output").action(async (repo, stage, o) => {
31994
+ tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the central tenant-deploy.yml for an already-promoted ref (no re-tag/merge); train-authority gated").option("--ref <ref>", "ref to deploy (defaults to the stage branch rc/main, or `development` for dev ? the promoted/staging ref)").option("--watch", "block on the dispatched run and report its outcome (gh run watch --exit-status)").option("--json", "machine-readable output").action(async (repo, stage, o) => {
30904
31995
  if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime tenant redeploy: <stage> must be dev, rc, or main");
30905
31996
  try {
30906
31997
  const result = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, watch: o.watch });
@@ -30909,9 +32000,9 @@ tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the cen
30909
32000
  return failGraceful(`runtime tenant redeploy: ${e.message}`);
30910
32001
  }
30911
32002
  });
30912
- tenant.command("sweep-rc").description("discover (and optionally retire) running rc tenant runtimes across tenant-containers \u2014 orphan cleanup after a failed post-release retire (#942)").option("--retire", "retire every running rc runtime found (requires --yes) \u2014 WARNING: tears down a legitimately-staged rc too").option("--yes", "confirm the destructive --retire").option("--json", "machine-readable output").action(async (o) => {
32003
+ tenant.command("sweep-rc").description("discover (and optionally retire) running rc tenant runtimes across tenant-containers ? orphan cleanup after a failed post-release retire (#942)").option("--retire", "retire every running rc runtime found (requires --yes) ? WARNING: tears down a legitimately-staged rc too").option("--yes", "confirm the destructive --retire").option("--json", "machine-readable output").action(async (o) => {
30913
32004
  if (o.retire && !o.yes) {
30914
- return fail("runtime tenant sweep-rc --retire is destructive (it tears down EVERY running rc, including one legitimately staged between /rcand and /release) \u2014 re-run with --yes to confirm");
32005
+ return fail("runtime tenant sweep-rc --retire is destructive (it tears down EVERY running rc, including one legitimately staged between /rcand and /release) ? re-run with --yes to confirm");
30915
32006
  }
30916
32007
  const cfg = await loadConfig();
30917
32008
  const cdeps = registryClientDeps(cfg);
@@ -30964,7 +32055,7 @@ async function buildTenantRuntimeStatusFor(target, stage, cfg) {
30964
32055
  lastTenantDeployRun: (await fetchLastTenantDeployRun(slug, stage)).run
30965
32056
  });
30966
32057
  }
30967
- var project = program2.command("project").description("the DDB org registry \u2014 list/get projects (any member); set is master-only");
32058
+ var project = program2.command("project").description("the DDB org registry ? list/get projects (any member); set is master-only");
30968
32059
  async function projectTarget(commandName, explicitTarget) {
30969
32060
  return requireProjectTarget(commandName, explicitTarget, explicitTarget ? void 0 : await resolveRepo());
30970
32061
  }
@@ -31008,7 +32099,7 @@ project.command("list").description("list all projects (identity + board, never
31008
32099
  console.log(`${p.slug ?? "?"} - ${p.name ?? ""}${p.division ? ` [${p.division}]` : ""}${p.class ? ` (${p.class})` : ""}${p.projectType ? ` <${p.projectType}>` : ""}${p.deployModel ? ` {${p.deployModel}}` : ""}`);
31009
32100
  }
31010
32101
  });
31011
- project.command("get [owner/repo]").description("a project's META (board ids + pointers) by repo or slug; defaults to the current repo \u2014 identity, NOT deploy coords").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
32102
+ project.command("get [owner/repo]").description("a project's META (board ids + pointers) by repo or slug; defaults to the current repo ? identity, NOT deploy coords").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
31012
32103
  const cfg = await loadConfig();
31013
32104
  let target;
31014
32105
  try {
@@ -31018,7 +32109,7 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
31018
32109
  }
31019
32110
  const read = await fetchProjectBySlugChecked(slugOf(target), registryClientDeps(cfg));
31020
32111
  if (!read.ok) {
31021
- return failGraceful(`org project get: Hub registry read failed (${read.error}) \u2014 likely transient (cold start, network, or auth blip); retry shortly`);
32112
+ return failGraceful(`org project get: Hub registry read failed (${read.error}) ? likely transient (cold start, network, or auth blip); retry shortly`);
31022
32113
  }
31023
32114
  if (!read.project) {
31024
32115
  return failGraceful(`org project get: no registry META for ${target} (unknown or unbootstrapped)`);
@@ -31028,10 +32119,10 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
31028
32119
  const m = read.project;
31029
32120
  const track = resolveReleaseTrack(m, void 0, target);
31030
32121
  const stages = branchesForTrack(track).join(" -> ");
31031
- const note = track === "direct" ? " (direct \u2014 no rc; /rcand refuses, /release ships development -> main)" : track === "trunk" ? " (trunk \u2014 main only)" : "";
32122
+ const note = track === "direct" ? " (direct ? no rc; /rcand refuses, /release ships development -> main)" : track === "trunk" ? " (trunk ? main only)" : "";
31032
32123
  console.error(
31033
- `${m.name ?? target} \xB7 class ${m.class ?? "?"} \xB7 deploy ${m.deployModel ?? "?"}
31034
- release track: ${track} \u2014 stages: ${stages}${note}
32124
+ `${m.name ?? target} ? class ${m.class ?? "?"} ? deploy ${m.deployModel ?? "?"}
32125
+ release track: ${track} ? stages: ${stages}${note}
31035
32126
  deploys run centrally (tenant-deploy.yml); product repos carry no deploy files. Inspect nonsecret DEPLOY facts with \`mmi-cli org project deploy get\`; full coords remain OIDC-gated.`
31036
32127
  );
31037
32128
  }
@@ -31064,7 +32155,7 @@ projectDeploy.command("get [owner/repo]").description("read nonsecret DEPLOY# fa
31064
32155
  const payload = stage ? { slug: out.slug, stage, deploy: out.stages[stage] ?? null } : out;
31065
32156
  console.log(JSON.stringify(payload));
31066
32157
  });
31067
- project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").addOption(new Option("--class <class>", "deployable | content").choices(["deployable", "content"])).addOption(new Option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (capability shape)`).choices([...PROJECT_TYPES])).addOption(new Option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).choices([...DEPLOY_MODELS])).addOption(new Option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).choices([...RELEASE_TRACKS])).option("--var <KEY=VALUE...>", settableVarHelp()).option("--set <KEY=VALUE...>", "alias of --var (one KEY=VALUE per flag; repeat the flag to set several)").option("--secrets-file <path>", 'read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (clear all with --unset secrets). SHAPE: a JSON object keyed by env name, each entry {"key":"UPPER_SNAKE" (repeat the env name), "purpose":"<non-empty>", "group":"<e.g. auth|database>", "owner":"<github login>", "stages":[] (empty = the one stageless shared value; else any of dev/rc/main), "consumers":["runtime"|"box"|\u2026], "provider":"<optional>"}').option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets, 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) => {
32158
+ project.command("set [owner/repo]").description("upsert project META (idempotent merge; master-gated)").addOption(new Option("--class <class>", "deployable | content").choices(["deployable", "content"])).addOption(new Option("--project-type <type>", `${PROJECT_TYPES.join(" | ")} (capability shape)`).choices([...PROJECT_TYPES])).addOption(new Option("--deploy-model <model>", `${DEPLOY_MODELS.join(" | ")} (release/deploy path; none means no Hub deploy registration)`).choices([...DEPLOY_MODELS])).addOption(new Option("--release-track <track>", `${RELEASE_TRACKS.join(" | ")} (branch topology; direct skips rc)`).choices([...RELEASE_TRACKS])).option("--var <KEY=VALUE...>", settableVarHelp()).option("--set <KEY=VALUE...>", "alias of --var (one KEY=VALUE per flag; repeat the flag to set several)").option("--secrets-file <path>", 'read the #2244 secrets catalog map (JSON) from a file and merge it per entry into the existing catalog (clear all with --unset secrets). SHAPE: a JSON object keyed by env name, each entry {"key":"UPPER_SNAKE" (repeat the env name), "purpose":"<non-empty>", "group":"<e.g. auth|database>", "owner":"<github login>", "stages":[] (empty = the one stageless shared value; else any of dev/rc/main), "consumers":["runtime"|"box"|?], "provider":"<optional>"}').option("--unset <KEY...>", "META field to remove (repeatable): oauth, requiredRuntimeSecrets, requiredBuildSecrets, secrets, 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) => {
31068
32159
  const cfg = await loadConfig();
31069
32160
  let target;
31070
32161
  try {
@@ -31080,7 +32171,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
31080
32171
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
31081
32172
  if (o.secretsFile) {
31082
32173
  try {
31083
- vars.push(`secrets=${(0, import_node_fs36.readFileSync)(o.secretsFile, "utf8")}`);
32174
+ vars.push(`secrets=${(0, import_node_fs39.readFileSync)(o.secretsFile, "utf8")}`);
31084
32175
  } catch (e) {
31085
32176
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
31086
32177
  }
@@ -31105,7 +32196,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
31105
32196
  const res = await upsertProject(slug, { ...patch, repo }, registryClientDeps(cfg));
31106
32197
  return reportWrite("org project set", res);
31107
32198
  });
31108
- project.command("retire [owner/repo]").description("retire an orphaned registry slug (master-only): soft-delete the PROJECT# META row + drop the repo's board items; secrets/vault left alone. DRY-RUN by default \u2014 pass --apply to actually delete; defaults to the current repo").option("--apply", "actually delete (default is a dry-run preview of what would be removed)").option("--skip-board", "retire the registry META only \u2014 leave board items in place").option("--json", "machine-readable {slug, removedMeta, removedBoardItem} output").action(async (repoOrSlug, o) => {
32199
+ project.command("retire [owner/repo]").description("retire an orphaned registry slug (master-only): soft-delete the PROJECT# META row + drop the repo's board items; secrets/vault left alone. DRY-RUN by default ? pass --apply to actually delete; defaults to the current repo").option("--apply", "actually delete (default is a dry-run preview of what would be removed)").option("--skip-board", "retire the registry META only ? leave board items in place").option("--json", "machine-readable {slug, removedMeta, removedBoardItem} output").action(async (repoOrSlug, o) => {
31109
32200
  const cfg = await loadConfig();
31110
32201
  let target;
31111
32202
  try {
@@ -31137,9 +32228,9 @@ project.command("retire [owner/repo]").description("retire an orphaned registry
31137
32228
  const verb = result.applied ? "retired" : "WOULD retire (dry-run; pass --apply to delete)";
31138
32229
  printLine(`org project retire: ${verb} ${result.slug} (${result.repo})`);
31139
32230
  if (result.applied) {
31140
- printLine(` META: ${result.removedMeta.ok ? `removed=${result.removedMeta.removed ?? "unknown"}` : `FAILED \u2014 ${result.removedMeta.error}`}`);
32231
+ printLine(` META: ${result.removedMeta.ok ? `removed=${result.removedMeta.removed ?? "unknown"}` : `FAILED ? ${result.removedMeta.error}`}`);
31141
32232
  } else {
31142
- printLine(` META: ${result.removedMeta.ok ? result.removedMeta.existing ? "live row present \u2014 would be tombstoned" : "no live row (already absent/tombstoned)" : `read failed \u2014 ${result.removedMeta.error}`}`);
32233
+ printLine(` META: ${result.removedMeta.ok ? result.removedMeta.existing ? "live row present ? would be tombstoned" : "no live row (already absent/tombstoned)" : `read failed ? ${result.removedMeta.error}`}`);
31143
32234
  }
31144
32235
  if (result.removedBoardItem) {
31145
32236
  const b = result.removedBoardItem;
@@ -31150,7 +32241,7 @@ project.command("retire [owner/repo]").description("retire an orphaned registry
31150
32241
  printLine(` ${result.vaultNote}`);
31151
32242
  }
31152
32243
  if (result.applied && !result.removedMeta.ok) {
31153
- return failGraceful(`org project retire: META delete failed \u2014 ${result.removedMeta.error}`);
32244
+ return failGraceful(`org project retire: META delete failed ? ${result.removedMeta.error}`);
31154
32245
  }
31155
32246
  });
31156
32247
  var fullTrack = program2.command("full-track").description("direct-to-train readiness audits");
@@ -31178,7 +32269,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
31178
32269
  console.log(JSON.stringify(report, null, 2));
31179
32270
  if (!report.rcand.canApply) process.exitCode = 1;
31180
32271
  });
31181
- project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row \u2014 project-admin may set only --no-env-file on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row \u2014 `mmi-cli runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--clear-aliases", "remove EVERY serving alias from the row (#2986) \u2014 omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) \u2014 own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
32272
+ project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row ? project-admin may set only --no-env-file on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row ? `mmi-cli runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--clear-aliases", "remove EVERY serving alias from the row (#2986) ? omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) ? own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
31182
32273
  const cfg = await loadConfig();
31183
32274
  let target;
31184
32275
  try {
@@ -31204,7 +32295,7 @@ ${filelessTransitionGuide(target, o.stage)}`);
31204
32295
  if (verdict.warn) console.error(`org project set-deploy: ${verdict.warn}`);
31205
32296
  } else if (!o.force) {
31206
32297
  return fail(
31207
- `org project set-deploy: cannot verify ${target} ${branch} from this checkout \u2014 refusing to change noEnvFile. Run from the target repo with origin/${branch} fetched.
32298
+ `org project set-deploy: cannot verify ${target} ${branch} from this checkout ? refusing to change noEnvFile. Run from the target repo with origin/${branch} fetched.
31208
32299
  ${filelessTransitionGuide(target, o.stage)}`
31209
32300
  );
31210
32301
  } else {
@@ -31239,14 +32330,14 @@ ${filelessTransitionGuide(target, o.stage)}`
31239
32330
  const res = await setDeployCoords(slug, body, registryClientDeps(cfg));
31240
32331
  return reportWrite("org project set-deploy", res);
31241
32332
  });
31242
- var registry = program2.command("registry").description("the DDB org registry \u2014 org-level constants");
32333
+ var registry = program2.command("registry").description("the DDB org registry ? org-level constants");
31243
32334
  registry.command("org").description("the org config (account id, region, orgProjectId, sagaApiUrl)").option("--json", "machine-readable output").action(async (_o) => {
31244
32335
  const cfg = await loadConfig();
31245
32336
  const org = await fetchOrgConfig(registryClientDeps(cfg));
31246
32337
  if (!org) return failGraceful("org config get: Hub API unreachable, unseeded, or this repo is not bootstrapped");
31247
32338
  console.log(JSON.stringify(org));
31248
32339
  });
31249
- var oauth = program2.command("oauth").description("per-repo Google OAuth \u2014 plan the canonical URI set, verify the client is port-agnostic");
32340
+ var oauth = program2.command("oauth").description("per-repo Google OAuth ? plan the canonical URI set, verify the client is port-agnostic");
31250
32341
  oauth.command("plan", { isDefault: true }).description("print the canonical JS origins + redirect URIs + SSM cred param names for this repo").option("--repo <owner/repo>", "slug source (defaults to the current repo)").option("--json", "machine-readable output").action(async (o) => {
31251
32342
  const cfg = await loadConfig();
31252
32343
  const slug = (o.repo ? o.repo.split("/").pop() : cfg.project ?? await repoSlug()).toLowerCase();
@@ -31260,7 +32351,7 @@ oauth.command("plan", { isDefault: true }).description("print the canonical JS o
31260
32351
  return failGraceful(
31261
32352
  `org oauth plan: ${message}. Declare it in the registry META first:
31262
32353
  mmi-cli org project set ${o.repo ?? `mutmutco/${slug}`} --var 'oauth={"subdomains":["${defaultSubdomain(slug)}"],"callbackPath":"${DEFAULT_CALLBACK_PATH}"}'
31263
- New repo? The GCP project and its Google Auth Platform consent screen must exist too \u2014 docs/Guides/oauth-provision.md \xA7 New repo.`
32354
+ New repo? The GCP project and its Google Auth Platform consent screen must exist too ? docs/Guides/oauth-provision.md ? New repo.`
31264
32355
  );
31265
32356
  }
31266
32357
  return failGraceful(`org oauth plan: ${message}`);
@@ -31286,7 +32377,7 @@ SSM cred params (under /mmi-future/${slug}/):`);
31286
32377
  oauth.command("set-creds").description('store the OAuth client into the canonical stageless GOOGLE_CLIENT_ID/SECRET pair (pipe the Console "Download JSON" on stdin)').option("--repo <owner/repo>", "target repo (defaults to the current repo)").action(async (o) => {
31287
32378
  const raw = await readStdin();
31288
32379
  if (!raw.trim()) {
31289
- return fail("org oauth set-creds: pipe the Google client JSON on stdin \u2014 e.g.\n mmi-cli org oauth set-creds --repo <owner/repo> < client.json");
32380
+ return fail("org oauth set-creds: pipe the Google client JSON on stdin ? e.g.\n mmi-cli org oauth set-creds --repo <owner/repo> < client.json");
31290
32381
  }
31291
32382
  let creds;
31292
32383
  try {
@@ -31336,9 +32427,9 @@ oauth.command("verify").description("probe Google authorize with an arbitrary po
31336
32427
  if (o.json) {
31337
32428
  console.log(JSON.stringify({ slug, redirectUri, portAgnostic: !mismatch }));
31338
32429
  } else if (mismatch) {
31339
- console.error(`FAIL ${slug}: redirect_uri_mismatch for ${redirectUri} \u2014 client is not port-agnostic (run /oauth-provision)`);
32430
+ console.error(`FAIL ${slug}: redirect_uri_mismatch for ${redirectUri} ? client is not port-agnostic (run /oauth-provision)`);
31340
32431
  } else {
31341
- console.log(`PASS ${slug}: ${redirectUri} accepted \u2014 port-agnostic OAuth is live`);
32432
+ console.log(`PASS ${slug}: ${redirectUri} accepted ? port-agnostic OAuth is live`);
31342
32433
  }
31343
32434
  if (mismatch) process.exitCode = 1;
31344
32435
  });
@@ -31351,7 +32442,7 @@ function resolveCreatePriority(raw, command) {
31351
32442
  try {
31352
32443
  return normalizePriority(raw);
31353
32444
  } catch {
31354
- fail(`${command}: unknown priority "${raw}" \u2014 expected one of: ${CLI_PRIORITIES.join(", ")}`, {
32445
+ fail(`${command}: unknown priority "${raw}" ? expected one of: ${CLI_PRIORITIES.join(", ")}`, {
31355
32446
  code: ERROR_CODES.ERR_BAD_ENUM,
31356
32447
  offending_flag: "--priority",
31357
32448
  expected: [...CLI_PRIORITIES]
@@ -31361,15 +32452,15 @@ function resolveCreatePriority(raw, command) {
31361
32452
  function resolveCreateType(raw, command, labels) {
31362
32453
  if (raw === void 0 || raw === "") {
31363
32454
  const nearMiss = labels?.find((l) => ISSUE_TYPES.includes(l));
31364
- const hint = nearMiss ? ` (you passed --label ${nearMiss} \u2014 did you mean --type ${nearMiss}?)` : "";
32455
+ const hint = nearMiss ? ` (you passed --label ${nearMiss} ? did you mean --type ${nearMiss}?)` : "";
31365
32456
  fail(`${command}: --type is required (bug | feature | task) unless --batch supplies it per row${hint}`, {
31366
32457
  code: ERROR_CODES.ERR_MISSING_FLAG,
31367
32458
  offending_flag: "--type",
31368
- ...nearMiss ? { corrected_command: `${command} --type ${nearMiss} \u2026` } : {}
32459
+ ...nearMiss ? { corrected_command: `${command} --type ${nearMiss} ?` } : {}
31369
32460
  });
31370
32461
  }
31371
32462
  if (!ISSUE_TYPES.includes(raw)) {
31372
- fail(`${command}: unknown type "${raw}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`, {
32463
+ fail(`${command}: unknown type "${raw}" ? expected one of: ${ISSUE_TYPES.join(", ")}`, {
31373
32464
  code: ERROR_CODES.ERR_BAD_ENUM,
31374
32465
  offending_flag: "--type",
31375
32466
  expected: [...ISSUE_TYPES]
@@ -31383,9 +32474,9 @@ function resolveCreateSurface(opts) {
31383
32474
  function surfaceWaived() {
31384
32475
  return rawFlag("--no-surface");
31385
32476
  }
31386
- var issue = program2.command("issue").description("issues \u2014 reliable create with structured output");
32477
+ var issue = program2.command("issue").description("issues ? reliable create with structured output");
31387
32478
  withExamples(mutating(
31388
- issue.command("create").description("create an issue (type \u2192 label) and print {number,url,label} JSON").addOption(new Option("--type <type>", "bug | feature | task (sets the matching label; required unless --batch)").choices([...ISSUE_TYPES])).option("--title <title>", "issue title").option("--title-file <path|->", "read the issue title from a UTF-8 file, or from stdin with -").option("--body <body>", "issue body (markdown)").option("--body-file <path|->", "read issue body from a UTF-8 file. `-` (stdin) needs a heredoc, which the agent inline-body guard denies (#1473/#2125) \u2014 prefer a real path; a title with backticks needs --title-file for the same reason (#3381)").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only \u2014 never a priority:* label, #416)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--label <label...>", "extra label(s) to attach (repeatable; auto-created if missing)").option("--surface <surface>", "issue surface, with or without the surface: prefix (#3789). Required when the target repo runs the one-surface-label board rule; any value satisfies it, so this is not a closed enum").option("--no-surface", "file without a surface label on a repo that requires one \u2014 for a genuinely exempt filing (e.g. a coop proof issue that spans every surface)").option("--parent <ref>", "file as a native sub-issue of this parent (#123, owner/repo#123, or URL)").option("--no-related", "skip the auto related-issues comment"),
32479
+ issue.command("create").description("create an issue (type ? label) and print {number,url,label} JSON").addOption(new Option("--type <type>", "bug | feature | task (sets the matching label; required unless --batch)").choices([...ISSUE_TYPES])).option("--title <title>", "issue title").option("--title-file <path|->", "read the issue title from a UTF-8 file, or from stdin with -").option("--body <body>", "issue body (markdown)").option("--body-file <path|->", "read issue body from a UTF-8 file. `-` (stdin) needs a heredoc, which the agent inline-body guard denies (#1473/#2125) ? prefer a real path; a title with backticks needs --title-file for the same reason (#3381)").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only ? never a priority:* label, #416)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--label <label...>", "extra label(s) to attach (repeatable; auto-created if missing)").option("--surface <surface>", "issue surface, with or without the surface: prefix (#3789). Required when the target repo runs the one-surface-label board rule; any value satisfies it, so this is not a closed enum").option("--no-surface", "file without a surface label on a repo that requires one ? for a genuinely exempt filing (e.g. a coop proof issue that spans every surface)").option("--parent <ref>", "file as a native sub-issue of this parent (#123, owner/repo#123, or URL)").option("--no-related", "skip the auto related-issues comment"),
31389
32480
  // --dry-run/--validate-only plan: resolve the same title source and validate the same type, priority,
31390
32481
  // and surface contract as the real action. A plan that echoes the title-file PATH instead of its value
31391
32482
  // is not a plan of the mutation that will run (#3914).
@@ -31452,7 +32543,7 @@ withExamples(mutating(
31452
32543
  }
31453
32544
  targetRepo2 = await resolveRepo(o.repo);
31454
32545
  if (!targetRepo2) {
31455
- return fail("issue create: could not resolve the target repo \u2014 run inside a git checkout or pass --repo <owner/repo>");
32546
+ return fail("issue create: could not resolve the target repo ? run inside a git checkout or pass --repo <owner/repo>");
31456
32547
  }
31457
32548
  args = buildIssueArgs({
31458
32549
  type: issueType,
@@ -31481,7 +32572,7 @@ withExamples(mutating(
31481
32572
  labels: extraLabels.length ? extraLabels : void 0
31482
32573
  });
31483
32574
  process.stderr.write(
31484
- `warning: --surface ${surfaceFlagLabel} was dropped \u2014 ${targetRepo2} defines no surface:* labels, and creating one here would switch the one-surface-label rule on for every later filing in it. Use --label ${surfaceFlagLabel} if you really mean to start that taxonomy.
32575
+ `warning: --surface ${surfaceFlagLabel} was dropped ? ${targetRepo2} defines no surface:* labels, and creating one here would switch the one-surface-label rule on for every later filing in it. Use --label ${surfaceFlagLabel} if you really mean to start that taxonomy.
31485
32576
  `
31486
32577
  );
31487
32578
  }
@@ -31530,7 +32621,7 @@ async function readParentField(number, repo) {
31530
32621
  }
31531
32622
  return resolveParentField(payload);
31532
32623
  }
31533
- issue.command("view <number>").description('read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347). --comments folds in every comment; --context also adds linkedPrs + children (the one-shot "load the whole item" read, #2894)').option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "number,title,body,url"').option("--comments", 'include every comment (body + comments in one call) \u2014 the agentic "read the whole item before working it" read (#2894)').option("--context", "full working context in one call: implies --comments and also adds linkedPrs and, for an epic, a children summary (#2894)").action(async (number, o) => {
32624
+ issue.command("view <number>").description('read an issue as structured JSON ? the mmi-cli path for non-board issue reads (#2347). --comments folds in every comment; --context also adds linkedPrs + children (the one-shot "load the whole item" read, #2894)').option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags ? in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "number,title,body,url"').option("--comments", 'include every comment (body + comments in one call) ? the agentic "read the whole item before working it" read (#2894)').option("--context", "full working context in one call: implies --comments and also adds linkedPrs and, for an epic, a children summary (#2894)").action(async (number, o) => {
31534
32625
  const n = Number(number);
31535
32626
  if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
31536
32627
  const repo = await resolveRepo(o.repo);
@@ -31624,7 +32715,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
31624
32715
  return fail(`issue comment: ${e.message}`);
31625
32716
  }
31626
32717
  const repo = await resolveRepo(parsed.repo ?? o.repo);
31627
- if (!repo) return fail("issue comment: could not resolve repo \u2014 pass --repo owner/repo");
32718
+ if (!repo) return fail("issue comment: could not resolve repo ? pass --repo owner/repo");
31628
32719
  try {
31629
32720
  const result = await postIssueComment(defaultGitHubClient(), { ref, defaultRepo: repo, body });
31630
32721
  console.log(JSON.stringify(result));
@@ -31633,7 +32724,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
31633
32724
  return failGraceful(`issue comment: ${(err.stderr || err.message || String(e)).trim()}`);
31634
32725
  }
31635
32726
  });
31636
- jsonParity(issue.command("check <ref>").description("tick (or with --off untick) a task-list checkbox in an issue/epic body by its item text and print {number,repo,item,checked,changed} JSON").requiredOption("--item <text>", "the checklist item to match \u2014 exact item text, else a unique substring").option("--off", "untick the item ([x] \u2192 [ ]) instead of ticking it").option("--repo <owner/repo>", "repo for a bare ref (defaults to the current repo)")).action(async (ref, o) => {
32727
+ jsonParity(issue.command("check <ref>").description("tick (or with --off untick) a task-list checkbox in an issue/epic body by its item text and print {number,repo,item,checked,changed} JSON").requiredOption("--item <text>", "the checklist item to match ? exact item text, else a unique substring").option("--off", "untick the item ([x] ? [ ]) instead of ticking it").option("--repo <owner/repo>", "repo for a bare ref (defaults to the current repo)")).action(async (ref, o) => {
31637
32728
  let parsed;
31638
32729
  try {
31639
32730
  parsed = parseIssueRef(ref);
@@ -31641,7 +32732,7 @@ jsonParity(issue.command("check <ref>").description("tick (or with --off untick)
31641
32732
  return fail(`issue check: ${e.message}`);
31642
32733
  }
31643
32734
  const repo = await resolveRepo(parsed.repo ?? o.repo);
31644
- if (!repo) return fail("issue check: could not resolve repo \u2014 pass --repo owner/repo");
32735
+ if (!repo) return fail("issue check: could not resolve repo ? pass --repo owner/repo");
31645
32736
  const checked = o.off !== true;
31646
32737
  let body;
31647
32738
  try {
@@ -31654,7 +32745,7 @@ jsonParity(issue.command("check <ref>").description("tick (or with --off untick)
31654
32745
  if (!result.ok) {
31655
32746
  if (result.reason === "ambiguous") {
31656
32747
  const list = result.matches.map((m) => ` - ${m.text}`).join("\n");
31657
- return fail(`issue check: "${o.item}" matches ${result.matches.length} checklist items in ${repo}#${parsed.number} \u2014 narrow the text:
32748
+ return fail(`issue check: "${o.item}" matches ${result.matches.length} checklist items in ${repo}#${parsed.number} ? narrow the text:
31658
32749
  ${list}`);
31659
32750
  }
31660
32751
  return fail(`issue check: no checklist item matching "${o.item}" in ${repo}#${parsed.number}`);
@@ -31673,7 +32764,7 @@ ${list}`);
31673
32764
  }
31674
32765
  console.log(JSON.stringify({ number: parsed.number, repo, item: result.item.text, checked, changed: true }));
31675
32766
  });
31676
- program2.command("report").description("file a friction report on the Hub board (Hub session auth, dedups open reports) and print {number,url} JSON").option("--title <title>", "one-line friction summary").option("--title-file <path|->", "read the friction summary from a UTF-8 file, or from stdin with -").option("--body <body>", "report body (markdown)").option("--body-file <path|->", "read report body from a UTF-8 file, or from stdin with -").addOption(new Option("--type <type>", "bug | feature | task (sets the matching label)").default("task").choices([...ISSUE_TYPES])).option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", 'attribute the report to a different source repo than the current checkout for the "Filed via..." footer (rare \u2014 usually auto-detected; every report always lands on the org Hub, never an alternate target, #263)').option("--force", "file a new issue even when an open report looks like a duplicate").option("--json", "machine-readable output (already the default \u2014 report always prints JSON; #682)").action(async (o) => {
32767
+ program2.command("report").description("file a friction report on the Hub board (Hub session auth, dedups open reports) and print {number,url} JSON").option("--title <title>", "one-line friction summary").option("--title-file <path|->", "read the friction summary from a UTF-8 file, or from stdin with -").option("--body <body>", "report body (markdown)").option("--body-file <path|->", "read report body from a UTF-8 file, or from stdin with -").addOption(new Option("--type <type>", "bug | feature | task (sets the matching label)").default("task").choices([...ISSUE_TYPES])).option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", 'attribute the report to a different source repo than the current checkout for the "Filed via..." footer (rare ? usually auto-detected; every report always lands on the org Hub, never an alternate target, #263)').option("--force", "file a new issue even when an open report looks like a duplicate").option("--json", "machine-readable output (already the default ? report always prints JSON; #682)").action(async (o) => {
31677
32768
  let body;
31678
32769
  let priority;
31679
32770
  let title;
@@ -31683,7 +32774,7 @@ program2.command("report").description("file a friction report on the Hub board
31683
32774
  body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
31684
32775
  priority = resolveCreatePriority(o.priority, "report");
31685
32776
  if (!ISSUE_TYPES.includes(o.type)) {
31686
- throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
32777
+ throw new Error(`unknown issue type "${o.type}" ? expected one of: ${ISSUE_TYPES.join(", ")}`);
31687
32778
  }
31688
32779
  } catch (e) {
31689
32780
  const m = e.message;
@@ -31720,7 +32811,7 @@ async function resolvePluginSha() {
31720
32811
  return void 0;
31721
32812
  }
31722
32813
  }
31723
- program2.command("skill-lesson").description("file a skill-lesson on the Hub board (GitHub auth, dedups open lessons) and print {number,url} JSON").addOption(new Option("--skill <name>", `which skill misfired (${SKILL_NAMES.join(" | ")})`).makeOptionMandatory().choices([...SKILL_NAMES])).option("--title <title>", "one-line summary of what misfired").option("--title-file <path|->", "read the one-line summary from a UTF-8 file, or from stdin with -").option("--body <body>", "lesson body: what misfired, the evidence, and the proposed amendment (markdown)").option("--body-file <path|->", "read the lesson body from a UTF-8 file, or from stdin with -").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", `target repo (defaults to the org Hub: ${HUB_REPO3})`).option("--force", "file a new issue even when an open lesson looks like a duplicate").option("--json", "machine-readable output (already the default \u2014 skill-lesson always prints JSON)").action(async (o) => {
32814
+ program2.command("skill-lesson").description("file a skill-lesson on the Hub board (GitHub auth, dedups open lessons) and print {number,url} JSON").addOption(new Option("--skill <name>", `which skill misfired (${SKILL_NAMES.join(" | ")})`).makeOptionMandatory().choices([...SKILL_NAMES])).option("--title <title>", "one-line summary of what misfired").option("--title-file <path|->", "read the one-line summary from a UTF-8 file, or from stdin with -").option("--body <body>", "lesson body: what misfired, the evidence, and the proposed amendment (markdown)").option("--body-file <path|->", "read the lesson body from a UTF-8 file, or from stdin with -").option("--priority <priority>", "urgent | high | medium | low (defaults to medium; sets the board Priority field only, #416)").option("--repo <owner/repo>", `target repo (defaults to the org Hub: ${HUB_REPO3})`).option("--force", "file a new issue even when an open lesson looks like a duplicate").option("--json", "machine-readable output (already the default ? skill-lesson always prints JSON)").action(async (o) => {
31724
32815
  const targetRepo2 = o.repo ?? HUB_REPO3;
31725
32816
  const sourceRepo = await resolveRepo(void 0);
31726
32817
  const pluginSha = await resolvePluginSha();
@@ -31775,7 +32866,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
31775
32866
  repo: targetRepo2,
31776
32867
  labels: [SKILL_LESSON_LABEL],
31777
32868
  command: "skill-lesson",
31778
- waiver: { reason: "tooling lesson spans product surfaces \u2014 coop-proof class (#3789)" }
32869
+ waiver: { reason: "tooling lesson spans product surfaces ? coop-proof class (#3789)" }
31779
32870
  });
31780
32871
  if (surfaceWarn) process.stderr.write(`${surfaceWarn}
31781
32872
  `);
@@ -31787,7 +32878,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
31787
32878
  const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
31788
32879
  console.log(JSON.stringify({ ...created, deduped: false, label: SKILL_LESSON_LABEL, skill, priority, projectItemId, onBoard }));
31789
32880
  });
31790
- var pr = program2.command("pr").description("pull requests \u2014 reliable create with structured output");
32881
+ var pr = program2.command("pr").description("pull requests ? reliable create with structured output");
31791
32882
  withExamples(pr.command("create").description("create a PR and print {number,url} JSON").option("--title <title>", "PR title").option("--title-file <path|->", "read the PR title from a UTF-8 file, or from stdin with -").option("--body <body>", "PR body (markdown)").option("--body-file <path|->", "read PR body from a UTF-8 file, or from stdin with -").option("--base <branch>", "base branch (defaults to the repo default)").option("--head <branch>", "head branch (defaults to the current branch)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--draft", "open the PR in draft state (#2667)").option("--json", "machine-readable output (default; accepted for parity)").action(async (o) => {
31792
32883
  let body;
31793
32884
  let title;
@@ -31800,7 +32891,7 @@ withExamples(pr.command("create").description("create a PR and print {number,url
31800
32891
  body = normalizeClosingDirectives(body);
31801
32892
  const docsCheck = await checkDocsIndexAtHead({ repo: o.repo, head: o.head }, createPrCreateDocsIndexDeps());
31802
32893
  if (docsCheck && !docsCheck.ok) {
31803
- return fail(`pr create: ${docsCheck.detail} \u2014 ${docsCheck.fix}`);
32894
+ return fail(`pr create: ${docsCheck.detail} ? ${docsCheck.fix}`);
31804
32895
  }
31805
32896
  const created = await ghCreate(buildPrArgs({ title, body, base: o.base, head: o.head, repo: o.repo, draft: o.draft }));
31806
32897
  console.log(JSON.stringify(created));
@@ -31811,7 +32902,7 @@ withExamples(pr.command("create").description("create a PR and print {number,url
31811
32902
  "--head and --base default to the current branch and the repo default; only pass them to override.",
31812
32903
  "Use --body-file for multiline PR bodies instead of shell-escaped inline markdown."
31813
32904
  ]);
31814
- pr.command("view <number>").description("read a PR as structured JSON (merged state, head/base, URL, merge commit) \u2014 the mmi-cli read path (#2347). --comments folds in every comment; --context also adds linkedIssues (the issues it closes/references, #2894)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags \u2014 in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "state,baseRefName,mergeCommit"').option("--comments", "include every comment (body + comments in one call) \u2014 read the whole PR before landing it (#2894)").option("--context", "full working context in one call: implies --comments and also adds linkedIssues (the issues the PR closes/references) (#2894)").action(async (number, o) => {
32905
+ pr.command("view <number>").description("read a PR as structured JSON (merged state, head/base, URL, merge commit) ? the mmi-cli read path (#2347). --comments folds in every comment; --context also adds linkedIssues (the issues it closes/references, #2894)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags ? in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "state,baseRefName,mergeCommit"').option("--comments", "include every comment (body + comments in one call) ? read the whole PR before landing it (#2894)").option("--context", "full working context in one call: implies --comments and also adds linkedIssues (the issues the PR closes/references) (#2894)").action(async (number, o) => {
31815
32906
  const n = Number(number);
31816
32907
  if (!Number.isInteger(n) || n <= 0) return fail("pr view: <number> must be a positive integer");
31817
32908
  const repo = await resolveRepo(o.repo);
@@ -31832,11 +32923,11 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
31832
32923
  }
31833
32924
  });
31834
32925
  async function listCiWorkflowPaths(cwd = process.cwd()) {
31835
- const wfDir = (0, import_node_path35.join)(cwd, ".github", "workflows");
31836
- if (!(0, import_node_fs36.existsSync)(wfDir)) return [];
31837
- return (0, import_node_fs36.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
32926
+ const wfDir = (0, import_node_path38.join)(cwd, ".github", "workflows");
32927
+ if (!(0, import_node_fs39.existsSync)(wfDir)) return [];
32928
+ return (0, import_node_fs39.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
31838
32929
  try {
31839
- return workflowReportsPrChecks((0, import_node_fs36.readFileSync)((0, import_node_path35.join)(wfDir, name), "utf8"));
32930
+ return workflowReportsPrChecks((0, import_node_fs39.readFileSync)((0, import_node_path38.join)(wfDir, name), "utf8"));
31840
32931
  } catch {
31841
32932
  return true;
31842
32933
  }
@@ -31868,16 +32959,16 @@ function ciAuditDeps() {
31868
32959
  // gate re-seed step is skipped gracefully rather than failing mid-run.
31869
32960
  readSeedFile: (path2) => {
31870
32961
  if (!root) return null;
31871
- const fullPath = (0, import_node_path35.join)(root, path2);
31872
- return (0, import_node_fs36.existsSync)(fullPath) ? (0, import_node_fs36.readFileSync)(fullPath, "utf8") : null;
32962
+ const fullPath = (0, import_node_path38.join)(root, path2);
32963
+ return (0, import_node_fs39.existsSync)(fullPath) ? (0, import_node_fs39.readFileSync)(fullPath, "utf8") : null;
31873
32964
  }
31874
32965
  };
31875
32966
  }
31876
32967
  function hubRoot() {
31877
- const fromPkg = (0, import_node_path35.join)(__dirname, "..", "..");
32968
+ const fromPkg = (0, import_node_path38.join)(__dirname, "..", "..");
31878
32969
  const marker = "skills/bootstrap/seeds/manifest.json";
31879
- if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(fromPkg, marker))) return fromPkg;
31880
- if ((0, import_node_fs36.existsSync)((0, import_node_path35.join)(process.cwd(), marker))) return process.cwd();
32970
+ if ((0, import_node_fs39.existsSync)((0, import_node_path38.join)(fromPkg, marker))) return fromPkg;
32971
+ if ((0, import_node_fs39.existsSync)((0, import_node_path38.join)(process.cwd(), marker))) return process.cwd();
31881
32972
  return null;
31882
32973
  }
31883
32974
  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) => {
@@ -31885,7 +32976,7 @@ pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs
31885
32976
  if (o.json) return printLine(JSON.stringify(result));
31886
32977
  printLine(`merge CI policy: ${result.policy} (${result.reason})`);
31887
32978
  });
31888
- pr.command("checks-wait <number>").description(`bounded wait for PR checks; skips immediately on no-ci repos (#1432), fails immediately on a CONFLICTING PR \u2014 GitHub never queues checks for one (#2970). REST-only polling with a pool floor (#3024). Default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m; --timeout raises it. Exit 1 = a check FAILED or the PR is CONFLICTING, exit ${PR_CHECKS_TIMEOUT_EXIT_CODE} = the wait window expired or the API pool ran dry (re-arm)`).option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--timeout <minutes>", `wait budget in minutes (default ${PR_CHECKS_TIMEOUT_MS / 6e4}) \u2014 raise it for serial self-hosted e2e queues`).action(async (number, o) => {
32979
+ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skips immediately on no-ci repos (#1432), fails immediately on a CONFLICTING PR ? GitHub never queues checks for one (#2970). REST-only polling with a pool floor (#3024). Default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m; --timeout raises it. Exit 1 = a check FAILED or the PR is CONFLICTING, exit ${PR_CHECKS_TIMEOUT_EXIT_CODE} = the wait window expired or the API pool ran dry (re-arm)`).option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--timeout <minutes>", `wait budget in minutes (default ${PR_CHECKS_TIMEOUT_MS / 6e4}) ? raise it for serial self-hosted e2e queues`).action(async (number, o) => {
31889
32980
  let timeoutMs;
31890
32981
  if (o.timeout !== void 0) {
31891
32982
  const minutes = Number(o.timeout);
@@ -31910,24 +33001,27 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
31910
33001
  timeoutMs,
31911
33002
  // Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
31912
33003
  // an agent harness kills it on its own (shorter) deadline before the verdict ever prints (#2940).
31913
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr checks-wait: ${state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
33004
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr checks-wait: ${state} ? ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
31914
33005
  });
31915
33006
  if (o.json) printLine(JSON.stringify(result));
31916
33007
  else if (result.status === "conflicting") {
31917
- printLine(`pr checks-wait: conflicting \u2014 ${result.reason}`);
33008
+ printLine(`pr checks-wait: conflicting ? ${result.reason}`);
31918
33009
  } else if (result.status === "timeout") {
31919
- printLine(`pr checks-wait: timeout \u2014 waited ${Math.round((result.waitedMs ?? 0) / 6e4)}m, last state: ${result.detail ?? "pending"}. No check failed; re-run to keep waiting, or pass --timeout <minutes>.`);
33010
+ const stuckQueuing = /no-checks-reported/i.test(result.detail ?? "");
33011
+ printLine(
33012
+ `pr checks-wait: timeout ? waited ${Math.round((result.waitedMs ?? 0) / 6e4)}m, last state: ${result.detail ?? "pending"}. No check failed; re-run to keep waiting, or pass --timeout <minutes>.` + (stuckQueuing ? " Tip: jobs never appeared ? mmi-live may be saturated or Actions may be failing before checkout; check runner health (docs/Guides/gh-runner-runbook.md) and re-run the workflow." : "")
33013
+ );
31920
33014
  } else if (result.status === "rate-limited") {
31921
- printLine(`pr checks-wait: rate-limited \u2014 ${result.reason ?? "REST pool below floor"}. No check failed; re-run after the pool resets.`);
33015
+ printLine(`pr checks-wait: rate-limited ? ${result.reason ?? "REST pool below floor"}. No check failed; re-run after the pool resets.`);
31922
33016
  } else if (result.detail === "runner-infra") {
31923
- printLine(`pr checks-wait: failure (runner infrastructure, NOT a test failure) \u2014 ${result.reason}`);
33017
+ printLine(`pr checks-wait: failure (runner infrastructure, NOT a test failure) ? ${result.reason}`);
31924
33018
  } else if (result.detail === "stale-head") {
31925
- printLine(`pr checks-wait: failure (stale PR head, NOT a test failure) \u2014 ${result.reason}`);
31926
- } else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
33019
+ printLine(`pr checks-wait: failure (stale PR head, NOT a test failure) ? ${result.reason}`);
33020
+ } else printLine(`pr checks-wait: ${result.status}${result.reason ? ` ? ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
31927
33021
  if (result.status === "failure" || result.status === "conflicting") process.exitCode = 1;
31928
33022
  if (result.status === "timeout" || result.status === "rate-limited") process.exitCode = PR_CHECKS_TIMEOUT_EXIT_CODE;
31929
33023
  });
31930
- pr.command("land <number>").description("agent merge path (#1440): train probe \u2192 checks-wait \u2192 merge --auto \u2192 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--preserve-worktree", "after merge, keep the local PR worktree/stage/branch for an active batch (#1888)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword refusal (#3718)").action(async (number, o) => {
33024
+ pr.command("land <number>").description("agent merge path (#1440): train probe ? checks-wait ? merge --auto ? poll enqueued ? development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--preserve-worktree", "after merge, keep the local PR worktree/stage/branch for an active batch (#1888)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword refusal (#3718)").action(async (number, o) => {
31931
33025
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
31932
33026
  const startingPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
31933
33027
  assertPrMergeHousekeepingClean(startingPath || process.cwd(), "pr land", { force: o.force });
@@ -31953,8 +33047,8 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
31953
33047
  } catch {
31954
33048
  }
31955
33049
  if (isPromotionBase(base, track)) {
31956
- const shape = track ? `${track} track` : "unresolved track \u2014 strict reading";
31957
- throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) \u2014 promotion merges stay human-only`);
33050
+ const shape = track ? `${track} track` : "unresolved track ? strict reading";
33051
+ throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) ? promotion merges stay human-only`);
31958
33052
  }
31959
33053
  return repo;
31960
33054
  },
@@ -31962,23 +33056,23 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
31962
33056
  resolveCiPolicy: (repo) => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
31963
33057
  waitForChecks: (prNumber, repo) => waitForPrChecks({
31964
33058
  resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
31965
- // #3024: REST-only wait loop — runPrLand's resolveRepo already guarantees `repo` is set.
33059
+ // #3024: REST-only wait loop ? runPrLand's resolveRepo already guarantees `repo` is set.
31966
33060
  pollChecks: () => pollRestPrChecks(prNumber, repo),
31967
33061
  // #2970: `pr land` only ever lands PRs based on development (resolveRepo above already rejects any
31968
33062
  // other base), so the fast-fail message's base branch is always 'development' here.
31969
33063
  pollMergeable: () => pollRestPrMergeable(prNumber, repo),
31970
33064
  pollRateLimit: () => fetchRestCorePool(),
31971
- // #3388: `pr land` is the batch path — the one most likely to self-DOS the shared runner and
33065
+ // #3388: `pr land` is the batch path ? the one most likely to self-DOS the shared runner and
31972
33066
  // then read its own wall-clock kill as a broken diff.
31973
33067
  diagnoseFailure: () => diagnoseFailedRestChecks(prNumber, repo),
31974
33068
  baseBranch: "development",
31975
33069
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
31976
33070
  log: (message) => console.warn(message),
31977
- // `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
33071
+ // `pr land` inherits the same (raised) checks budget, so it needs the same liveness ? otherwise the
31978
33072
  // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
31979
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr land: waiting on checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
33073
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr land: waiting on checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
31980
33074
  }),
31981
- // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying — a
33075
+ // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying ? a
31982
33076
  // fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
31983
33077
  probeMergeReady: async (prNumber, repo) => {
31984
33078
  const [snapshot, checks] = await Promise.all([
@@ -32008,7 +33102,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
32008
33102
  });
32009
33103
  if (result.status !== "failed") {
32010
33104
  const repoFlag = result.repo ? ` --repo ${result.repo}` : "";
32011
- console.warn(`pr land: merge confirmed; cleanup pending \u2014 if this process is interrupted, resume with: mmi-cli pr merge ${number}${repoFlag} --squash`);
33105
+ console.warn(`pr land: merge confirmed; cleanup pending ? if this process is interrupted, resume with: mmi-cli pr merge ${number}${repoFlag} --squash`);
32012
33106
  }
32013
33107
  if (result.status !== "failed") {
32014
33108
  try {
@@ -32037,13 +33131,13 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
32037
33131
  }
32038
33132
  if (o.json) printLine(JSON.stringify(result));
32039
33133
  else {
32040
- printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
33134
+ printLine(`pr land: ${result.status}${result.error ? ` ? ${result.error}` : ""}`);
32041
33135
  for (const line of renderPrLandCleanupLines(result.cleanup)) printLine(line);
32042
33136
  if (result.cleanupError) printLine(`pr land cleanup: ${result.cleanupError}`);
32043
33137
  }
32044
33138
  if (result.status === "failed" || result.cleanupError) process.exitCode = 1;
32045
33139
  });
32046
- jsonParity(pr.command("merge <number>").description("merge a PR (squash by default) and clean up its branch + worktree \u2014 no leftover local branch; on no-ci repos run pr ci-policy / checks-wait first (#1432)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--auto", "enable auto-merge \u2014 merge once the base-branch policy is satisfied (use for policy-gated repos)").option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m)`).option("--preserve-worktree", "keep the local PR worktree/stage/branch for an active multi-issue batch (#1888)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword refusal (#3718)")).action(async (number, o) => {
33140
+ jsonParity(pr.command("merge <number>").description("merge a PR (squash by default) and clean up its branch + worktree ? no leftover local branch; on no-ci repos run pr ci-policy / checks-wait first (#1432)").option("--squash", "squash merge (default)").option("--merge", "create a merge commit").option("--rebase", "rebase merge").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--auto", "enable auto-merge ? merge once the base-branch policy is satisfied (use for policy-gated repos)").option("--wait", `wait for checks to reach a terminal passing verdict before merging (default budget ${PR_CHECKS_TIMEOUT_MS / 6e4}m)`).option("--preserve-worktree", "keep the local PR worktree/stage/branch for an active multi-issue batch (#1888)").option("--force", "acknowledge and merge past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword refusal (#3718)")).action(async (number, o) => {
32047
33141
  const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
32048
33142
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
32049
33143
  const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
@@ -32076,10 +33170,10 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
32076
33170
  baseBranch,
32077
33171
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
32078
33172
  log: (message) => console.warn(message),
32079
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
33173
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
32080
33174
  });
32081
33175
  if (wait.status !== "success" && wait.status !== "skipped") {
32082
- console.warn(`pr merge: --wait stopped before merge \u2014 ${wait.status}${wait.reason ? `: ${wait.reason}` : ""}${wait.detail ? ` (${wait.detail})` : ""}`);
33176
+ console.warn(`pr merge: --wait stopped before merge ? ${wait.status}${wait.reason ? `: ${wait.reason}` : ""}${wait.detail ? ` (${wait.detail})` : ""}`);
32083
33177
  process.exitCode = wait.status === "timeout" || wait.status === "rate-limited" ? PR_CHECKS_TIMEOUT_EXIT_CODE : 1;
32084
33178
  return;
32085
33179
  }
@@ -32118,7 +33212,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
32118
33212
  return;
32119
33213
  }
32120
33214
  if (!o.auto && basePolicyBlocksImmediateMerge(message)) {
32121
- console.warn(`pr merge: the base-branch policy blocks an immediate merge \u2014 upgrading to --auto (merges once required checks pass).`);
33215
+ console.warn(`pr merge: the base-branch policy blocks an immediate merge ? upgrading to --auto (merges once required checks pass).`);
32122
33216
  upgradedToAuto = true;
32123
33217
  await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: true, deleteBranch: !headIsProtected, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
32124
33218
  const m2 = String(e2.message || "");
@@ -32149,7 +33243,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
32149
33243
  const state = stateRead.ok ? stateRead.state : "";
32150
33244
  const enqueued = describePrMergeEnqueuedReason(await pollGhPrChecks(number, repoArgs).catch(() => void 0));
32151
33245
  console.log(JSON.stringify({ mergeStatus: "auto-merge-enqueued", enqueuedReason: enqueued.reason, pr: number, branch: headRef, state: state || "unknown", upgradedToAuto: upgradedToAuto || void 0 }));
32152
- console.warn(`pr merge: PR #${number} is ENQUEUED, not merged \u2014 ${enqueued.message}.`);
33246
+ console.warn(`pr merge: PR #${number} is ENQUEUED, not merged ? ${enqueued.message}.`);
32153
33247
  if (upgradedToAuto && !o.auto) {
32154
33248
  console.warn(`pr merge: exiting ${PR_MERGE_ENQUEUED_EXIT_CODE} so a chained command does not treat this as a completed merge.`);
32155
33249
  process.exitCode = PR_MERGE_ENQUEUED_EXIT_CODE;
@@ -32165,7 +33259,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
32165
33259
  state: stateRead.ok ? stateRead.state : "unknown",
32166
33260
  cleanupStatus: "skipped"
32167
33261
  }));
32168
- console.error(`pr merge: ${gate.message}. Nothing was deleted \u2014 re-check the PR and retry.`);
33262
+ console.error(`pr merge: ${gate.message}. Nothing was deleted ? re-check the PR and retry.`);
32169
33263
  process.exitCode = 1;
32170
33264
  return;
32171
33265
  }
@@ -32185,7 +33279,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
32185
33279
  localCleanup = await cleanupPrMergeLocalBranch(headRef, {
32186
33280
  beforeWorktrees,
32187
33281
  startingPath,
32188
- pathExists: (p) => (0, import_node_fs36.existsSync)(p),
33282
+ pathExists: (p) => (0, import_node_fs39.existsSync)(p),
32189
33283
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
32190
33284
  teardownWorktreeStage,
32191
33285
  deferredStore,
@@ -32259,7 +33353,7 @@ function trainApplyDeps() {
32259
33353
  const verdict = await fetchTrainAuthority(repo, registryClientDeps(await loadConfig()));
32260
33354
  return verdict.ok ? { ok: true, role: verdict.authority.role, train: verdict.authority.train } : verdict;
32261
33355
  },
32262
- // Hub-App-authority dispatch of the central tenant deploy (#953) — the Hub fires the
33356
+ // Hub-App-authority dispatch of the central tenant deploy (#953) ? the Hub fires the
32263
33357
  // workflow_dispatch with its App token, so the caller needs no MMI-Hub Actions write.
32264
33358
  dispatchTenantDeploy: async ({ repo, stage, ref }) => {
32265
33359
  const res = await tenantDeploy({ repo, stage, ref }, registryClientDeps(await loadConfig()));
@@ -32268,7 +33362,7 @@ function trainApplyDeps() {
32268
33362
  throw new Error(`tenant deploy dispatch failed: ${detail}`);
32269
33363
  }
32270
33364
  },
32271
- // Hub-App-authority dispatch of the central tenant-control.yml (#1717) — the Hub fires the
33365
+ // Hub-App-authority dispatch of the central tenant-control.yml (#1717) ? the Hub fires the
32272
33366
  // workflow_dispatch with its App token. Never throws for an expected rejection: it returns the dispatch
32273
33367
  // outcome so runTenantControl can map a 5xx (transport-failed, retryable) vs a 4xx (rejected) vs ok.
32274
33368
  dispatchTenantControl: async ({ repo, stage, action, lines }) => {
@@ -32284,7 +33378,7 @@ function trainApplyDeps() {
32284
33378
  return { ok: false, category: body?.category, error: body?.error ?? res.error };
32285
33379
  },
32286
33380
  // Hotfix-coverage guard (#958): runs against the local clone via real git. manifestPaths exempts the
32287
- // release version fold (#976) — a main-only commit touching ONLY the root package manifest is the
33381
+ // release version fold (#976) ? a main-only commit touching ONLY the root package manifest is the
32288
33382
  // fold's version metadata, which the candidate replaces with its own. (The Hub's wider distribution
32289
33383
  // set never reaches this guard: the Hub is direct-track.)
32290
33384
  hotfixCoverage: (input) => checkHotfixCoverage({ ...input, manifestPaths: ["package.json", "package-lock.json"] }),
@@ -32319,13 +33413,13 @@ function renderDeployLine(d) {
32319
33413
  else if (d.runUrl) parts.push(`run ${d.runUrl}`);
32320
33414
  if (d.deployStatus === "success") parts.push("deploy: SUCCEEDED");
32321
33415
  else if (d.deployStatus === "failure") parts.push("deploy: FAILED (promotion stands; retry the deploy, do not re-tag)");
32322
- else if (d.runId != null) parts.push(`deploy: UNVERIFIED \u2014 dispatched, not resolved (watch: gh run watch ${d.runId} --repo mutmutco/MMI-Hub --exit-status)`);
32323
- else if (d.workflowRuns?.length) parts.push("deploy: UNVERIFIED \u2014 the runs above are enumerated, not resolved; watch each to conclusion before calling this release healthy (#3322)");
32324
- else parts.push("deploy: UNVERIFIED \u2014 no run correlated or watched; resolve every workflow run on the release SHA before calling this release healthy (#3322)");
33416
+ else if (d.runId != null) parts.push(`deploy: UNVERIFIED ? dispatched, not resolved (watch: gh run watch ${d.runId} --repo mutmutco/MMI-Hub --exit-status)`);
33417
+ else if (d.workflowRuns?.length) parts.push("deploy: UNVERIFIED ? the runs above are enumerated, not resolved; watch each to conclusion before calling this release healthy (#3322)");
33418
+ else parts.push("deploy: UNVERIFIED ? no run correlated or watched; resolve every workflow run on the release SHA before calling this release healthy (#3322)");
32325
33419
  return parts.join("; ");
32326
33420
  }
32327
33421
  function renderReleaseResume(r) {
32328
- const lines = [`mmi-cli release --resume: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}] \u2014 ${r.note}`];
33422
+ const lines = [`mmi-cli release --resume: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}] ? ${r.note}`];
32329
33423
  for (const step of r.steps) lines.push(` - ${step}`);
32330
33424
  if (r.releaseUrl) lines.push(` release: ${r.releaseUrl}`);
32331
33425
  if (r.announceNote) lines.push(` announce: ${r.announceNote}`);
@@ -32335,23 +33429,23 @@ function renderReleaseResume(r) {
32335
33429
  return lines.join("\n");
32336
33430
  }
32337
33431
  function renderReleaseAbort(r) {
32338
- return `mmi-cli release --abort --apply: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) \u2014 ${r.note}`;
33432
+ return `mmi-cli release --abort --apply: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) ? ${r.note}`;
32339
33433
  }
32340
33434
  function renderReleasePublishRetry(r) {
32341
- return `mmi-cli release --retry-publish: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) \u2014 ${r.note}; ${r.runUrl}`;
33435
+ return `mmi-cli release --retry-publish: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) ? ${r.note}; ${r.runUrl}`;
32342
33436
  }
32343
33437
  function renderRcandResume(r) {
32344
- return `mmi-cli rcand --resume: promoted ${r.repo} \u2192 rc at ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}]; ${renderDeployLine(r)}; ${r.note}`;
33438
+ return `mmi-cli rcand --resume: promoted ${r.repo} ? rc at ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}]; ${renderDeployLine(r)}; ${r.note}`;
32345
33439
  }
32346
33440
  function renderAlignment(label, alignment) {
32347
33441
  if (alignment.status !== "pr-pending") return `${label}: ${alignment.note}`;
32348
33442
  if (alignment.autoMergeEnqueued) {
32349
33443
  return `${label}: alignment PR #${alignment.prNumber ?? "?"} AUTO-MERGE ENQUEUED (merges when checks pass)${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
32350
33444
  }
32351
- return `${label}: ALIGNMENT PR PENDING \u2014 land it with \`mmi-cli pr merge ${alignment.prNumber ?? "<number>"} --auto --merge\`${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
33445
+ return `${label}: ALIGNMENT PR PENDING ? land it with \`mmi-cli pr merge ${alignment.prNumber ?? "<number>"} --auto --merge\`${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
32352
33446
  }
32353
33447
  function renderTrainApply(commandName, r) {
32354
- let base = `mmi-cli ${commandName}: promoted ${r.repo} \u2192 ${r.stage} at ${r.tag} [${r.deployModel}]; ${renderDeployLine(r)}`;
33448
+ let base = `mmi-cli ${commandName}: promoted ${r.repo} ? ${r.stage} at ${r.tag} [${r.deployModel}]; ${renderDeployLine(r)}`;
32355
33449
  if (r.versionFold) base = `${base}; ${r.versionFold}`;
32356
33450
  if (r.resumeNote) base = `${base}; ${r.resumeNote}`;
32357
33451
  if (r.devNote) base = `${base}; ${r.devNote}`;
@@ -32409,16 +33503,16 @@ for (const commandName of ["rcand", "release"]) {
32409
33503
  return fail(`${commandName}: ${e.message}`);
32410
33504
  }
32411
33505
  if (o.ack && commandName !== "release") {
32412
- return fail(`${commandName}: --ack applies only to release \u2014 it overrides the rc -> main hotfix-coverage guard, which rcand does not run. Run: mmi-cli release --ack <shas>`);
33506
+ return fail(`${commandName}: --ack applies only to release ? it overrides the rc -> main hotfix-coverage guard, which rcand does not run. Run: mmi-cli release --ack <shas>`);
32413
33507
  }
32414
33508
  if (o.dev && commandName !== "release") {
32415
- return fail(`${commandName}: --dev applies only to release \u2014 it ships development -> main skipping rc, which rcand cannot do. Run: mmi-cli release --dev`);
33509
+ return fail(`${commandName}: --dev applies only to release ? it ships development -> main skipping rc, which rcand cannot do. Run: mmi-cli release --dev`);
32416
33510
  }
32417
33511
  if (o.announceSummaryFile && commandName !== "release") {
32418
- return fail(`${commandName}: --announce-summary-file applies only to release \u2014 rcand posts no Hub Slack announcement. Run: mmi-cli release --announce-summary-file <path>`);
33512
+ return fail(`${commandName}: --announce-summary-file applies only to release ? rcand posts no Hub Slack announcement. Run: mmi-cli release --announce-summary-file <path>`);
32419
33513
  }
32420
33514
  if (o.abort && commandName !== "release") {
32421
- return fail(`${commandName}: --abort applies only to release \u2014 it rolls back a proven unpublished Hub release tag. Run: mmi-cli release --abort --apply`);
33515
+ return fail(`${commandName}: --abort applies only to release ? it rolls back a proven unpublished Hub release tag. Run: mmi-cli release --abort --apply`);
32422
33516
  }
32423
33517
  if (o.retryPublish && commandName !== "release") {
32424
33518
  return fail(`${commandName}: --retry-publish applies only to release. Run: mmi-cli release --retry-publish <run-id> --apply --watch`);
@@ -32440,7 +33534,7 @@ for (const commandName of ["rcand", "release"]) {
32440
33534
  }
32441
33535
  }
32442
33536
  if (o.abort) {
32443
- if (o.resume) return fail("release: --abort and --resume are mutually exclusive \u2014 abort removes an unpublished tag while resume preserves and promotes it");
33537
+ if (o.resume) return fail("release: --abort and --resume are mutually exclusive ? abort removes an unpublished tag while resume preserves and promotes it");
32444
33538
  if (!o.apply) return fail("release: --abort requires --apply after explicit approval; nothing was written");
32445
33539
  if (o.watch || o.announceSummaryFile || o.ack || o.dev) {
32446
33540
  return fail("release: --abort accepts only --apply, --repo and --json; promotion flags cannot be combined with rollback");
@@ -32457,7 +33551,7 @@ for (const commandName of ["rcand", "release"]) {
32457
33551
  }
32458
33552
  }
32459
33553
  if (o.resume) {
32460
- if (o.apply) return fail(`${commandName}: --resume and --apply are mutually exclusive \u2014 --apply cuts the NEXT version, --resume finishes the immutable tag already on origin`);
33554
+ if (o.apply) return fail(`${commandName}: --resume and --apply are mutually exclusive ? --apply cuts the NEXT version, --resume finishes the immutable tag already on origin`);
32461
33555
  try {
32462
33556
  if (commandName === "rcand") {
32463
33557
  const result2 = await runRcandResume(trainApplyDeps(), { watch: o.watch });
@@ -32505,7 +33599,7 @@ for (const commandName of ["rcand", "release"]) {
32505
33599
  projectInfoSync = await runProjectInfoSync(result.repo, true);
32506
33600
  } catch (e) {
32507
33601
  const error = e.message;
32508
- projectInfoSync = { applied: false, note: `FAILED \u2014 ${error}`, error };
33602
+ projectInfoSync = { applied: false, note: `FAILED ? ${error}`, error };
32509
33603
  }
32510
33604
  }
32511
33605
  const alignmentPending = result.devRollForward?.status === "pr-pending" || result.rcAlignment?.status === "pr-pending";
@@ -32560,13 +33654,13 @@ function renderHotfixRelease(r) {
32560
33654
  ...r.runs.map((run) => ` - ${run.workflow}: ${run.conclusion}${run.url ? ` (${run.url})` : ""}`),
32561
33655
  ` - ${r.verifyNote}`,
32562
33656
  ...r.announceNote ? [` - announce: ${r.announceNote}`] : [],
32563
- ` - next: mmi-cli hotfix status ${r.tag} (no back-merge \u2014 development already has the fix; the next /release back-merge aligns the version manifests)`
33657
+ ` - next: mmi-cli hotfix status ${r.tag} (no back-merge ? development already has the fix; the next /release back-merge aligns the version manifests)`
32564
33658
  ].join("\n");
32565
33659
  }
32566
33660
  function renderHotfixStatus(r) {
32567
33661
  return [
32568
- `mmi-cli hotfix status: ${r.tag} on ${r.repo} \u2014 ${r.state}`,
32569
- ` - branch: ${r.branchExists ? "pushed" : "absent"} \xB7 PR: ${r.pr ? `#${r.pr.number} ${r.pr.state}` : "none"} \xB7 tag: ${r.tagPushed ? "pushed" : "absent"} \xB7 Release: ${r.releaseExists ? "exists" : "absent"}`,
33662
+ `mmi-cli hotfix status: ${r.tag} on ${r.repo} ? ${r.state}`,
33663
+ ` - branch: ${r.branchExists ? "pushed" : "absent"} ? PR: ${r.pr ? `#${r.pr.number} ${r.pr.state}` : "none"} ? tag: ${r.tagPushed ? "pushed" : "absent"} ? Release: ${r.releaseExists ? "exists" : "absent"}`,
32570
33664
  ...r.runs.map((run) => ` - ${run.workflow}: ${run.conclusion}${run.url ? ` (${run.url})` : ""}`),
32571
33665
  ` - npm @mutmutco/cli: ${r.npmVersion}`,
32572
33666
  ` - next: ${r.next}`,
@@ -32582,13 +33676,13 @@ async function runHotfixSub(sub, body, json, render) {
32582
33676
  return failGraceful(`hotfix ${sub}: ${e.message}`);
32583
33677
  }
32584
33678
  }
32585
- var hotfixCmd = program2.command("hotfix").description("stepwise hotfix orchestrator: start \u2192 release, with status (bare command prints the dry-run plan; no back-merge \u2014 #839)").option("--json", "machine-readable output").option("--apply", "not a verb; use the hotfix subcommands (start/release/status)").action(async (o) => {
33679
+ var hotfixCmd = program2.command("hotfix").description("stepwise hotfix orchestrator: start ? release, with status (bare command prints the dry-run plan; no back-merge ? #839)").option("--json", "machine-readable output").option("--apply", "not a verb; use the hotfix subcommands (start/release/status)").action(async (o) => {
32586
33680
  try {
32587
33681
  await requireFreshTrainCli("hotfix");
32588
33682
  } catch (e) {
32589
33683
  return fail(`hotfix: ${e.message}`);
32590
33684
  }
32591
- if (o.apply) return fail("hotfix: use the stepwise subcommands \u2014 mmi-cli hotfix start --from <pr#|sha> \xB7 release <vX.Y.Z> \xB7 status [vX.Y.Z]");
33685
+ if (o.apply) return fail("hotfix: use the stepwise subcommands ? mmi-cli hotfix start --from <pr#|sha> ? release <vX.Y.Z> ? status [vX.Y.Z]");
32592
33686
  const steps = trainPlan("hotfix");
32593
33687
  console.log(o.json ? JSON.stringify({ command: "hotfix", steps }, null, 2) : renderSteps("mmi-cli hotfix: dry-run plan", steps));
32594
33688
  });
@@ -32603,7 +33697,7 @@ ci.command("audit").description("read-only fleet scan: gate workflow, ruleset co
32603
33697
  else console.log(renderCiAuditText(report));
32604
33698
  if (!report.ok) process.exitCode = 1;
32605
33699
  });
32606
- ci.command("reconcile").description("audit + optionally apply merge settings and product ruleset activation (master-admin)").option("--json", "machine-readable output").option("--repo <owner/repo>", "reconcile one repo instead of the full registry").option("--apply", "PATCH merge settings + activate product ruleset when missing (master role required); activation is skipped while the repo's gate has never passed (#3694)").option("--park-ruleset", "stop the product ruleset enforcing without deleting it \u2014 keeps its required contexts (master role required)").action(async (o) => {
33700
+ ci.command("reconcile").description("audit + optionally apply merge settings and product ruleset activation (master-admin)").option("--json", "machine-readable output").option("--repo <owner/repo>", "reconcile one repo instead of the full registry").option("--apply", "PATCH merge settings + activate product ruleset when missing (master role required); activation is skipped while the repo's gate has never passed (#3694)").option("--park-ruleset", "stop the product ruleset enforcing without deleting it ? keeps its required contexts (master role required)").action(async (o) => {
32607
33701
  if (o.apply && o.parkRuleset) {
32608
33702
  return fail("ci reconcile: --apply and --park-ruleset ask for opposite things; pass one");
32609
33703
  }
@@ -32626,7 +33720,7 @@ ci.command("reconcile").description("audit + optionally apply merge settings and
32626
33720
  ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${r.errors.length ? ` errors=[${r.errors.join("; ")}]` : ""}`);
32627
33721
  }
32628
33722
  } else {
32629
- console.log("\nDry-run \u2014 re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
33723
+ console.log("\nDry-run ? re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
32630
33724
  }
32631
33725
  }
32632
33726
  const applyFailed = applyResults.some((result) => result.errors.length > 0 || result.postApply?.state === "failed");
@@ -32654,7 +33748,7 @@ access.command("role [repo]").description("D14 train authority for a repo (serve
32654
33748
  if (!a.train) process.exitCode = 1;
32655
33749
  return;
32656
33750
  }
32657
- console.log(`${a.repo}: @${a.login} is ${a.role} \u2014 train ${a.train ? "AUTHORIZED" : "not authorized"}${a.hubTrainMasterOnly ? " (Hub train is master-only)" : ""}`);
33751
+ console.log(`${a.repo}: @${a.login} is ${a.role} ? train ${a.train ? "AUTHORIZED" : "not authorized"}${a.hubTrainMasterOnly ? " (Hub train is master-only)" : ""}`);
32658
33752
  if (!a.train) process.exitCode = 1;
32659
33753
  });
32660
33754
  access.command("audit").description("audit collaborator roles + train-branch push allowlists vs the locked state; read-only, emits gh remediation, never applies").option("--json", "machine-readable output").option("--repo <owner/repo>", "audit a single repo instead of the whole org").option("--class <class>", "repo class for --repo (deployable | content)", "deployable").action(async () => {
@@ -32679,19 +33773,19 @@ access.command("audit").description("audit collaborator roles + train-branch pus
32679
33773
  targets = resolution.targets;
32680
33774
  }
32681
33775
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
32682
- const fileMatrix = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
33776
+ const fileMatrix = (0, import_node_fs39.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs39.readFileSync)("access-matrix.json", "utf8")) : {};
32683
33777
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
32684
33778
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
32685
- const fileContracts = (0, import_node_fs36.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs36.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
33779
+ const fileContracts = (0, import_node_fs39.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs39.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
32686
33780
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
32687
- const sanctioned = (0, import_node_fs36.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs36.readFileSync)("access-matrix.json", "utf8")) : {};
33781
+ const sanctioned = (0, import_node_fs39.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs39.readFileSync)("access-matrix.json", "utf8")) : {};
32688
33782
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
32689
33783
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
32690
33784
  if (!report.ok) process.exitCode = 1;
32691
33785
  });
32692
- access.command("capabilities").description("enumerate your effective vault reach \u2014 every credential NAME + tier + scope you can read/use across project + org/master tiers (names only, no values) (#1615)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets((d) => secretsCapabilities(d, o)));
33786
+ access.command("capabilities").description("enumerate your effective vault reach ? every credential NAME + tier + scope you can read/use across project + org/master tiers (names only, no values) (#1615)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "machine-readable output").action((o) => withSecrets((d) => secretsCapabilities(d, o)));
32693
33787
  var isWin2 = process.platform === "win32";
32694
- program2.command("doctor").description("heal CLI/plugin wiring and clean up repo cruft \u2014 repairs run by default (#3975); use --verbose for the full audit checklist").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; read-only, skips slow checks and network probes").option("--preflight", "eager version/plugin-heal (env repairs only \u2014 never the repo working tree) with upfront notice when stale; silent when healthy (#1871)").option("--verbose", "print the evidence behind every check \u2014 probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output (an output format \u2014 repairs still run by lane)").option("--apply", "deprecated no-op: repairs run by default now (#3975); kept so older instructions still parse").option("--no-repo-writes", "env/plugin repairs only \u2014 never mutate the repo working tree; report pending managed .gitignore repairs with the follow-up command (for train preflights)").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nA plain run heals env drift (CLI, plugin, marketplace pins) and cleans repo cruft (gitignore block,\nmerged branches, dead worktrees, aged scratch) automatically (#3975). --no-repo-writes keeps the\nworking tree untouched for train preflights; --banner/--fast/--self are read-only lanes.\n\n--banner keeps the legacy SessionStart contract and returns 0 unless the process crashes.\n").action(async (opts) => {
33788
+ program2.command("doctor").description("heal CLI/plugin wiring and clean up repo cruft ? repairs run by default (#3975); use --verbose for the full audit checklist").option("--banner", "one-line resume summary; silent when all gates pass").option("--fast", "offline-safe fast lane; read-only, skips slow checks and network probes").option("--preflight", "eager version/plugin-heal (env repairs only ? never the repo working tree) with upfront notice when stale; silent when healthy (#1871)").option("--verbose", "print the evidence behind every check ? probes, resolved paths, versions compared, and the names behind each count (#2977)").option("--guide", "print the MMI Agentic Onboarding guide URL").option("--json", "machine-readable output (an output format ? repairs still run by lane)").option("--apply", "deprecated no-op: repairs run by default now (#3975); kept so older instructions still parse").option("--no-repo-writes", "env/plugin repairs only ? never mutate the repo working tree; report pending managed .gitignore repairs with the follow-up command (for train preflights)").option("--self", "verify CLI/plugin version parity and gh auth reach; suggests plugin-heal on a hard gap (reads the published version, so not offline-safe) (#2689)").addHelpText("after", "\nExit codes:\n 0 no hard gaps remain; advisory gaps may exist\n 1 one or more included hard checks failed\n\nA plain run heals env drift (CLI, plugin, marketplace pins) and cleans repo cruft (gitignore block,\nmerged branches, dead worktrees, aged scratch) automatically (#3975). --no-repo-writes keeps the\nworking tree untouched for train preflights; --banner/--fast/--self are read-only lanes.\n\n--banner keeps the legacy SessionStart contract and returns 0 unless the process crashes.\n").action(async (opts) => {
32695
33789
  if (opts.guide) {
32696
33790
  consoleIo.log("MMI Agentic Onboarding: docs/Architecture/agentic-dev-environment.md");
32697
33791
  return;
@@ -32716,16 +33810,16 @@ function directoryBytes(path2) {
32716
33810
  let total = 0;
32717
33811
  let entries;
32718
33812
  try {
32719
- entries = (0, import_node_fs36.readdirSync)(path2, { withFileTypes: true });
33813
+ entries = (0, import_node_fs39.readdirSync)(path2, { withFileTypes: true });
32720
33814
  } catch {
32721
33815
  return 0;
32722
33816
  }
32723
33817
  for (const entry of entries) {
32724
- const child2 = (0, import_node_path35.join)(path2, entry.name);
33818
+ const child2 = (0, import_node_path38.join)(path2, entry.name);
32725
33819
  if (entry.isDirectory()) total += directoryBytes(child2);
32726
33820
  else {
32727
33821
  try {
32728
- total += (0, import_node_fs36.statSync)(child2).size;
33822
+ total += (0, import_node_fs39.statSync)(child2).size;
32729
33823
  } catch {
32730
33824
  }
32731
33825
  }
@@ -32733,25 +33827,25 @@ function directoryBytes(path2) {
32733
33827
  return total;
32734
33828
  }
32735
33829
  function listDirEntries(dir) {
32736
- return (0, import_node_fs36.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
33830
+ return (0, import_node_fs39.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
32737
33831
  }
32738
33832
  function readInstalledPluginRefs(configRoot) {
32739
33833
  const p = installedPluginsPathForConfig(configRoot);
32740
- if (!(0, import_node_fs36.existsSync)(p)) return [];
33834
+ if (!(0, import_node_fs39.existsSync)(p)) return [];
32741
33835
  try {
32742
- return installedPluginPaths((0, import_node_fs36.readFileSync)(p, "utf8"));
33836
+ return installedPluginPaths((0, import_node_fs39.readFileSync)(p, "utf8"));
32743
33837
  } catch {
32744
33838
  return null;
32745
33839
  }
32746
33840
  }
32747
33841
  function pluginCacheFsDeps(configRoot, dirBytes) {
32748
33842
  return {
32749
- exists: (p) => (0, import_node_fs36.existsSync)(p),
32750
- listVersionDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
33843
+ exists: (p) => (0, import_node_fs39.existsSync)(p),
33844
+ listVersionDirs: (root) => (0, import_node_fs39.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
32751
33845
  dirBytes,
32752
- listStagingDirs: (root) => (0, import_node_fs36.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
33846
+ listStagingDirs: (root) => (0, import_node_fs39.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
32753
33847
  try {
32754
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path35.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs36.statSync)(p).mtimeMs) };
33848
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path38.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs39.statSync)(p).mtimeMs) };
32755
33849
  } catch {
32756
33850
  return { name: d.name, mtimeMs: Date.now() };
32757
33851
  }
@@ -32765,10 +33859,10 @@ function stagingApplyFsGuard(configRoot) {
32765
33859
  return {
32766
33860
  referencedPaths: () => readInstalledPluginRefs(configRoot),
32767
33861
  mtimeMs: (name) => {
32768
- const p = (0, import_node_path35.join)(stagingRoot, name);
32769
- if (!(0, import_node_fs36.existsSync)(p)) return null;
33862
+ const p = (0, import_node_path38.join)(stagingRoot, name);
33863
+ if (!(0, import_node_fs39.existsSync)(p)) return null;
32770
33864
  try {
32771
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs36.statSync)(q).mtimeMs);
33865
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs39.statSync)(q).mtimeMs);
32772
33866
  } catch {
32773
33867
  return null;
32774
33868
  }
@@ -32788,13 +33882,13 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
32788
33882
  return;
32789
33883
  }
32790
33884
  const plan = buildPluginCachePlan(
32791
- (0, import_node_os14.homedir)(),
33885
+ (0, import_node_os15.homedir)(),
32792
33886
  running,
32793
33887
  pluginCacheFsDeps(configRoot, directoryBytes),
32794
33888
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
32795
33889
  );
32796
33890
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
32797
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs36.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
33891
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs39.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
32798
33892
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
32799
33893
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
32800
33894
  else console.log(renderPluginCachePlan(plan, result));
@@ -32802,7 +33896,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
32802
33896
  });
32803
33897
  program2.command("session-start").description("run the SessionStart verbs (whoami, board slice, doctor) in one process").action(async () => {
32804
33898
  if (isInsideRepoSubdir(process.cwd())) {
32805
- console.error("[mmi-hook] plugin session-start: cwd is a repository SUBDIRECTORY \u2014 skipping the SessionStart hook (spine/docs/plan/saga delivery); run it from the repo root.");
33899
+ console.error("[mmi-hook] plugin session-start: cwd is a repository SUBDIRECTORY ? skipping the SessionStart hook (spine/docs/plan/saga delivery); run it from the repo root.");
32806
33900
  appendHookActivity(process.cwd(), { event: "SessionStart", script: "session-start", outcome: "ran", action: "skip (repo subdirectory)" });
32807
33901
  return;
32808
33902
  }
@@ -32815,7 +33909,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
32815
33909
  const bannerIo = measured.io;
32816
33910
  const { parallel, sequential } = buildSessionStartPlan({
32817
33911
  // whoami (#879): surface the resolved human so agents act --for them without asking. Silent
32818
- // when unknown — a missing gh login must not noise or fail the banner.
33912
+ // when unknown ? a missing gh login must not noise or fail the banner.
32819
33913
  whoami: async (io) => {
32820
33914
  const report = await resolveWhoami({
32821
33915
  hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
@@ -32826,15 +33920,15 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
32826
33920
  const authority = authorityLine(report);
32827
33921
  if (authority) io.log(authority);
32828
33922
  },
32829
- // command-ladder hint (#2609): compact gh→mmi-cli cheat sheet so agents reach for mmi-cli first,
32830
- // not after a denied gh call. <1KB, pure in-memory — no network, no fs, fail-soft.
33923
+ // command-ladder hint (#2609): compact gh?mmi-cli cheat sheet so agents reach for mmi-cli first,
33924
+ // not after a denied gh call. <1KB, pure in-memory ? no network, no fs, fail-soft.
32831
33925
  commandLadderHint: async (io) => {
32832
33926
  const hint = commandLadderHint();
32833
33927
  if (hint) io.log(hint);
32834
33928
  },
32835
33929
  // stale worktrees: fast LOCAL-only git check (worktree list + branch -vv gone status). Flags leaked
32836
33930
  // worktrees / merged-but-undeleted branches so the session never starts atop leftover state. No
32837
- // network — never the ~20s `gh pr list` path of `worktree list --stale`. Fail-soft.
33931
+ // network ? never the ~20s `gh pr list` path of `worktree list --stale`. Fail-soft.
32838
33932
  staleWorktrees: async (io) => {
32839
33933
  const line = await gatherStaleWorktreeWarning();
32840
33934
  if (line) io.log(line);
@@ -32847,7 +33941,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
32847
33941
  const line = localTrainSyncBannerLine(result);
32848
33942
  if (line) io.log(line);
32849
33943
  },
32850
- // #3485 item 7: the ONLY lane that throttles the npm read — it runs on every session start, on a
33944
+ // #3485 item 7: the ONLY lane that throttles the npm read ? it runs on every session start, on a
32851
33945
  // blocking hook. Every interactive lane still reads live.
32852
33946
  doctor: async (io) => {
32853
33947
  await runDoctorClean({ banner: true }, io, mmiDoctorDeps({ throttleReleasedRead: true }));
@@ -32857,7 +33951,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
32857
33951
  for (const line of scratchGcLines(process.cwd())) bannerIo.log(line);
32858
33952
  const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
32859
33953
  if (worktreeBanner) {
32860
- spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process16.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
33954
+ spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process18.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
32861
33955
  bannerIo.log(worktreeBanner);
32862
33956
  }
32863
33957
  if (isLinkedWorktree(process.cwd())) {