@mutmutco/cli 3.114.0 → 3.115.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 +209 -184
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -7602,26 +7602,32 @@ function branchForTrackingRef(ref, remote) {
7602
7602
  function sameWorktreeMetadataPath(a, b) {
7603
7603
  return a.replace(/\\/g, "/").replace(/\/+$/, "") === b.replace(/\\/g, "/").replace(/\/+$/, "");
7604
7604
  }
7605
- function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoot, deps) {
7606
- let rootReal;
7605
+ function resolveSafeSiblingWorktreeCleanupTarget(worktreePath, siblingRoots, deps) {
7607
7606
  let worktreeReal;
7608
- try {
7609
- rootReal = deps.realpath(siblingRoot);
7610
- } catch {
7611
- return { ok: false, reason: "sibling root could not be resolved" };
7612
- }
7613
- if (!isPathUnderDirectory2(rootReal, siblingRoot) || !isPathUnderDirectory2(siblingRoot, rootReal)) {
7614
- return { ok: false, reason: "sibling root resolves outside expected path" };
7615
- }
7616
7607
  try {
7617
7608
  worktreeReal = deps.realpath(worktreePath);
7618
7609
  } catch {
7619
7610
  return { ok: false, reason: "worktree path could not be resolved" };
7620
7611
  }
7621
- if (!isPathUnderDirectory2(worktreeReal, rootReal)) {
7622
- return { ok: false, reason: "resolved worktree path outside sibling root" };
7612
+ const reasons = [];
7613
+ for (const siblingRoot of siblingRoots) {
7614
+ let rootReal;
7615
+ try {
7616
+ rootReal = deps.realpath(siblingRoot);
7617
+ } catch {
7618
+ reasons.push(`${siblingRoot}: root could not be resolved`);
7619
+ continue;
7620
+ }
7621
+ if (!isPathUnderDirectory2(rootReal, siblingRoot) || !isPathUnderDirectory2(siblingRoot, rootReal)) {
7622
+ reasons.push(`${siblingRoot}: root resolves outside expected path`);
7623
+ continue;
7624
+ }
7625
+ if (isPathUnderDirectory2(worktreeReal, rootReal)) {
7626
+ return { ok: true, path: worktreePath };
7627
+ }
7628
+ reasons.push(`${siblingRoot}: resolved worktree path outside this root`);
7623
7629
  }
7624
- return { ok: true, path: worktreePath };
7630
+ return { ok: false, reason: reasons.join("; ") || "no worktrees root to check against" };
7625
7631
  }
7626
7632
  function siblingMmiWorktreesRoot(repoRoot2) {
7627
7633
  const parent = (0, import_node_path9.dirname)(repoRoot2);
@@ -7630,6 +7636,9 @@ function siblingMmiWorktreesRoot(repoRoot2) {
7630
7636
  if ((0, import_node_path9.basename)(grandparent).toLowerCase() === "mmi-worktrees") return grandparent;
7631
7637
  return (0, import_node_path9.join)(parent, "mmi-worktrees");
7632
7638
  }
7639
+ function agentWorktreesRoot(repoRoot2) {
7640
+ return (0, import_node_path9.join)(repoRoot2, ".claude", "worktrees");
7641
+ }
7633
7642
  function worktreeScanDirs(root, repoRoot2, listDirs, isRepoCheckout) {
7634
7643
  const projectsDir = (0, import_node_path9.dirname)(root);
7635
7644
  const ownName = (0, import_node_path9.basename)(repoRoot2).toLowerCase();
@@ -15419,7 +15428,8 @@ async function siblingWorktreeDirs(explicitRoot) {
15419
15428
  const primaryRepoRoot = worktreeGitRoot ? (0, import_node_path18.dirname)((0, import_node_path18.dirname)(worktreeGitRoot)) : repoRoot2;
15420
15429
  try {
15421
15430
  const dirs = explicitRoot ? listDirsIn(resolveExplicitScanRoot(explicitRoot, primaryRepoRoot)) : worktreeScanDirs(siblingMmiWorktreesRoot(primaryRepoRoot), primaryRepoRoot, listDirsIn, isRepoCheckoutDir);
15422
- return dirs.map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
15431
+ const agentDirs2 = listDirsIn(agentWorktreesRoot(primaryRepoRoot));
15432
+ return [...dirs, ...agentDirs2].map((dir) => inspectSiblingWorktreeDir(dir, worktreeGitRoot)).filter((entry) => Boolean(entry));
15423
15433
  } catch {
15424
15434
  return [];
15425
15435
  }
@@ -15769,10 +15779,10 @@ var rollout_plan_default = {
15769
15779
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
15770
15780
  },
15771
15781
  baseline: {
15772
- version: "3.114.0",
15773
- tag: "v3.114.0",
15774
- commit: "cbe675e9904f",
15775
- npm: "@mutmutco/cli@3.114.0"
15782
+ version: "3.115.0",
15783
+ tag: "v3.115.0",
15784
+ commit: "955ec7ae88f1",
15785
+ npm: "@mutmutco/cli@3.115.0"
15776
15786
  },
15777
15787
  exitCriterion: "fleet-n-of-n",
15778
15788
  hubOnlyShortcut: "forbidden",
@@ -15789,14 +15799,14 @@ var rollout_plan_default = {
15789
15799
  repo: "mutmutco/mmi-hub",
15790
15800
  role: "canary",
15791
15801
  schedule: "train",
15792
- v3Target: "v3.114.0"
15802
+ v3Target: "v3.115.0"
15793
15803
  }
15794
15804
  ],
15795
15805
  rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
15796
15806
  rollback: {
15797
15807
  independent: true,
15798
- mechanism: "npm dist-tag latest -> 3.114.0 and redeploy the Hub Lambda from tag v3.114.0 (cbe675e9904f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15799
- v3Target: "v3.114.0 (@mutmutco/cli@3.114.0, tag commit cbe675e9904f \u2014 the preserved latest-v3 distribution, D6b)"
15808
+ mechanism: "npm dist-tag latest -> 3.115.0 and redeploy the Hub Lambda from tag v3.115.0 (955ec7ae88f1); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15809
+ v3Target: "v3.115.0 (@mutmutco/cli@3.115.0, tag commit 955ec7ae88f1 \u2014 the preserved latest-v3 distribution, D6b)"
15800
15810
  }
15801
15811
  },
15802
15812
  {
@@ -32936,12 +32946,15 @@ async function applyGcPlan(plan, remote, opts = {}) {
32936
32946
  result.removedTrackingRefs.push(...branchTracking.removedTrackingRefs);
32937
32947
  result.failed.push(...branchTracking.failed);
32938
32948
  const removeDeps = worktreeRemoveDeps(async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
32939
- const siblingRoot = opts.root ? resolveExplicitScanRoot(opts.root, primaryRepoRoot) : siblingMmiWorktreesRoot(primaryRepoRoot);
32949
+ const cleanupRoots = [
32950
+ opts.root ? resolveExplicitScanRoot(opts.root, primaryRepoRoot) : siblingMmiWorktreesRoot(primaryRepoRoot),
32951
+ agentWorktreesRoot(primaryRepoRoot)
32952
+ ];
32940
32953
  for (const wt of worktreeDirsToRemove) {
32941
32954
  const owner = findWorktreeOwner(owners, wt.path);
32942
32955
  let removalAttempted = false;
32943
32956
  try {
32944
- const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
32957
+ const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, cleanupRoots, {
32945
32958
  realpath: (path2) => (0, import_node_fs38.realpathSync)(path2)
32946
32959
  });
32947
32960
  if (!cleanupTarget.ok) {
@@ -39195,9 +39208,9 @@ async function withEnvHealLock(what, run) {
39195
39208
  );
39196
39209
  } catch (e) {
39197
39210
  if (e instanceof FileLockBusyError) {
39198
- return { ok: false, skipped: true, detail: `${what} skipped ? ${e.message}` };
39211
+ return { ok: false, skipped: true, detail: `${what} skipped \u2014 ${e.message}` };
39199
39212
  }
39200
- return { ok: false, detail: `${what} could not take the env-heal lock ? ${e.message}` };
39213
+ return { ok: false, detail: `${what} could not take the env-heal lock \u2014 ${e.message}` };
39201
39214
  }
39202
39215
  }
39203
39216
  function throttledReleasedVersion() {
@@ -39253,13 +39266,13 @@ function mmiDoctorDeps(opts = {}) {
39253
39266
  pluginTrustState: () => codexHookTrustState(),
39254
39267
  releasedVersion: throttled ? throttled.read : fetchNpmReleasedVersion,
39255
39268
  releasedVersionNote: throttled ? throttled.note : void 0,
39256
- // #3272: the --apply self-heal for a stale running CLI ? npm global, shadows any plugin shim (#2879).
39269
+ // #3272: the --apply self-heal for a stale running CLI npm global, shadows any plugin shim (#2879).
39257
39270
  // #3489: serialised. Both heals mutate machine-global state (the npm global prefix; the Claude
39258
39271
  // marketplace clone + plugin cache) and neither is idempotent under concurrency. They share ONE lock
39259
39272
  // rather than one each, because they are not independent: the plugin heal reinstalls a bundle that
39260
39273
  // carries a CLI shim, so a concurrent npm global install races the same PATH surface.
39261
39274
  updateCli: (target, onStep) => withEnvHealLock("npm global CLI self-update", () => npmSelfUpdateCli(target, onStep)),
39262
- // #3282: the --apply self-heal for a stale/unresolved Claude plugin ? the same marketplace reinstall
39275
+ // #3282: the --apply self-heal for a stale/unresolved Claude plugin the same marketplace reinstall
39263
39276
  // `mmi-cli plugin heal` drives. Takes effect on the next Claude reload, so the row asks for a restart.
39264
39277
  healPlugin: (onStep) => {
39265
39278
  const surface = detectSurface(process.env);
@@ -39277,7 +39290,7 @@ function mmiDoctorDeps(opts = {}) {
39277
39290
  // #2759: same fetch+ff-only sync SessionStart runs, wired here too so an on-demand `mmi-cli doctor`
39278
39291
  // self-heals a checkout a long-running session left stale.
39279
39292
  syncTrain: () => syncLocalTrainBranches(execFileGitRun),
39280
- // #2903: doctor DETECTS stale cached plugin versions and defers the delete to `plugin-prune` ? it never
39293
+ // #2903: doctor DETECTS stale cached plugin versions and defers the delete to `plugin-prune` it never
39281
39294
  // writes to the harness-owned cache itself. Same plan builder the verb uses, so the two never disagree.
39282
39295
  // No `withBytes` here: doctor runs on EVERY SessionStart, and sizing means recursively stat'ing every
39283
39296
  // stale version tree. The count comes from one cheap readdir; `plugin-prune` reports the MB.
@@ -39361,9 +39374,9 @@ function mmiDoctorDeps(opts = {}) {
39361
39374
  // binary is exactly the half-applied state being repaired.
39362
39375
  healClaudeBinary: (onStep) => withEnvHealLock("claude binary restore", async () => healClaudeBinary({}, onStep)),
39363
39376
  // #3470: what the SessionStart hook LAST emitted, measured at emission by the session-start verb below.
39364
- // A local record read ? cheap enough for every lane, including the banner.
39377
+ // A local record read cheap enough for every lane, including the banner.
39365
39378
  sessionPayload: () => readSessionPayload(process.cwd()),
39366
- // #3485 items 9 and 8: why the MMI plugin has never printed an "updated ? please restart" notice, and
39379
+ // #3485 items 9 and 8: why the MMI plugin has never printed an "updated please restart" notice, and
39367
39380
  // which branch it would pick one up from. Two local file reads, no network, fail-soft to no rows.
39368
39381
  marketplaceRows: (hostBlocked) => {
39369
39382
  try {
@@ -39420,13 +39433,13 @@ function mmiDoctorDeps(opts = {}) {
39420
39433
  }
39421
39434
  },
39422
39435
  // #4091: the same `docs index --check` comparison the required CI gate runs, in-process. Rooted at the
39423
- // repo root doctor already resolved ? never `process.cwd()`, which would have `mmi-cli doctor` run from
39436
+ // repo root doctor already resolved never `process.cwd()`, which would have `mmi-cli doctor` run from
39424
39437
  // `cli/` measure a `cli/docs/` tree that does not exist.
39425
39438
  //
39426
39439
  // `existsSync` on `docs/index.md` is the adoption gate, and it sits UNDER the table's org-repo gate
39427
39440
  // rather than replacing it: a missing index is drift by construction, so without this a repo that never
39428
39441
  // adopted the generated routing index (MMG-Unlive: `docs/Archive/**` only, no index, no gate.yml) would
39429
- // get a permanent ? demanding an artifact it never asked for.
39442
+ // get a permanent demanding an artifact it never asked for.
39430
39443
  docsIndexState: (root) => {
39431
39444
  if (!(0, import_node_fs46.existsSync)((0, import_node_path43.join)(root, DOCS_INDEX_PATH))) return void 0;
39432
39445
  const real = createDocsIndexDeps(root);
@@ -39514,7 +39527,7 @@ function mmiDoctorDeps(opts = {}) {
39514
39527
  const names = new Set((opts.program?.commands ?? []).map((c) => c.name()));
39515
39528
  return required.filter((n) => !names.has(n));
39516
39529
  },
39517
- // #4156: short-timeout Hub status for the current repo. Fail-soft ? never throws to doctor.
39530
+ // #4156: short-timeout Hub status for the current repo. Fail-soft never throws to doctor.
39518
39531
  repoIndexCloudState: async (root) => {
39519
39532
  const local = repoIndexStatus(root);
39520
39533
  try {
@@ -39574,7 +39587,7 @@ async function requireFreshTrainCli(commandName) {
39574
39587
  const releasedVersion = await fetchNpmReleasedVersion();
39575
39588
  if (!releasedVersion) {
39576
39589
  throw new Error(
39577
- `${commandName}: the train staleness gate could not read the released CLI version (\`npm view @mutmutco/cli version\` returned nothing ? registry unreachable, npm missing, or output unparseable). That is a FAILED read, not "the running CLI is fresh", so this train command refuses rather than running unproven. This gate runs before any train mutation, so this invocation changed nothing. Retry once the registry answers, or set MMI_TRAIN_FRESH_OVERRIDE=1 to run this command with the staleness check deliberately skipped.`
39590
+ `${commandName}: the train staleness gate could not read the released CLI version (\`npm view @mutmutco/cli version\` returned nothing \u2014 registry unreachable, npm missing, or output unparseable). That is a FAILED read, not "the running CLI is fresh", so this train command refuses rather than running unproven. This gate runs before any train mutation, so this invocation changed nothing. Retry once the registry answers, or set MMI_TRAIN_FRESH_OVERRIDE=1 to run this command with the staleness check deliberately skipped.`
39578
39591
  );
39579
39592
  }
39580
39593
  const report = buildVersionLagReport({
@@ -39619,7 +39632,7 @@ function argvWantsJson2() {
39619
39632
  var unknownFlagJsonHandled = false;
39620
39633
  var PARSE_HINT_SENTINEL = "@@mmi-parse-hint@@";
39621
39634
  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";
39622
- 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)`;
39635
+ 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)`;
39623
39636
  var lastParseErrorKind = "other";
39624
39637
  var lastUnknownCommand;
39625
39638
  function classifyParseError(plain) {
@@ -39685,7 +39698,7 @@ function resolveParseHint() {
39685
39698
  const path2 = commandPath2(cmd);
39686
39699
  return [
39687
39700
  `Usage: mmi-cli ${path2} ${cmd.usage()}`,
39688
- "This command exists and your mmi-cli is not the problem ? the ARGUMENTS did not parse.",
39701
+ "This command exists and your mmi-cli is not the problem \u2014 the ARGUMENTS did not parse.",
39689
39702
  `Run \`mmi-cli ${path2} --help\` for its signature, \`mmi-cli explain ${path2}\` for detail, or \`mmi-cli commands\` for the full map.`
39690
39703
  ].join("\n");
39691
39704
  }
@@ -39722,7 +39735,7 @@ function envelopeAwareWriteErr(str) {
39722
39735
  const positional = TARGET_SELECTOR_FLAGS.has(flag) ? positionalTargetForm(invoked, { flag, argv }) : void 0;
39723
39736
  if (argvWantsJson2()) {
39724
39737
  const suggestion = positional ? void 0 : didYouMean(flag, commandOwnLongFlags(invoked));
39725
- const corrected = suggestion ? `mmi-cli ${argv.map((a) => a === flag ? suggestion : a).join(" ")}` : void 0;
39738
+ const corrected = suggestion ? `mmi-cli ${canonicalArgvFor(argv.map((a) => a === flag ? suggestion : a)).join(" ")}` : void 0;
39726
39739
  const message = positional ? unknownTargetFlagMessage(flag, positional) : `unknown option '${flag}'`;
39727
39740
  process.stderr.write(
39728
39741
  formatErrorEnvelope(message, {
@@ -39746,7 +39759,7 @@ function envelopeAwareWriteErr(str) {
39746
39759
  process.stderr.write(str);
39747
39760
  }
39748
39761
  var program2 = new Command();
39749
- 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);
39762
+ 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);
39750
39763
  program2.addHelpText(
39751
39764
  "before",
39752
39765
  `Houses (canonical roots): oracle \xB7 harbour \xB7 devops \xB7 vault \xB7 learning
@@ -39807,7 +39820,7 @@ rules.command("gitignore").option("--write", "upsert the managed block into .git
39807
39820
  return;
39808
39821
  }
39809
39822
  if (plan.changed) {
39810
- console.error(`mmi-cli devops org rules gitignore: managed block drift (${drift}) ? run \`mmi-cli devops org rules gitignore --write\` and commit`);
39823
+ console.error(`mmi-cli devops org rules gitignore: managed block drift (${drift}) \u2014 run \`mmi-cli devops org rules gitignore --write\` and commit`);
39811
39824
  process.exitCode = 1;
39812
39825
  } else {
39813
39826
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -39913,7 +39926,7 @@ wave.command("land").description("serial merge open PRs to development with reba
39913
39926
  });
39914
39927
  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)");
39915
39928
  var DEFERRED_SWEEP_HARD_TIMEOUT_MS = 12e4;
39916
- 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) => {
39929
+ 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) => {
39917
39930
  await runWithSweepWatchdog(async () => {
39918
39931
  try {
39919
39932
  const deferredStore = await createDeferredWorktreeStore();
@@ -39921,7 +39934,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
39921
39934
  const result = await sweepDeferredWorktreesWithRetry(
39922
39935
  deferredStore,
39923
39936
  // #2841: `-c core.fsmonitor=false` stops a git fsmonitor daemon from inheriting this detached
39924
- // worker's stdio pipe (the likely Windows hang ? execFile then never sees EOF and never resolves).
39937
+ // worker's stdio pipe (the likely Windows hang execFile then never sees EOF and never resolves).
39925
39938
  worktreeRemoveDeps(async (args) => (await execFileP2("git", ["-c", "core.fsmonitor=false", ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
39926
39939
  { removalContext }
39927
39940
  );
@@ -39937,11 +39950,11 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
39937
39950
  fail(`worktree gc sweep-deferred: ${e.message}`);
39938
39951
  }
39939
39952
  }, DEFERRED_SWEEP_HARD_TIMEOUT_MS, () => {
39940
- console.error("worktree gc sweep-deferred: exceeded hard timeout ? exiting so the detached worker cannot leak (#2841)");
39953
+ console.error("worktree gc sweep-deferred: exceeded hard timeout \u2014 exiting so the detached worker cannot leak (#2841)");
39941
39954
  process.exit(process.exitCode ?? 0);
39942
39955
  });
39943
39956
  });
39944
- gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH ? an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
39957
+ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH \u2014 an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) \u2014 descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
39945
39958
  if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
39946
39959
  if (o.scratch) {
39947
39960
  try {
@@ -39962,7 +39975,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
39962
39975
  if (!(0, import_node_fs46.existsSync)(root) || !(0, import_node_fs46.statSync)(root).isDirectory()) return fail(`worktree gc: --root ${o.root} is not a directory`);
39963
39976
  const gcRepoRoot = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim() || process.cwd();
39964
39977
  if (isPathUnderDirectory2(gcRepoRoot, root)) {
39965
- return fail(`worktree gc: --root ${root} contains this checkout ? name a worktrees root, not the repo or an ancestor of it`);
39978
+ return fail(`worktree gc: --root ${root} contains this checkout \u2014 name a worktrees root, not the repo or an ancestor of it`);
39966
39979
  }
39967
39980
  }
39968
39981
  try {
@@ -39976,7 +39989,7 @@ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--appl
39976
39989
  const removalContext = await currentWorktreeRemovalContext("worktree gc", o.force);
39977
39990
  const sweepResult = await sweepDeferredWorktrees(
39978
39991
  deferredStore,
39979
- // #2841: same `-c core.fsmonitor=false` guard as the detached `gc sweep-deferred` worker ? keep a
39992
+ // #2841: same `-c core.fsmonitor=false` guard as the detached `gc sweep-deferred` worker keep a
39980
39993
  // git fsmonitor daemon from inheriting this sweep's stdio pipe and wedging the git call on Windows.
39981
39994
  worktreeRemoveDeps(async (args) => (await execFileP2("git", ["-c", "core.fsmonitor=false", ...args], { timeout: GIT_TIMEOUT_MS })).stdout),
39982
39995
  removalContext
@@ -40063,7 +40076,7 @@ async function unprovenWorktreeReason(wtPath, repoRoot2) {
40063
40076
  return `'git worktree list' could not be read from ${repoRoot2}, so ${wtPath} could not be proven registered`;
40064
40077
  }
40065
40078
  if (!registered.some((w) => samePath(w.path, wtPath))) {
40066
- 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`;
40079
+ 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`;
40067
40080
  }
40068
40081
  return void 0;
40069
40082
  }
@@ -40113,15 +40126,15 @@ function acquireWorktreeSetupLock(worktreeRoot) {
40113
40126
  return null;
40114
40127
  }
40115
40128
  }
40116
- var worktree = program2.command("worktree").description("self-provisioning worktrees ? install deps + copy local-only config");
40129
+ var worktree = program2.command("worktree").description("self-provisioning worktrees \u2014 install deps + copy local-only config");
40117
40130
  withExamples(mutating(
40118
- 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"),
40131
+ 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"),
40119
40132
  worktreeCreatePlan
40120
40133
  ).action(async (target, o, cmd) => {
40121
40134
  let step = "resolve the branch name";
40122
40135
  try {
40123
40136
  if (o.base !== void 0 && cmd.getOptionValueSource("from") === "cli" && o.base !== o.from) {
40124
- 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`);
40137
+ 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`);
40125
40138
  }
40126
40139
  const fromRef = o.base ?? o.from;
40127
40140
  const issueForm = isIssueRef(target);
@@ -40139,7 +40152,7 @@ withExamples(mutating(
40139
40152
  const slug = o.slug ?? await fetchIssueTitle(selector.repo, selector.number).then((t) => t ? slugifyIssueTitle(t) : "");
40140
40153
  branch = buildNewBranchName(selector.number, slug ?? "");
40141
40154
  if (PROTECTED_BRANCHES2.has(branch)) {
40142
- return fail(`worktree create: generated branch name '${branch}' collides with a protected train branch ? pass a branch name instead`);
40155
+ return fail(`worktree create: generated branch name '${branch}' collides with a protected train branch \u2014 pass a branch name instead`);
40143
40156
  }
40144
40157
  } else if (o.claim || o.for) {
40145
40158
  return fail("worktree create: --claim/--for need an issue-ref argument (e.g. worktree create 2687 --claim)");
@@ -40179,7 +40192,7 @@ withExamples(mutating(
40179
40192
  return fail(`worktree create: ${wtPath} is already registered for '${exact.branch ?? "detached HEAD"}', not '${branch}'`);
40180
40193
  }
40181
40194
  const status = (await execFileP2("git", ["-C", wtPath, "status", "--porcelain"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
40182
- if (status) return fail(`worktree create: refusing to resume ${wtPath} ? it has uncommitted changes`);
40195
+ if (status) return fail(`worktree create: refusing to resume ${wtPath} \u2014 it has uncommitted changes`);
40183
40196
  const head = await revParseRef(`refs/heads/${branch}`);
40184
40197
  const baseOid = await revParseRef(base);
40185
40198
  if (!head || !baseOid) return fail(`worktree create: could not prove '${branch}' and '${base}' before resume`);
@@ -40190,7 +40203,7 @@ withExamples(mutating(
40190
40203
  // io-census-allow: an unprovable ancestor probe conservatively refuses the resume below rather than fast-forwarding onto unproven history
40191
40204
  ).then(() => true).catch(() => false);
40192
40205
  if (!canFastForward) {
40193
- return fail(`worktree create: refusing to resume '${branch}' ? it has local commits or diverges from ${base}`);
40206
+ return fail(`worktree create: refusing to resume '${branch}' \u2014 it has local commits or diverges from ${base}`);
40194
40207
  }
40195
40208
  await execFileP2("git", ["-C", wtPath, "merge", "--ff-only", base], { timeout: GIT_TIMEOUT_MS });
40196
40209
  resumed = true;
@@ -40292,11 +40305,11 @@ withExamples(mutating(
40292
40305
  console.log(` installed: ${report.installed.map((i) => i.dir || ".").join(", ") || "none"}`);
40293
40306
  console.log(` copied: ${report.copied.join(", ") || "none"}`);
40294
40307
  if (issueForm && o.claim && selector) {
40295
- if (claimError) console.error(` warning: board claim failed (${claimError}) ? worktree is ready, finish the claim manually`);
40308
+ if (claimError) console.error(` warning: board claim failed (${claimError}) \u2014 worktree is ready, finish the claim manually`);
40296
40309
  else console.log(` board item ${selector.repo}#${selector.number} claimed`);
40297
40310
  }
40298
40311
  } catch (e) {
40299
- fail(`worktree create: failed at step '${step}' ? ${e.message}`);
40312
+ fail(`worktree create: failed at step '${step}' \u2014 ${e.message}`);
40300
40313
  }
40301
40314
  }), [
40302
40315
  "mmi-cli worktree create fix/gc-sweep-2687",
@@ -40307,7 +40320,7 @@ worktree.command("setup [path]").description("provision an existing worktree (in
40307
40320
  const root = path2 ?? process.cwd();
40308
40321
  const release = acquireWorktreeSetupLock(root);
40309
40322
  if (!release) {
40310
- if (!o.quiet && !o.json) console.log("worktree setup: another provision is in progress ? skipping");
40323
+ if (!o.quiet && !o.json) console.log("worktree setup: another provision is in progress \u2014 skipping");
40311
40324
  return;
40312
40325
  }
40313
40326
  try {
@@ -40327,7 +40340,7 @@ worktree.command("setup [path]").description("provision an existing worktree (in
40327
40340
  release();
40328
40341
  }
40329
40342
  });
40330
- 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) => {
40343
+ 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) => {
40331
40344
  const limit = Number.parseInt(o.limit, 10);
40332
40345
  if (!Number.isFinite(limit) || limit < 1) return fail("worktree events: --limit must be a positive integer");
40333
40346
  const primaryRoot = await primaryCheckoutRoot(process.cwd()) ?? process.cwd();
@@ -40345,11 +40358,11 @@ async function attachToProject(issueNumber, repo, priority) {
40345
40358
  try {
40346
40359
  cfg = await loadConfigForRepo(targetRepo2);
40347
40360
  } catch (e) {
40348
- console.error(`issue create: board attach skipped ? ${e.message}`);
40361
+ console.error(`issue create: board attach skipped \u2014 ${e.message}`);
40349
40362
  return { onBoard: false };
40350
40363
  }
40351
40364
  if (!cfg.projectId) {
40352
- console.error(`issue create: board attach skipped ? no Hub registry board META for ${targetRepo2 ?? "current repo"}; run \`mmi-cli oracle org project get ${targetRepo2 ?? "<owner/repo>"}\` and backfill board coords`);
40365
+ console.error(`issue create: board attach skipped \u2014 no Hub registry board META for ${targetRepo2 ?? "current repo"}; run \`mmi-cli oracle org project get ${targetRepo2 ?? "<owner/repo>"}\` and backfill board coords`);
40353
40366
  return { onBoard: false };
40354
40367
  }
40355
40368
  try {
@@ -40404,7 +40417,7 @@ registerEdgeCommands(program2);
40404
40417
  registerBoxCommands(program2);
40405
40418
  registerSchedulesCommands(program2);
40406
40419
  registerSchedulesLiftCommand(program2);
40407
- var repoIndex = program2.command("repo-index").description("Hub cloud + local pointer index ? paths/symbols only, never wiki prose (Hub#4133)");
40420
+ var repoIndex = program2.command("repo-index").description("Hub cloud + local pointer index \u2014 paths/symbols only, never wiki prose (Hub#4133)");
40408
40421
  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) => {
40409
40422
  try {
40410
40423
  const root = await repoRoot();
@@ -40422,7 +40435,7 @@ repoIndex.command("rebuild").description("walk the checkout (git ls-files + giti
40422
40435
  }, null, 2));
40423
40436
  return;
40424
40437
  }
40425
- console.log(`repo-index: rebuilt ${built.repo} ? ${built.entries.length} files, ${symbolCount} symbols`);
40438
+ console.log(`repo-index: rebuilt ${built.repo} \u2014 ${built.entries.length} files, ${symbolCount} symbols`);
40426
40439
  } catch (e) {
40427
40440
  await failGraceful(e.message);
40428
40441
  }
@@ -40446,7 +40459,7 @@ repoIndex.command("publish").description("publish this checkout's projection to
40446
40459
  const embRequested = Number(res.body.embRequested ?? 0);
40447
40460
  const embCap = Number(res.body.embCap ?? 0);
40448
40461
  const capNote = o.embed && embRequested > embCap && embCap > 0 ? ` (emb=${embCount} of ${embRequested} requested \u2014 capped at ${embCap}/publish, run \`--embed\` again to continue)` : "";
40449
- console.log(`repo-index: published ${res.body.repo} ? ${res.body.fileCount} files, emb=${embCount}${capNote}`);
40462
+ console.log(`repo-index: published ${res.body.repo} \u2014 ${res.body.fileCount} files, emb=${embCount}${capNote}`);
40450
40463
  } catch (e) {
40451
40464
  return await failGraceful(e.message);
40452
40465
  }
@@ -40522,7 +40535,7 @@ repoIndex.command("status").description("show local and/or cloud repo-index stat
40522
40535
  const embGap = Number(st2.embGap ?? Math.max(0, fileCount - embCount));
40523
40536
  const stale = st2.staleBuiltAt === true ? " stale-builtAt" : "";
40524
40537
  console.log(
40525
- `repo-index: cloud ${st2.repo} built ${st2.builtAt} ? ${fileCount} files, emb=${embCount}/${fileCount} gap=${embGap}${stale}`
40538
+ `repo-index: cloud ${st2.repo} built ${st2.builtAt} \u2014 ${fileCount} files, emb=${embCount}/${fileCount} gap=${embGap}${stale}`
40526
40539
  );
40527
40540
  return;
40528
40541
  }
@@ -40531,7 +40544,7 @@ repoIndex.command("status").description("show local and/or cloud repo-index stat
40531
40544
  const present = st2.present;
40532
40545
  if (count === 0 || present === false) {
40533
40546
  console.error(
40534
- "repo-index: no cloud projection yet ? after Hub deploy, run harbour `repo-index-reconcile` or `mmi-cli oracle repo-index sync-estate` (see docs/Guides/repo-index-runbook.md)."
40547
+ "repo-index: no cloud projection yet \u2014 after Hub deploy, run harbour `repo-index-reconcile` or `mmi-cli oracle repo-index sync-estate` (see docs/Guides/repo-index-runbook.md)."
40535
40548
  );
40536
40549
  }
40537
40550
  return;
@@ -40543,10 +40556,10 @@ repoIndex.command("status").description("show local and/or cloud repo-index stat
40543
40556
  return;
40544
40557
  }
40545
40558
  if (!st.present) {
40546
- console.log(`repo-index: local missing ? cloud search needs no rebuild; optional \`repo-index rebuild\` for --local`);
40559
+ console.log(`repo-index: local missing \u2014 cloud search needs no rebuild; optional \`repo-index rebuild\` for --local`);
40547
40560
  return;
40548
40561
  }
40549
- console.log(`repo-index: local ${st.repo} built ${st.builtAt} ? ${st.fileCount} files, ${st.symbolCount} symbols`);
40562
+ console.log(`repo-index: local ${st.repo} built ${st.builtAt} \u2014 ${st.fileCount} files, ${st.symbolCount} symbols`);
40550
40563
  } catch (e) {
40551
40564
  return await failGraceful(e.message);
40552
40565
  }
@@ -40593,7 +40606,7 @@ repoIndex.command("health").description("post-deploy health gate: status + golde
40593
40606
  return await failGraceful(e.message);
40594
40607
  }
40595
40608
  });
40596
- 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) => {
40609
+ repoIndex.command("gc").description("remove cloud projections for repos no longer on the registry roster").option("--cloud", "required \u2014 GC only applies to Hub cloud").option("--json", "machine-readable result").action(async (o) => {
40597
40610
  try {
40598
40611
  if (!o.cloud) return await failGraceful("repo-index gc requires --cloud");
40599
40612
  const cfg = await loadConfig();
@@ -40627,7 +40640,7 @@ repoIndex.command("sync-estate").description("Hub indexer: shallow-clone registr
40627
40640
  for (const p of res.published) {
40628
40641
  const gap = p.embGap ?? Math.max(0, p.fileCount - (p.embCount ?? 0));
40629
40642
  const trunc = p.embTruncated ? " truncated" : "";
40630
- console.log(`repo-index: published ${p.repo} ? ${p.fileCount} files emb=${p.embCount ?? 0}/${p.fileCount} gap=${gap}${trunc}`);
40643
+ console.log(`repo-index: published ${p.repo} \u2014 ${p.fileCount} files emb=${p.embCount ?? 0}/${p.fileCount} gap=${gap}${trunc}`);
40631
40644
  }
40632
40645
  for (const f of res.failed) {
40633
40646
  console.error(`repo-index: FAILED ${f.repo}: ${f.error}`);
@@ -40637,14 +40650,14 @@ repoIndex.command("sync-estate").description("Hub indexer: shallow-clone registr
40637
40650
  await failGraceful(e.message);
40638
40651
  }
40639
40652
  });
40640
- var docs = program2.command("docs").description("generated docs surfaces ? the routing index (org knowledge layer)");
40641
- 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) => {
40653
+ var docs = program2.command("docs").description("generated docs surfaces \u2014 the routing index (org knowledge layer)");
40654
+ 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) => {
40642
40655
  try {
40643
40656
  const root = await repoRoot();
40644
40657
  const result = docsIndex(createDocsIndexDeps(root), { check: Boolean(o.check) });
40645
40658
  if (o.check) {
40646
40659
  if (result.drift) {
40647
- return failGraceful(`docs index: ${DOCS_INDEX_PATH} is stale ? run \`mmi-cli oracle docs index --write\` and commit the result`);
40660
+ return failGraceful(`docs index: ${DOCS_INDEX_PATH} is stale \u2014 run \`mmi-cli oracle docs index --write\` and commit the result`);
40648
40661
  }
40649
40662
  console.log(`docs index: ${DOCS_INDEX_PATH} is current`);
40650
40663
  return;
@@ -40654,7 +40667,7 @@ docs.command("index").description("regenerate docs/index.md from the docs/ tree
40654
40667
  await failGraceful(e.message);
40655
40668
  }
40656
40669
  });
40657
- 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) => {
40670
+ docs.command("refs").description("deterministic doc reference gate: every backticked repo path, relative .md link, `mmi-cli` command ref, and `<!-- pinned by \u2014 -->` 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) => {
40658
40671
  try {
40659
40672
  const root = await repoRoot();
40660
40673
  const commandPaths = new Set(
@@ -40682,7 +40695,7 @@ docs.command("refs").description("deterministic doc reference gate: every backti
40682
40695
  await failGraceful(e.message);
40683
40696
  }
40684
40697
  });
40685
- var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract ? every child process must be unable to pop a console window");
40698
+ var spawnCmd = program2.command("spawn").description("this repo's process-spawn contract \u2014 every child process must be unable to pop a console window");
40686
40699
  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) => {
40687
40700
  try {
40688
40701
  const root = await repoRoot();
@@ -40698,14 +40711,14 @@ spawnCmd.command("policy").description("enforce the windowsHide contract across
40698
40711
  }
40699
40712
  for (const f of result.findings) console.error(`spawn policy: ${f.detail}`);
40700
40713
  console.error(
40701
- "\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."
40714
+ "\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."
40702
40715
  );
40703
40716
  process.exitCode = 1;
40704
40717
  } catch (e) {
40705
40718
  await failGraceful(e.message);
40706
40719
  }
40707
40720
  });
40708
- var tests = program2.command("tests").description("a repo's test-policy.json ? the opt-in test contract and its enforcement");
40721
+ var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
40709
40722
  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) => {
40710
40723
  try {
40711
40724
  const root = await repoRoot();
@@ -40718,7 +40731,7 @@ tests.command("policy").description("enforce this repo's test-policy.json agains
40718
40731
  if (result.overriddenBy && result.ok) {
40719
40732
  const { sha, reason, kinds } = result.overriddenBy;
40720
40733
  const scope = (result.waived ?? []).map((f) => f.kind).join(", ") || "nothing";
40721
- console.log(`tests policy: overridden by ${sha.slice(0, 8)} (waiving ${scope}; scope ${kinds.join(", ")}) ? ${reason}`);
40734
+ console.log(`tests policy: overridden by ${sha.slice(0, 8)} (waiving ${scope}; scope ${kinds.join(", ")}) \u2014 ${reason}`);
40722
40735
  return;
40723
40736
  }
40724
40737
  if (result.ok) {
@@ -40740,7 +40753,7 @@ async function reportWrite(label, res) {
40740
40753
  }
40741
40754
  if (res.error) return failGraceful(`${label}: ${res.error}`);
40742
40755
  const detail = res.body?.error ?? "";
40743
- return failGraceful(`${label}: HTTP ${res.status}${detail ? ` ? ${detail}` : ""}`);
40756
+ return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
40744
40757
  }
40745
40758
  var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
40746
40759
  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) => {
@@ -40767,7 +40780,7 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
40767
40780
  printLine(o.json ? JSON.stringify(result, null, 2) : renderTenantControl(result));
40768
40781
  }
40769
40782
  if (result.conclusion === "failure") {
40770
- return failGraceful(`runtime tenant control ${stage} ${action}: ${result.category ?? "failed"} ? ${result.note}`);
40783
+ return failGraceful(`runtime tenant control ${stage} ${action}: ${result.category ?? "failed"} \u2014 ${result.note}`);
40771
40784
  }
40772
40785
  } catch (e) {
40773
40786
  return failGraceful(`runtime tenant control: ${e.message}`);
@@ -40776,7 +40789,7 @@ tenant.command("control <owner/repo> <stage> <action>").description("bounded ten
40776
40789
  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) => {
40777
40790
  try {
40778
40791
  const result = await runTenantReconcile(trainApplyDeps(), { repo, stage, watch: o.watch });
40779
- printLine(o.json ? JSON.stringify(result, null, 2) : `${result.note}${result.runUrl ? ` ? ${result.runUrl}` : ""}`);
40792
+ printLine(o.json ? JSON.stringify(result, null, 2) : `${result.note}${result.runUrl ? ` \u2014 ${result.runUrl}` : ""}`);
40780
40793
  if (result.conclusion === "failure" || !result.dispatched) process.exitCode = 1;
40781
40794
  } catch (e) {
40782
40795
  return failGraceful(`runtime tenant reconcile: ${e.message}`);
@@ -40789,7 +40802,7 @@ tenant.command("status <owner/repo> <stage>").description("read tenant runtime r
40789
40802
  console.log(JSON.stringify(result, null, 2));
40790
40803
  if (result.publicProbe?.ok === false) process.exitCode = 1;
40791
40804
  });
40792
- 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) => {
40805
+ 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) => {
40793
40806
  if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime tenant redeploy: <stage> must be dev, rc, or main");
40794
40807
  try {
40795
40808
  const result = await runTenantRedeploy(trainApplyDeps(), { repo, stage, ref: o.ref, watch: o.watch });
@@ -40798,9 +40811,9 @@ tenant.command("redeploy <owner/repo> <stage>").description("re-dispatch the cen
40798
40811
  return failGraceful(`runtime tenant redeploy: ${e.message}`);
40799
40812
  }
40800
40813
  });
40801
- 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) => {
40814
+ 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) => {
40802
40815
  if (o.retire && !o.yes) {
40803
- 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");
40816
+ 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");
40804
40817
  }
40805
40818
  const cfg = await loadConfig();
40806
40819
  const cdeps = registryClientDeps(cfg);
@@ -40853,7 +40866,7 @@ async function buildTenantRuntimeStatusFor(target, stage, cfg) {
40853
40866
  lastTenantDeployRun: (await fetchLastTenantDeployRun(slug, stage)).run
40854
40867
  });
40855
40868
  }
40856
- var project = program2.command("project").description("the DDB org registry ? list/get projects (any member); set is master-only");
40869
+ var project = program2.command("project").description("the DDB org registry \u2014 list/get projects (any member); set is master-only");
40857
40870
  async function projectTarget(commandName, explicitTarget) {
40858
40871
  return requireProjectTarget(commandName, explicitTarget, explicitTarget ? void 0 : await resolveRepo());
40859
40872
  }
@@ -40897,7 +40910,7 @@ project.command("list").description("list all projects (identity + board, never
40897
40910
  console.log(`${p.slug ?? "?"} - ${p.name ?? ""}${p.division ? ` [${p.division}]` : ""}${p.class ? ` (${p.class})` : ""}${p.projectType ? ` <${p.projectType}>` : ""}${p.deployModel ? ` {${p.deployModel}}` : ""}`);
40898
40911
  }
40899
40912
  });
40900
- 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) => {
40913
+ 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) => {
40901
40914
  const cfg = await loadConfig();
40902
40915
  let target;
40903
40916
  try {
@@ -40907,7 +40920,7 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
40907
40920
  }
40908
40921
  const read = await fetchProjectBySlugChecked(slugOf(target), registryClientDeps(cfg));
40909
40922
  if (!read.ok) {
40910
- return failGraceful(`org project get: Hub registry read failed (${read.error}) ? likely transient (cold start, network, or auth blip); retry shortly`);
40923
+ return failGraceful(`org project get: Hub registry read failed (${read.error}) \u2014 likely transient (cold start, network, or auth blip); retry shortly`);
40911
40924
  }
40912
40925
  if (!read.project) {
40913
40926
  return failGraceful(`org project get: no registry META for ${target} (unknown or unbootstrapped)`);
@@ -40917,10 +40930,10 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
40917
40930
  const m = read.project;
40918
40931
  const track = resolveReleaseTrack(m, void 0, target);
40919
40932
  const stages = branchesForTrack(track).join(" -> ");
40920
- const note = track === "direct" ? " (direct ? no rc; /rcand refuses, /release ships development -> main)" : track === "trunk" ? " (trunk ? main only)" : "";
40933
+ const note = track === "direct" ? " (direct \u2014 no rc; /rcand refuses, /release ships development -> main)" : track === "trunk" ? " (trunk \u2014 main only)" : "";
40921
40934
  console.error(
40922
- `${m.name ?? target} ? class ${m.class ?? "?"} ? deploy ${m.deployModel ?? "?"}
40923
- release track: ${track} ? stages: ${stages}${note}
40935
+ `${m.name ?? target} \u2014 class ${m.class ?? "?"} \u2014 deploy ${m.deployModel ?? "?"}
40936
+ release track: ${track} \u2014 stages: ${stages}${note}
40924
40937
  deploys run centrally (tenant-deploy.yml); product repos carry no deploy files. Inspect nonsecret DEPLOY facts with \`mmi-cli oracle org project deploy get\`; full coords remain OIDC-gated.`
40925
40938
  );
40926
40939
  }
@@ -40994,7 +41007,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
40994
41007
  const res = await upsertProject(slug, { ...patch, repo }, registryClientDeps(cfg));
40995
41008
  return reportWrite("org project set", res);
40996
41009
  });
40997
- 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) => {
41010
+ 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) => {
40998
41011
  const cfg = await loadConfig();
40999
41012
  let target;
41000
41013
  try {
@@ -41026,9 +41039,9 @@ project.command("retire [owner/repo]").description("retire an orphaned registry
41026
41039
  const verb = result.applied ? "retired" : "WOULD retire (dry-run; pass --apply to delete)";
41027
41040
  printLine(`org project retire: ${verb} ${result.slug} (${result.repo})`);
41028
41041
  if (result.applied) {
41029
- printLine(` META: ${result.removedMeta.ok ? `removed=${result.removedMeta.removed ?? "unknown"}` : `FAILED ? ${result.removedMeta.error}`}`);
41042
+ printLine(` META: ${result.removedMeta.ok ? `removed=${result.removedMeta.removed ?? "unknown"}` : `FAILED \u2014 ${result.removedMeta.error}`}`);
41030
41043
  } else {
41031
- 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}`}`);
41044
+ 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}`}`);
41032
41045
  }
41033
41046
  if (result.removedBoardItem) {
41034
41047
  const b = result.removedBoardItem;
@@ -41039,7 +41052,7 @@ project.command("retire [owner/repo]").description("retire an orphaned registry
41039
41052
  printLine(` ${result.vaultNote}`);
41040
41053
  }
41041
41054
  if (result.applied && !result.removedMeta.ok) {
41042
- return failGraceful(`org project retire: META delete failed ? ${result.removedMeta.error}`);
41055
+ return failGraceful(`org project retire: META delete failed \u2014 ${result.removedMeta.error}`);
41043
41056
  }
41044
41057
  });
41045
41058
  var fullTrack = program2.command("full-track").description("direct-to-train readiness audits");
@@ -41067,7 +41080,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
41067
41080
  console.log(JSON.stringify(report, null, 2));
41068
41081
  if (!report.rcand.canApply) process.exitCode = 1;
41069
41082
  });
41070
- 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 devops 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) => {
41083
+ 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 devops 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) => {
41071
41084
  const cfg = await loadConfig();
41072
41085
  let target;
41073
41086
  try {
@@ -41093,7 +41106,7 @@ ${filelessTransitionGuide(target, o.stage)}`);
41093
41106
  if (verdict.warn) console.error(`org project set-deploy: ${verdict.warn}`);
41094
41107
  } else if (!o.force) {
41095
41108
  return fail(
41096
- `org project set-deploy: cannot verify ${target} ${branch} from this checkout ? refusing to change noEnvFile. Run from the target repo with origin/${branch} fetched.
41109
+ `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.
41097
41110
  ${filelessTransitionGuide(target, o.stage)}`
41098
41111
  );
41099
41112
  } else {
@@ -41128,14 +41141,14 @@ ${filelessTransitionGuide(target, o.stage)}`
41128
41141
  const res = await setDeployCoords(slug, body, registryClientDeps(cfg));
41129
41142
  return reportWrite("org project set-deploy", res);
41130
41143
  });
41131
- var registry = program2.command("registry").description("the DDB org registry ? org-level constants");
41144
+ var registry = program2.command("registry").description("the DDB org registry \u2014 org-level constants");
41132
41145
  registry.command("org").description("the org config (account id, region, orgProjectId, sagaApiUrl)").option("--json", "machine-readable output").action(async (_o) => {
41133
41146
  const cfg = await loadConfig();
41134
41147
  const org = await fetchOrgConfig(registryClientDeps(cfg));
41135
41148
  if (!org) return failGraceful("org config get: Hub API unreachable, unseeded, or this repo is not bootstrapped");
41136
41149
  console.log(JSON.stringify(org));
41137
41150
  });
41138
- var oauth = program2.command("oauth").description("per-repo Google OAuth ? plan the canonical URI set, verify the client is port-agnostic");
41151
+ var oauth = program2.command("oauth").description("per-repo Google OAuth \u2014 plan the canonical URI set, verify the client is port-agnostic");
41139
41152
  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) => {
41140
41153
  const cfg = await loadConfig();
41141
41154
  const slug = (o.repo ? o.repo.split("/").pop() : cfg.project ?? await repoSlug()).toLowerCase();
@@ -41149,7 +41162,7 @@ oauth.command("plan", { isDefault: true }).description("print the canonical JS o
41149
41162
  return failGraceful(
41150
41163
  `org oauth plan: ${message}. Declare it in the registry META first:
41151
41164
  mmi-cli oracle org project set ${o.repo ?? `mutmutco/${slug}`} --var 'oauth={"subdomains":["${defaultSubdomain(slug)}"],"callbackPath":"${DEFAULT_CALLBACK_PATH}"}'
41152
- New repo? The GCP project and its Google Auth Platform consent screen must exist too ? docs/Guides/oauth-provision.md ? New repo.`
41165
+ New repo? The GCP project and its Google Auth Platform consent screen must exist too \u2014 docs/Guides/oauth-provision.md \u2014 New repo.`
41153
41166
  );
41154
41167
  }
41155
41168
  return failGraceful(`org oauth plan: ${message}`);
@@ -41175,7 +41188,7 @@ SSM cred params (under /mmi-future/${slug}/):`);
41175
41188
  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) => {
41176
41189
  const raw = await readStdin();
41177
41190
  if (!raw.trim()) {
41178
- return fail("org oauth set-creds: pipe the Google client JSON on stdin ? e.g.\n mmi-cli vault org oauth set-creds --repo <owner/repo> < client.json");
41191
+ return fail("org oauth set-creds: pipe the Google client JSON on stdin \u2014 e.g.\n mmi-cli vault org oauth set-creds --repo <owner/repo> < client.json");
41179
41192
  }
41180
41193
  let creds;
41181
41194
  try {
@@ -41225,9 +41238,9 @@ oauth.command("verify").description("probe Google authorize with an arbitrary po
41225
41238
  if (o.json) {
41226
41239
  console.log(JSON.stringify({ slug, redirectUri, portAgnostic: !mismatch }));
41227
41240
  } else if (mismatch) {
41228
- console.error(`FAIL ${slug}: redirect_uri_mismatch for ${redirectUri} ? client is not port-agnostic (run /oauth-provision)`);
41241
+ console.error(`FAIL ${slug}: redirect_uri_mismatch for ${redirectUri} \u2014 client is not port-agnostic (run /oauth-provision)`);
41229
41242
  } else {
41230
- console.log(`PASS ${slug}: ${redirectUri} accepted ? port-agnostic OAuth is live`);
41243
+ console.log(`PASS ${slug}: ${redirectUri} accepted \u2014 port-agnostic OAuth is live`);
41231
41244
  }
41232
41245
  if (mismatch) process.exitCode = 1;
41233
41246
  });
@@ -41240,7 +41253,7 @@ function resolveCreatePriority(raw, command) {
41240
41253
  try {
41241
41254
  return normalizePriority(raw);
41242
41255
  } catch {
41243
- fail(`${command}: unknown priority "${raw}" ? expected one of: ${CLI_PRIORITIES.join(", ")}`, {
41256
+ fail(`${command}: unknown priority "${raw}" \u2014 expected one of: ${CLI_PRIORITIES.join(", ")}`, {
41244
41257
  code: ERROR_CODES.ERR_BAD_ENUM,
41245
41258
  offending_flag: "--priority",
41246
41259
  expected: [...CLI_PRIORITIES]
@@ -41250,15 +41263,25 @@ function resolveCreatePriority(raw, command) {
41250
41263
  function resolveCreateType(raw, command, labels) {
41251
41264
  if (raw === void 0 || raw === "") {
41252
41265
  const nearMiss = labels?.find((l) => ISSUE_TYPES.includes(l));
41253
- const hint = nearMiss ? ` (you passed --label ${nearMiss} ? did you mean --type ${nearMiss}?)` : "";
41266
+ const hint = nearMiss ? ` (you passed --label ${nearMiss} \u2014 did you mean --type ${nearMiss}?)` : "";
41267
+ let correctedCommand;
41268
+ if (nearMiss) {
41269
+ const argv = process.argv.slice(2);
41270
+ const collisionIndex = argv.findIndex((a, i) => a === "--label" && argv[i + 1] === nearMiss);
41271
+ if (collisionIndex !== -1) {
41272
+ const rewritten = argv.slice();
41273
+ rewritten[collisionIndex] = "--type";
41274
+ correctedCommand = `mmi-cli ${canonicalArgvFor(rewritten).join(" ")}`;
41275
+ }
41276
+ }
41254
41277
  fail(`${command}: --type is required (bug | feature | task) unless --batch supplies it per row${hint}`, {
41255
41278
  code: ERROR_CODES.ERR_MISSING_FLAG,
41256
41279
  offending_flag: "--type",
41257
- ...nearMiss ? { corrected_command: `${command} --type ${nearMiss} ?` } : {}
41280
+ ...correctedCommand ? { corrected_command: correctedCommand } : {}
41258
41281
  });
41259
41282
  }
41260
41283
  if (!ISSUE_TYPES.includes(raw)) {
41261
- fail(`${command}: unknown type "${raw}" ? expected one of: ${ISSUE_TYPES.join(", ")}`, {
41284
+ fail(`${command}: unknown type "${raw}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`, {
41262
41285
  code: ERROR_CODES.ERR_BAD_ENUM,
41263
41286
  offending_flag: "--type",
41264
41287
  expected: [...ISSUE_TYPES]
@@ -41272,9 +41295,9 @@ function resolveCreateSurface(opts) {
41272
41295
  function surfaceWaived() {
41273
41296
  return rawFlag("--no-surface");
41274
41297
  }
41275
- var issue = program2.command("issue").description("issues ? reliable create with structured output");
41298
+ var issue = program2.command("issue").description("issues \u2014 reliable create with structured output");
41276
41299
  withExamples(mutating(
41277
- 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"),
41300
+ issue.command("create").description("create an issue (type \u2014 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"),
41278
41301
  // --dry-run/--validate-only plan: resolve the same title source and validate the same type, priority,
41279
41302
  // and surface contract as the real action. A plan that echoes the title-file PATH instead of its value
41280
41303
  // is not a plan of the mutation that will run (#3914).
@@ -41341,7 +41364,7 @@ withExamples(mutating(
41341
41364
  }
41342
41365
  targetRepo2 = await resolveRepo(o.repo);
41343
41366
  if (!targetRepo2) {
41344
- return fail("issue create: could not resolve the target repo ? run inside a git checkout or pass --repo <owner/repo>");
41367
+ return fail("issue create: could not resolve the target repo \u2014 run inside a git checkout or pass --repo <owner/repo>");
41345
41368
  }
41346
41369
  args = buildIssueArgs({
41347
41370
  type: issueType,
@@ -41370,7 +41393,7 @@ withExamples(mutating(
41370
41393
  labels: extraLabels.length ? extraLabels : void 0
41371
41394
  });
41372
41395
  process.stderr.write(
41373
- `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.
41396
+ `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.
41374
41397
  `
41375
41398
  );
41376
41399
  }
@@ -41420,7 +41443,7 @@ async function readParentField(number, repo) {
41420
41443
  }
41421
41444
  return resolveParentField(payload);
41422
41445
  }
41423
- 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) => {
41446
+ 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) => {
41424
41447
  const n = Number(number);
41425
41448
  if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
41426
41449
  const repo = await resolveRepo(o.repo);
@@ -41514,7 +41537,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
41514
41537
  return fail(`issue comment: ${e.message}`);
41515
41538
  }
41516
41539
  const repo = await resolveRepo(parsed.repo ?? o.repo);
41517
- if (!repo) return fail("issue comment: could not resolve repo ? pass --repo owner/repo");
41540
+ if (!repo) return fail("issue comment: could not resolve repo \u2014 pass --repo owner/repo");
41518
41541
  try {
41519
41542
  const result = await postIssueComment(defaultGitHubClient(), { ref, defaultRepo: repo, body });
41520
41543
  console.log(JSON.stringify(result));
@@ -41523,7 +41546,7 @@ jsonParity(issue.command("comment <ref>").description("post a Markdown comment t
41523
41546
  return failGraceful(`issue comment: ${(err.stderr || err.message || String(e)).trim()}`);
41524
41547
  }
41525
41548
  });
41526
- 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) => {
41549
+ 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] \u2014 [ ]) instead of ticking it").option("--repo <owner/repo>", "repo for a bare ref (defaults to the current repo)")).action(async (ref, o) => {
41527
41550
  let parsed;
41528
41551
  try {
41529
41552
  parsed = parseIssueRef(ref);
@@ -41531,7 +41554,7 @@ jsonParity(issue.command("check <ref>").description("tick (or with --off untick)
41531
41554
  return fail(`issue check: ${e.message}`);
41532
41555
  }
41533
41556
  const repo = await resolveRepo(parsed.repo ?? o.repo);
41534
- if (!repo) return fail("issue check: could not resolve repo ? pass --repo owner/repo");
41557
+ if (!repo) return fail("issue check: could not resolve repo \u2014 pass --repo owner/repo");
41535
41558
  const checked = o.off !== true;
41536
41559
  let body;
41537
41560
  try {
@@ -41544,7 +41567,7 @@ jsonParity(issue.command("check <ref>").description("tick (or with --off untick)
41544
41567
  if (!result.ok) {
41545
41568
  if (result.reason === "ambiguous") {
41546
41569
  const list = result.matches.map((m) => ` - ${m.text}`).join("\n");
41547
- return fail(`issue check: "${o.item}" matches ${result.matches.length} checklist items in ${repo}#${parsed.number} ? narrow the text:
41570
+ return fail(`issue check: "${o.item}" matches ${result.matches.length} checklist items in ${repo}#${parsed.number} \u2014 narrow the text:
41548
41571
  ${list}`);
41549
41572
  }
41550
41573
  return fail(`issue check: no checklist item matching "${o.item}" in ${repo}#${parsed.number}`);
@@ -41563,7 +41586,7 @@ ${list}`);
41563
41586
  }
41564
41587
  console.log(JSON.stringify({ number: parsed.number, repo, item: result.item.text, checked, changed: true }));
41565
41588
  });
41566
- 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) => {
41589
+ 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) => {
41567
41590
  let body;
41568
41591
  let priority;
41569
41592
  let title;
@@ -41573,14 +41596,16 @@ program2.command("report").description("file a friction report on the Hub board
41573
41596
  body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises11.readFile, readStdin });
41574
41597
  priority = resolveCreatePriority(o.priority, "report");
41575
41598
  if (!ISSUE_TYPES.includes(o.type)) {
41576
- throw new Error(`unknown issue type "${o.type}" ? expected one of: ${ISSUE_TYPES.join(", ")}`);
41599
+ throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
41577
41600
  }
41578
41601
  } catch (e) {
41579
41602
  const m = e.message;
41580
41603
  const payload = m === "pass --title or --title-file" ? {
41581
41604
  code: ERROR_CODES.ERR_MISSING_FLAG,
41582
41605
  offending_flag: "--title",
41583
- corrected_command: `mmi-cli ${process.argv.slice(2).join(" ")} --title "..."`
41606
+ // #4803: process.argv's house token is already spliced by resolveHouseShim (#4438);
41607
+ // canonicalArgvFor re-prepends it so this stays the runnable house-prefixed form.
41608
+ corrected_command: `mmi-cli ${canonicalArgvFor(process.argv.slice(2)).join(" ")} --title "..."`
41584
41609
  } : void 0;
41585
41610
  return fail(`report: ${m}`, payload);
41586
41611
  }
@@ -41610,7 +41635,7 @@ async function resolvePluginSha() {
41610
41635
  return void 0;
41611
41636
  }
41612
41637
  }
41613
- 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_REPO})`).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) => {
41638
+ 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_REPO})`).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) => {
41614
41639
  const targetRepo2 = o.repo ?? HUB_REPO;
41615
41640
  const sourceRepo = await resolveRepo(void 0);
41616
41641
  const pluginSha = await resolvePluginSha();
@@ -41665,7 +41690,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
41665
41690
  repo: targetRepo2,
41666
41691
  labels: [SKILL_LESSON_LABEL],
41667
41692
  command: "skill-lesson",
41668
- waiver: { reason: "tooling lesson spans product surfaces ? coop-proof class (#3789)" }
41693
+ waiver: { reason: "tooling lesson spans product surfaces \u2014 coop-proof class (#3789)" }
41669
41694
  });
41670
41695
  if (surfaceWarn) process.stderr.write(`${surfaceWarn}
41671
41696
  `);
@@ -41677,7 +41702,7 @@ program2.command("skill-lesson").description("file a skill-lesson on the Hub boa
41677
41702
  const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
41678
41703
  console.log(JSON.stringify({ ...created, deduped: false, label: SKILL_LESSON_LABEL, skill, priority, projectItemId, onBoard }));
41679
41704
  });
41680
- var pr = program2.command("pr").description("pull requests ? reliable create with structured output");
41705
+ var pr = program2.command("pr").description("pull requests \u2014 reliable create with structured output");
41681
41706
  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) => {
41682
41707
  let body;
41683
41708
  let title;
@@ -41691,7 +41716,7 @@ withExamples(pr.command("create").description("create a PR and print {number,url
41691
41716
  body = rewriteNegatedClosingPhrases(body);
41692
41717
  const docsCheck = await checkDocsIndexAtHead({ repo: o.repo, head: o.head }, createPrCreateDocsIndexDeps());
41693
41718
  if (docsCheck && !docsCheck.ok) {
41694
- return fail(`pr create: ${docsCheck.detail} ? ${docsCheck.fix}`);
41719
+ return fail(`pr create: ${docsCheck.detail} \u2014 ${docsCheck.fix}`);
41695
41720
  }
41696
41721
  const created = await ghCreate(buildPrArgs({ title, body, base: o.base, head: o.head, repo: o.repo, draft: o.draft }));
41697
41722
  invalidateStatuslineBoardCache();
@@ -41704,7 +41729,7 @@ withExamples(pr.command("create").description("create a PR and print {number,url
41704
41729
  "Use --body-file for multiline PR bodies instead of shell-escaped inline markdown.",
41705
41730
  "Write that file under .jerv/ inside a worktree (#4405): any other untracked path makes `worktree land` refuse cleanup as untracked-files."
41706
41731
  ]);
41707
- 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) => {
41732
+ 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) => {
41708
41733
  const n = Number(number);
41709
41734
  if (!Number.isInteger(n) || n <= 0) return fail("pr view: <number> must be a positive integer");
41710
41735
  const repo = await resolveRepo(o.repo);
@@ -41814,7 +41839,7 @@ pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs
41814
41839
  if (o.json) return printLine(JSON.stringify(result));
41815
41840
  printLine(`merge CI policy: ${result.policy} (${result.reason})`);
41816
41841
  });
41817
- 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) => {
41842
+ 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) => {
41818
41843
  let timeoutMs;
41819
41844
  if (o.timeout !== void 0) {
41820
41845
  const minutes = Number(o.timeout);
@@ -41844,27 +41869,27 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
41844
41869
  timeoutMs,
41845
41870
  // Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
41846
41871
  // an agent harness kills it on its own (shorter) deadline before the verdict ever prints (#2940).
41847
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr checks-wait: ${state} ? ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
41872
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr checks-wait: ${state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
41848
41873
  });
41849
41874
  if (o.json) printLine(JSON.stringify(result));
41850
41875
  else if (result.status === "conflicting") {
41851
- printLine(`pr checks-wait: conflicting ? ${result.reason}`);
41876
+ printLine(`pr checks-wait: conflicting \u2014 ${result.reason}`);
41852
41877
  } else if (result.status === "timeout") {
41853
41878
  const stuckQueuing = /no-checks-reported/i.test(result.detail ?? "");
41854
41879
  printLine(
41855
- `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." : "")
41880
+ `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>.` + (stuckQueuing ? " Tip: jobs never appeared \u2014 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." : "")
41856
41881
  );
41857
41882
  } else if (result.status === "rate-limited") {
41858
- printLine(`pr checks-wait: rate-limited ? ${result.reason ?? "REST pool below floor"}. No check failed; re-run after the pool resets.`);
41883
+ printLine(`pr checks-wait: rate-limited \u2014 ${result.reason ?? "REST pool below floor"}. No check failed; re-run after the pool resets.`);
41859
41884
  } else if (result.detail === "runner-infra") {
41860
- printLine(`pr checks-wait: failure (runner infrastructure, NOT a test failure) ? ${result.reason}`);
41885
+ printLine(`pr checks-wait: failure (runner infrastructure, NOT a test failure) \u2014 ${result.reason}`);
41861
41886
  } else if (result.detail === "stale-head") {
41862
- printLine(`pr checks-wait: failure (stale PR head, NOT a test failure) ? ${result.reason}`);
41863
- } else printLine(`pr checks-wait: ${result.status}${result.reason ? ` ? ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
41887
+ printLine(`pr checks-wait: failure (stale PR head, NOT a test failure) \u2014 ${result.reason}`);
41888
+ } else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
41864
41889
  if (result.status === "failure" || result.status === "conflicting") process.exitCode = 1;
41865
41890
  if (result.status === "timeout" || result.status === "rate-limited") process.exitCode = PR_CHECKS_TIMEOUT_EXIT_CODE;
41866
41891
  });
41867
- 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 (#3718) or ambiguous-cross-repo-closing (#4279) refusal").action(async (number, o) => {
41892
+ pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 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 (#3718) or ambiguous-cross-repo-closing (#4279) refusal").action(async (number, o) => {
41868
41893
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
41869
41894
  const startingPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
41870
41895
  assertPrMergeHousekeepingClean(startingPath || process.cwd(), "pr land", { force: o.force });
@@ -41898,8 +41923,8 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
41898
41923
  } catch {
41899
41924
  }
41900
41925
  if (isPromotionBase(base, track)) {
41901
- const shape = track ? `${track} track` : "unresolved track ? strict reading";
41902
- throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) ? promotion merges stay human-only`);
41926
+ const shape = track ? `${track} track` : "unresolved track \u2014 strict reading";
41927
+ throw new Error(`pr land: base branch ${base} is a promotion target (${shape}) \u2014 promotion merges stay human-only`);
41903
41928
  }
41904
41929
  return repo;
41905
41930
  },
@@ -41909,26 +41934,26 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
41909
41934
  const requiredContexts = await fetchRequiredCheckContexts(repo, "development").catch(() => null);
41910
41935
  return waitForPrChecks({
41911
41936
  resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
41912
- // #3024: REST-only wait loop ? runPrLand's resolveRepo already guarantees `repo` is set.
41937
+ // #3024: REST-only wait loop runPrLand's resolveRepo already guarantees `repo` is set.
41913
41938
  pollChecks: () => pollRestPrChecks(prNumber, repo, void 0, requiredContexts),
41914
41939
  // #2970: `pr land` only ever lands PRs based on development (resolveRepo above already rejects any
41915
41940
  // other base), so the fast-fail message's base branch is always 'development' here.
41916
41941
  pollMergeable: () => pollRestPrMergeable(prNumber, repo),
41917
41942
  pollRateLimit: () => waitLoopCorePool("pr land"),
41918
- // #3388: `pr land` is the batch path ? the one most likely to self-DOS the shared runner and
41943
+ // #3388: `pr land` is the batch path the one most likely to self-DOS the shared runner and
41919
41944
  // then read its own wall-clock kill as a broken diff.
41920
41945
  diagnoseFailure: () => waitLoopDiagnosis("pr land", prNumber, repo),
41921
41946
  baseBranch: "development",
41922
41947
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
41923
41948
  log: (message) => console.warn(message),
41924
- // `pr land` inherits the same (raised) checks budget, so it needs the same liveness ? otherwise the
41949
+ // `pr land` inherits the same (raised) checks budget, so it needs the same liveness otherwise the
41925
41950
  // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
41926
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr land: waiting on checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
41951
+ 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`)
41927
41952
  });
41928
41953
  },
41929
- // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying ? a
41954
+ // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying a
41930
41955
  // fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
41931
- // #4396: same required-status scoping as the wait loop above ? a non-required red must not read
41956
+ // #4396: same required-status scoping as the wait loop above a non-required red must not read
41932
41957
  // as "not ready to retry" any more than it should have blocked the wait itself.
41933
41958
  probeMergeReady: async (prNumber, repo) => {
41934
41959
  const [snapshot, requiredContexts] = await Promise.all([
@@ -41958,7 +41983,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
41958
41983
  if (read.state === "merged") return true;
41959
41984
  if (read.state === "failed") {
41960
41985
  lastFailure = read.error;
41961
- console.warn(`pr land: merged-state read FAILED (${read.error}) ? retrying; this is not evidence the PR is unmerged`);
41986
+ console.warn(`pr land: merged-state read FAILED (${read.error}) \u2014 retrying; this is not evidence the PR is unmerged`);
41962
41987
  } else {
41963
41988
  lastFailure = void 0;
41964
41989
  }
@@ -41966,7 +41991,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
41966
41991
  }
41967
41992
  if (lastFailure) {
41968
41993
  throw new Error(
41969
- `pr land: could not verify whether PR #${prNumber} merged ? the last merged-state read FAILED (${lastFailure}). The auto-merge enqueue stands; read the PR directly (gh pr view ${prNumber} --repo ${repo} --json merged,state) and rerun cleanup if it merged.`
41994
+ `pr land: could not verify whether PR #${prNumber} merged \u2014 the last merged-state read FAILED (${lastFailure}). The auto-merge enqueue stands; read the PR directly (gh pr view ${prNumber} --repo ${repo} --json merged,state) and rerun cleanup if it merged.`
41970
41995
  );
41971
41996
  }
41972
41997
  return false;
@@ -41974,7 +41999,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
41974
41999
  });
41975
42000
  if (result.status !== "failed") {
41976
42001
  const repoFlag = result.repo ? ` --repo ${result.repo}` : "";
41977
- console.warn(`pr land: merge confirmed; cleanup pending ? if this process is interrupted, resume with: mmi-cli devops pr merge ${number}${repoFlag} --squash`);
42002
+ console.warn(`pr land: merge confirmed; cleanup pending \u2014 if this process is interrupted, resume with: mmi-cli devops pr merge ${number}${repoFlag} --squash`);
41978
42003
  }
41979
42004
  if (result.status !== "failed") {
41980
42005
  try {
@@ -42005,13 +42030,13 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
42005
42030
  }
42006
42031
  if (o.json) printLine(JSON.stringify(result));
42007
42032
  else {
42008
- printLine(`pr land: ${result.status}${result.error ? ` ? ${result.error}` : ""}`);
42033
+ printLine(`pr land: ${result.status}${result.error ? ` \u2014 ${result.error}` : ""}`);
42009
42034
  for (const line of renderPrLandCleanupLines(result.cleanup)) printLine(line);
42010
42035
  if (result.cleanupError) printLine(`pr land cleanup: ${result.cleanupError}`);
42011
42036
  }
42012
42037
  if (result.status === "failed" || result.cleanupError) process.exitCode = 1;
42013
42038
  });
42014
- 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 (#3718) or ambiguous-cross-repo-closing (#4279) refusal")).action(async (number, o) => {
42039
+ 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 (#3718) or ambiguous-cross-repo-closing (#4279) refusal")).action(async (number, o) => {
42015
42040
  const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
42016
42041
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
42017
42042
  const repoForPostCleanup = await resolveRepo(o.repo) ?? o.repo;
@@ -42064,10 +42089,10 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42064
42089
  baseBranch,
42065
42090
  sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
42066
42091
  log: (message) => console.warn(message),
42067
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
42092
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
42068
42093
  });
42069
42094
  if (wait.status !== "success" && wait.status !== "skipped") {
42070
- console.warn(`pr merge: --wait stopped before merge ? ${wait.status}${wait.reason ? `: ${wait.reason}` : ""}${wait.detail ? ` (${wait.detail})` : ""}`);
42095
+ console.warn(`pr merge: --wait stopped before merge \u2014 ${wait.status}${wait.reason ? `: ${wait.reason}` : ""}${wait.detail ? ` (${wait.detail})` : ""}`);
42071
42096
  process.exitCode = wait.status === "timeout" || wait.status === "rate-limited" ? PR_CHECKS_TIMEOUT_EXIT_CODE : 1;
42072
42097
  return;
42073
42098
  }
@@ -42123,7 +42148,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42123
42148
  return;
42124
42149
  }
42125
42150
  if (!o.auto && basePolicyBlocksImmediateMerge(message)) {
42126
- console.warn(`pr merge: the base-branch policy blocks an immediate merge ? upgrading to --auto (merges once required checks pass).`);
42151
+ console.warn(`pr merge: the base-branch policy blocks an immediate merge \u2014 upgrading to --auto (merges once required checks pass).`);
42127
42152
  upgradedToAuto = true;
42128
42153
  await execFileP2("gh", buildPrMergeArgs({ number, repoArgs, method, auto: true, deleteBranch: !headIsProtected, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
42129
42154
  const m2 = String(e2.message || "");
@@ -42158,7 +42183,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42158
42183
  const state = stateRead.ok ? stateRead.state : "";
42159
42184
  const enqueued = describePrMergeEnqueuedReason(await pollGhPrChecks(number, repoArgs).catch(() => void 0));
42160
42185
  console.log(JSON.stringify({ mergeStatus: "auto-merge-enqueued", enqueuedReason: enqueued.reason, pr: number, branch: headRef, state: state || "unknown", upgradedToAuto: upgradedToAuto || void 0 }));
42161
- console.warn(`pr merge: PR #${number} is ENQUEUED, not merged ? ${enqueued.message}.`);
42186
+ console.warn(`pr merge: PR #${number} is ENQUEUED, not merged \u2014 ${enqueued.message}.`);
42162
42187
  if (upgradedToAuto && !o.auto) {
42163
42188
  console.warn(`pr merge: exiting ${PR_MERGE_ENQUEUED_EXIT_CODE} so a chained command does not treat this as a completed merge.`);
42164
42189
  process.exitCode = PR_MERGE_ENQUEUED_EXIT_CODE;
@@ -42174,7 +42199,7 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
42174
42199
  state: stateRead.ok ? stateRead.state : "unknown",
42175
42200
  cleanupStatus: "skipped"
42176
42201
  }));
42177
- console.error(`pr merge: ${gate.message}. Nothing was deleted ? re-check the PR and retry.`);
42202
+ console.error(`pr merge: ${gate.message}. Nothing was deleted \u2014 re-check the PR and retry.`);
42178
42203
  process.exitCode = 1;
42179
42204
  return;
42180
42205
  }
@@ -42277,7 +42302,7 @@ function trainApplyDeps() {
42277
42302
  const verdict = await fetchTrainAuthority(repo, registryClientDeps(await loadConfig()));
42278
42303
  return verdict.ok ? { ok: true, role: verdict.authority.role, train: verdict.authority.train } : verdict;
42279
42304
  },
42280
- // Hub-App-authority dispatch of the central tenant deploy (#953) ? the Hub fires the
42305
+ // Hub-App-authority dispatch of the central tenant deploy (#953) the Hub fires the
42281
42306
  // workflow_dispatch with its App token, so the caller needs no MMI-Hub Actions write.
42282
42307
  dispatchTenantDeploy: async ({ repo, stage, ref }) => {
42283
42308
  const res = await tenantDeploy({ repo, stage, ref }, registryClientDeps(await loadConfig()));
@@ -42286,7 +42311,7 @@ function trainApplyDeps() {
42286
42311
  throw new Error(`tenant deploy dispatch failed: ${detail}`);
42287
42312
  }
42288
42313
  },
42289
- // Hub-App-authority dispatch of the central tenant-control.yml (#1717) ? the Hub fires the
42314
+ // Hub-App-authority dispatch of the central tenant-control.yml (#1717) the Hub fires the
42290
42315
  // workflow_dispatch with its App token. Never throws for an expected rejection: it returns the dispatch
42291
42316
  // outcome so runTenantControl can map a 5xx (transport-failed, retryable) vs a 4xx (rejected) vs ok.
42292
42317
  dispatchTenantControl: async ({ repo, stage, action, lines }) => {
@@ -42302,7 +42327,7 @@ function trainApplyDeps() {
42302
42327
  return { ok: false, category: body?.category, error: body?.error ?? res.error };
42303
42328
  },
42304
42329
  // Hotfix-coverage guard (#958): runs against the local clone via real git. manifestPaths exempts the
42305
- // release version fold (#976) ? a main-only commit touching ONLY the root package manifest is the
42330
+ // release version fold (#976) a main-only commit touching ONLY the root package manifest is the
42306
42331
  // fold's version metadata, which the candidate replaces with its own. (The Hub's wider distribution
42307
42332
  // set never reaches this guard: the Hub is direct-track.)
42308
42333
  hotfixCoverage: (input) => checkHotfixCoverage({ ...input, manifestPaths: ["package.json", "package-lock.json"] }),
@@ -42318,14 +42343,14 @@ function trainApplyDeps() {
42318
42343
  removeFile: (path2) => (0, import_promises11.unlink)(path2)
42319
42344
  }, args),
42320
42345
  // #4713 (I/O-boundary census): `null` used to mean BOTH "this project configures no edge domains"
42321
- // (a real answer) and "the registry read missed" ? so a release verdict printed an environments block
42346
+ // (a real answer) and "the registry read missed" so a release verdict printed an environments block
42322
42347
  // with no domains as though the project had none. The checked read separates them: an absent project
42323
42348
  // or an absent edgeDomains block still answers null; a FAILED read refuses by name, and the caller's
42324
42349
  // own best-effort catch degrades the block instead of the read pretending it saw an answer.
42325
42350
  fetchEdgeDomains: async (slug) => {
42326
42351
  const read = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
42327
42352
  if (!read.ok) {
42328
- const message = `release environments: Hub registry read FAILED for ${slug} (${read.error}) ? edge domains are UNREAD, not absent`;
42353
+ const message = `release environments: Hub registry read FAILED for ${slug} (${read.error}) \u2014 edge domains are UNREAD, not absent`;
42329
42354
  consoleIo.err(`mmi-cli: ${message}`);
42330
42355
  throw new Error(message);
42331
42356
  }
@@ -42347,13 +42372,13 @@ function renderDeployLine(d) {
42347
42372
  else if (d.runUrl) parts.push(`run ${d.runUrl}`);
42348
42373
  if (d.deployStatus === "success") parts.push("deploy: SUCCEEDED");
42349
42374
  else if (d.deployStatus === "failure") parts.push("deploy: FAILED (promotion stands; retry the deploy, do not re-tag)");
42350
- else if (d.runId != null) parts.push(`deploy: UNVERIFIED ? dispatched, not resolved (watch: gh run watch ${d.runId} --repo mutmutco/MMI-Hub --exit-status)`);
42351
- 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)");
42352
- else parts.push("deploy: UNVERIFIED ? no run correlated or watched; resolve every workflow run on the release SHA before calling this release healthy (#3322)");
42375
+ 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)`);
42376
+ 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)");
42377
+ 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)");
42353
42378
  return parts.join("; ");
42354
42379
  }
42355
42380
  function renderReleaseResume(r) {
42356
- const lines = [`mmi-cli devops release --resume: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}] ? ${r.note}`];
42381
+ const lines = [`mmi-cli devops release --resume: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}] \u2014 ${r.note}`];
42357
42382
  for (const step of r.steps) lines.push(` - ${step}`);
42358
42383
  if (r.releaseUrl) lines.push(` release: ${r.releaseUrl}`);
42359
42384
  if (r.announceNote) lines.push(` announce: ${r.announceNote}`);
@@ -42363,23 +42388,23 @@ function renderReleaseResume(r) {
42363
42388
  return lines.join("\n");
42364
42389
  }
42365
42390
  function renderReleaseAbort(r) {
42366
- return `mmi-cli devops release --abort --apply: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) ? ${r.note}`;
42391
+ return `mmi-cli devops release --abort --apply: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) \u2014 ${r.note}`;
42367
42392
  }
42368
42393
  function renderReleasePublishRetry(r) {
42369
- return `mmi-cli devops release --retry-publish: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) ? ${r.note}; ${r.runUrl}`;
42394
+ return `mmi-cli devops release --retry-publish: ${r.repo} ${r.tag} (${r.tagSha.slice(0, 12)}) \u2014 ${r.note}; ${r.runUrl}`;
42370
42395
  }
42371
42396
  function renderRcandResume(r) {
42372
- return `mmi-cli devops rcand --resume: promoted ${r.repo} ? rc at ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}]; ${renderDeployLine(r)}; ${r.note}`;
42397
+ return `mmi-cli devops rcand --resume: promoted ${r.repo} \u2014 rc at ${r.tag} (${r.tagSha.slice(0, 12)}) [${r.deployModel}]; ${renderDeployLine(r)}; ${r.note}`;
42373
42398
  }
42374
42399
  function renderAlignment(label, alignment) {
42375
42400
  if (alignment.status !== "pr-pending") return `${label}: ${alignment.note}`;
42376
42401
  if (alignment.autoMergeEnqueued) {
42377
42402
  return `${label}: alignment PR #${alignment.prNumber ?? "?"} AUTO-MERGE ENQUEUED (merges when checks pass)${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
42378
42403
  }
42379
- return `${label}: ALIGNMENT PR PENDING ? land it with \`mmi-cli devops pr merge ${alignment.prNumber ?? "<number>"} --auto --merge\`${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
42404
+ return `${label}: ALIGNMENT PR PENDING \u2014 land it with \`mmi-cli devops pr merge ${alignment.prNumber ?? "<number>"} --auto --merge\`${alignment.prUrl ? ` (${alignment.prUrl})` : ""}`;
42380
42405
  }
42381
42406
  function renderTrainApply(commandName, r) {
42382
- let base = `mmi-cli ${commandName}: promoted ${r.repo} ? ${r.stage} at ${r.tag} [${r.deployModel}]; ${renderDeployLine(r)}`;
42407
+ let base = `mmi-cli ${commandName}: promoted ${r.repo} \u2014 ${r.stage} at ${r.tag} [${r.deployModel}]; ${renderDeployLine(r)}`;
42383
42408
  if (r.versionFold) base = `${base}; ${r.versionFold}`;
42384
42409
  if (r.resumeNote) base = `${base}; ${r.resumeNote}`;
42385
42410
  if (r.devNote) base = `${base}; ${r.devNote}`;
@@ -42442,16 +42467,16 @@ for (const commandName of ["rcand", "release"]) {
42442
42467
  return fail(`${commandName}: ${e.message}`);
42443
42468
  }
42444
42469
  if (o.ack && commandName !== "release") {
42445
- return fail(`${commandName}: --ack applies only to release ? it overrides the rc -> main hotfix-coverage guard, which rcand does not run. Run: mmi-cli devops release --ack <shas>`);
42470
+ 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 devops release --ack <shas>`);
42446
42471
  }
42447
42472
  if (o.dev && commandName !== "release") {
42448
- return fail(`${commandName}: --dev applies only to release ? it ships development -> main skipping rc, which rcand cannot do. Run: mmi-cli devops release --dev`);
42473
+ return fail(`${commandName}: --dev applies only to release \u2014 it ships development -> main skipping rc, which rcand cannot do. Run: mmi-cli devops release --dev`);
42449
42474
  }
42450
42475
  if (o.announceSummaryFile && commandName !== "release") {
42451
- return fail(`${commandName}: --announce-summary-file applies only to release ? rcand posts no Hub Slack announcement. Run: mmi-cli devops release --announce-summary-file <path>`);
42476
+ return fail(`${commandName}: --announce-summary-file applies only to release \u2014 rcand posts no Hub Slack announcement. Run: mmi-cli devops release --announce-summary-file <path>`);
42452
42477
  }
42453
42478
  if (o.abort && commandName !== "release") {
42454
- return fail(`${commandName}: --abort applies only to release ? it rolls back a proven unpublished Hub release tag. Run: mmi-cli devops release --abort --apply`);
42479
+ return fail(`${commandName}: --abort applies only to release \u2014 it rolls back a proven unpublished Hub release tag. Run: mmi-cli devops release --abort --apply`);
42455
42480
  }
42456
42481
  if (o.retryPublish && commandName !== "release") {
42457
42482
  return fail(`${commandName}: --retry-publish applies only to release. Run: mmi-cli devops release --retry-publish <run-id> --apply --watch`);
@@ -42481,7 +42506,7 @@ for (const commandName of ["rcand", "release"]) {
42481
42506
  }
42482
42507
  }
42483
42508
  if (o.abort) {
42484
- if (o.resume) return fail("release: --abort and --resume are mutually exclusive ? abort removes an unpublished tag while resume preserves and promotes it");
42509
+ if (o.resume) return fail("release: --abort and --resume are mutually exclusive \u2014 abort removes an unpublished tag while resume preserves and promotes it");
42485
42510
  if (!o.apply) return fail("release: --abort requires --apply after explicit approval; nothing was written");
42486
42511
  if (o.watch || o.announceSummaryFile || o.ack || o.dev) {
42487
42512
  return fail("release: --abort accepts only --apply, --repo and --json; promotion flags cannot be combined with rollback");
@@ -42498,7 +42523,7 @@ for (const commandName of ["rcand", "release"]) {
42498
42523
  }
42499
42524
  }
42500
42525
  if (o.resume) {
42501
- 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`);
42526
+ 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`);
42502
42527
  try {
42503
42528
  if (commandName === "rcand") {
42504
42529
  const result2 = await runRcandResume(trainApplyDeps(), { watch: o.watch });
@@ -42554,7 +42579,7 @@ for (const commandName of ["rcand", "release"]) {
42554
42579
  projectInfoSync = await runProjectInfoSync(result.repo, true);
42555
42580
  } catch (e) {
42556
42581
  const error = e.message;
42557
- projectInfoSync = { applied: false, note: `FAILED ? ${error}`, error };
42582
+ projectInfoSync = { applied: false, note: `FAILED \u2014 ${error}`, error };
42558
42583
  }
42559
42584
  }
42560
42585
  const alignmentPending = result.devRollForward?.status === "pr-pending" || result.rcAlignment?.status === "pr-pending";
@@ -42596,7 +42621,7 @@ for (const commandName of ["rcand", "release"]) {
42596
42621
  }
42597
42622
  if (!read.ok) {
42598
42623
  return failGraceful(
42599
- `${commandName}: Hub registry read failed for ${slugOf(repo)} (${read.error}) ? the release track behind this plan is unproven, and a dry-run plan built on the isHub fallback would name the wrong branches. Likely transient (cold start, network, or auth blip); retry shortly.`
42624
+ `${commandName}: Hub registry read failed for ${slugOf(repo)} (${read.error}) \u2014 the release track behind this plan is unproven, and a dry-run plan built on the isHub fallback would name the wrong branches. Likely transient (cold start, network, or auth blip); retry shortly.`
42600
42625
  );
42601
42626
  }
42602
42627
  const raw = read.project?.releaseTrack;
@@ -42622,13 +42647,13 @@ function renderHotfixRelease(r) {
42622
42647
  ` - ${r.verifyNote}`,
42623
42648
  ...r.announceNote ? [` - announce: ${r.announceNote}`] : [],
42624
42649
  ` - fold: ${r.foldNote}`,
42625
- ` - next: mmi-cli devops hotfix status ${r.tag} (no back-merge of the FIX ? development already has it; the version fold above is ported for you, #4410)`
42650
+ ` - next: mmi-cli devops hotfix status ${r.tag} (no back-merge of the FIX \u2014 development already has it; the version fold above is ported for you, #4410)`
42626
42651
  ].join("\n");
42627
42652
  }
42628
42653
  function renderHotfixStatus(r) {
42629
42654
  return [
42630
- `mmi-cli devops hotfix status: ${r.tag} on ${r.repo} ? ${r.state}`,
42631
- ` - 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"}`,
42655
+ `mmi-cli devops hotfix status: ${r.tag} on ${r.repo} \u2014 ${r.state}`,
42656
+ ` - branch: ${r.branchExists ? "pushed" : "absent"} \u2014 PR: ${r.pr ? `#${r.pr.number} ${r.pr.state}` : "none"} \u2014 tag: ${r.tagPushed ? "pushed" : "absent"} \u2014 Release: ${r.releaseExists ? "exists" : "absent"}`,
42632
42657
  ...r.runs.map((run) => ` - ${run.workflow}: ${run.conclusion}${run.url ? ` (${run.url})` : ""}`),
42633
42658
  ` - npm @mutmutco/cli: ${r.npmVersion}`,
42634
42659
  ` - next: ${r.next}`,
@@ -42658,13 +42683,13 @@ async function runHotfixSub(sub, body, json, render) {
42658
42683
  return failGraceful(`hotfix ${sub}: ${e.message}`);
42659
42684
  }
42660
42685
  }
42661
- 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) => {
42686
+ var hotfixCmd = program2.command("hotfix").description("stepwise hotfix orchestrator: start \u2014 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) => {
42662
42687
  try {
42663
42688
  await requireFreshTrainCli("hotfix");
42664
42689
  } catch (e) {
42665
42690
  return fail(`hotfix: ${e.message}`);
42666
42691
  }
42667
- if (o.apply) return fail("hotfix: use the stepwise subcommands ? mmi-cli devops hotfix start --from <pr#|sha> ? release <vX.Y.Z> ? status [vX.Y.Z]");
42692
+ if (o.apply) return fail("hotfix: use the stepwise subcommands \u2014 mmi-cli devops hotfix start --from <pr#|sha> \u2014 release <vX.Y.Z> \u2014 status [vX.Y.Z]");
42668
42693
  const steps = trainPlan("hotfix");
42669
42694
  console.log(o.json ? JSON.stringify({ command: "hotfix", steps }, null, 2) : renderSteps("mmi-cli devops hotfix: dry-run plan", steps));
42670
42695
  });
@@ -42696,7 +42721,7 @@ ci.command("audit").description("read-only fleet scan: gate workflow, ruleset co
42696
42721
  else console.log(renderCiAuditText(report));
42697
42722
  if (!report.ok) process.exitCode = 1;
42698
42723
  });
42699
- 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) => {
42724
+ 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) => {
42700
42725
  if (o.apply && o.parkRuleset) {
42701
42726
  return fail("ci reconcile: --apply and --park-ruleset ask for opposite things; pass one");
42702
42727
  }
@@ -42719,7 +42744,7 @@ ci.command("reconcile").description("audit + optionally apply merge settings and
42719
42744
  ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${r.errors.length ? ` errors=[${r.errors.join("; ")}]` : ""}`);
42720
42745
  }
42721
42746
  } else {
42722
- console.log("\nDry-run ? re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
42747
+ console.log("\nDry-run \u2014 re-run with --apply to patch merge settings and activate product rulesets (master-admin).");
42723
42748
  }
42724
42749
  }
42725
42750
  const applyFailed = applyResults.some((result) => result.errors.length > 0 || result.postApply?.state === "failed");
@@ -42747,7 +42772,7 @@ access.command("role [repo]").description("D14 train authority for a repo (serve
42747
42772
  if (!a.train) process.exitCode = 1;
42748
42773
  return;
42749
42774
  }
42750
- console.log(`${a.repo}: @${a.login} is ${a.role} ? train ${a.train ? "AUTHORIZED" : "not authorized"}${a.hubTrainMasterOnly ? " (Hub train is master-only)" : ""}`);
42775
+ console.log(`${a.repo}: @${a.login} is ${a.role} \u2014 train ${a.train ? "AUTHORIZED" : "not authorized"}${a.hubTrainMasterOnly ? " (Hub train is master-only)" : ""}`);
42751
42776
  if (!a.train) process.exitCode = 1;
42752
42777
  });
42753
42778
  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 () => {
@@ -42782,9 +42807,9 @@ access.command("audit").description("audit collaborator roles + train-branch pus
42782
42807
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
42783
42808
  if (!report.ok) process.exitCode = 1;
42784
42809
  });
42785
- 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)));
42810
+ 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)));
42786
42811
  var isWin2 = process.platform === "win32";
42787
- 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", "read-only fast gate for automation: measures and reports with the shared exit code, zero writes (#4199, canon); heal env drift with a plain run or --no-repo-writes").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) => {
42812
+ 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", "read-only fast gate for automation: measures and reports with the shared exit code, zero writes (#4199, canon); heal env drift with a plain run or --no-repo-writes").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) => {
42788
42813
  if (opts.guide) {
42789
42814
  consoleIo.log("MMI Agentic Onboarding: docs/Architecture/agentic-dev-environment.md");
42790
42815
  return;
@@ -42938,7 +42963,7 @@ program2.command("plugin-release-catchup").description("install a newer released
42938
42963
  });
42939
42964
  program2.command("session-start").description("run the SessionStart verbs (whoami, board slice, doctor) in one process").action(async () => {
42940
42965
  if (isInsideRepoSubdir(process.cwd())) {
42941
- 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.");
42966
+ 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.");
42942
42967
  appendHookActivity(process.cwd(), { event: "SessionStart", script: "session-start", outcome: "ran", action: "skip (repo subdirectory)" });
42943
42968
  return;
42944
42969
  }
@@ -42951,7 +42976,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
42951
42976
  const bannerIo = measured.io;
42952
42977
  const { parallel, sequential } = buildSessionStartPlan({
42953
42978
  // whoami (#879): surface the resolved human so agents act --for them without asking. Silent
42954
- // when unknown ? a missing gh login must not noise or fail the banner.
42979
+ // when unknown a missing gh login must not noise or fail the banner.
42955
42980
  whoami: async (io) => {
42956
42981
  const report = await resolveWhoami({
42957
42982
  hubSession: async () => hubAuthSession({ baseUrl: (await loadConfig()).sagaApiUrl ?? defaultHubUrl(), githubToken }),
@@ -42963,14 +42988,14 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
42963
42988
  if (authority) io.log(authority);
42964
42989
  },
42965
42990
  // command-ladder hint (#2609): compact gh?mmi-cli cheat sheet so agents reach for mmi-cli first,
42966
- // not after a denied gh call. <1KB, pure in-memory ? no network, no fs, fail-soft.
42991
+ // not after a denied gh call. <1KB, pure in-memory no network, no fs, fail-soft.
42967
42992
  commandLadderHint: async (io) => {
42968
42993
  const hint = commandLadderHint();
42969
42994
  if (hint) io.log(hint);
42970
42995
  },
42971
42996
  // stale worktrees: fast LOCAL-only git check (worktree list + branch -vv gone status). Flags leaked
42972
42997
  // worktrees / merged-but-undeleted branches so the session never starts atop leftover state. No
42973
- // network ? never the ~20s `gh pr list` path of `worktree list --stale`. Fail-soft.
42998
+ // network never the ~20s `gh pr list` path of `worktree list --stale`. Fail-soft.
42974
42999
  staleWorktrees: async (io) => {
42975
43000
  const line = await gatherStaleWorktreeWarning();
42976
43001
  if (line) io.log(line);
@@ -42983,7 +43008,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
42983
43008
  const line = localTrainSyncBannerLine(result);
42984
43009
  if (line) io.log(line);
42985
43010
  },
42986
- // #3485 item 7: the ONLY lane that throttles the npm read ? it runs on every session start, on a
43011
+ // #3485 item 7: the ONLY lane that throttles the npm read it runs on every session start, on a
42987
43012
  // blocking hook. Every interactive lane still reads live.
42988
43013
  doctor: async (io) => {
42989
43014
  await runDoctorClean({ banner: true }, io, mmiDoctorDeps({ throttleReleasedRead: true, program: program2 }));