@mutmutco/cli 3.3.0 → 3.4.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 +641 -440
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -3408,7 +3408,7 @@ var program = new Command();
3408
3408
 
3409
3409
  // src/index.ts
3410
3410
  var import_promises3 = require("node:fs/promises");
3411
- var import_node_fs18 = require("node:fs");
3411
+ var import_node_fs19 = require("node:fs");
3412
3412
 
3413
3413
  // src/bootstrap-org-ruleset.ts
3414
3414
  var ORG_NO_AGENT_FILES_RULESET_NAME = "mmi-no-agent-files-org";
@@ -3502,7 +3502,7 @@ async function fetchOrgNoAgentFilesRuleset(client, org = ORG_LOGIN) {
3502
3502
  }
3503
3503
 
3504
3504
  // src/index.ts
3505
- var import_node_child_process11 = require("node:child_process");
3505
+ var import_node_child_process12 = require("node:child_process");
3506
3506
 
3507
3507
  // src/cli-shared.ts
3508
3508
  var import_node_child_process3 = require("node:child_process");
@@ -3605,7 +3605,7 @@ function hardExit(code) {
3605
3605
  async function cleanExit(code) {
3606
3606
  process.exitCode = code;
3607
3607
  await closeHttpPool();
3608
- await new Promise((resolve6) => setImmediate(resolve6));
3608
+ await new Promise((resolve5) => setImmediate(resolve5));
3609
3609
  process.exit(code);
3610
3610
  }
3611
3611
  async function failGraceful(msg) {
@@ -3770,7 +3770,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
3770
3770
  const attempts = opts.attempts ?? 3;
3771
3771
  const baseDelayMs = opts.baseDelayMs ?? 250;
3772
3772
  const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
3773
- const sleep = opts.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
3773
+ const sleep = opts.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
3774
3774
  let lastErr;
3775
3775
  for (let i = 0; i < attempts; i++) {
3776
3776
  const isLast = i === attempts - 1;
@@ -3914,8 +3914,8 @@ async function readStdin(opts = {}) {
3914
3914
  })().catch(() => {
3915
3915
  });
3916
3916
  let timer;
3917
- const timeout = new Promise((resolve6) => {
3918
- timer = setTimeout(resolve6, timeoutMs);
3917
+ const timeout = new Promise((resolve5) => {
3918
+ timer = setTimeout(resolve5, timeoutMs);
3919
3919
  });
3920
3920
  try {
3921
3921
  await Promise.race([drain, timeout]);
@@ -5257,7 +5257,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
5257
5257
  await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
5258
5258
  return cliPriorityToFieldName(priority);
5259
5259
  }
5260
- var defaultRetrySleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
5260
+ var defaultRetrySleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
5261
5261
  async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
5262
5262
  const attempts = Math.max(1, opts.attempts ?? 5);
5263
5263
  const delayMs = opts.delayMs ?? 300;
@@ -5861,7 +5861,7 @@ function whoamiLine(report) {
5861
5861
  }
5862
5862
 
5863
5863
  // src/index.ts
5864
- var import_node_path18 = require("node:path");
5864
+ var import_node_path19 = require("node:path");
5865
5865
 
5866
5866
  // src/merge-ci-policy.ts
5867
5867
  function resolveMergeCiPolicy(input) {
@@ -6265,11 +6265,10 @@ function parseOwnerRepo(repo) {
6265
6265
  if (owner.includes("\\") || name.includes("\\")) throw new Error("repo must be owner/repo");
6266
6266
  return { owner, name, slug: name.toLowerCase(), fullName: `${owner}/${name}` };
6267
6267
  }
6268
- var DEFAULT_INSTALL_CMD = "npm ci";
6269
6268
  var DEFAULT_GATE_CMD = "npm run check";
6270
6269
  var DEFAULT_GATE_PY_VERSION = "3.11";
6271
6270
  var GATE_RUNTIME_DEFAULTS = {
6272
- node: { cmd: DEFAULT_GATE_CMD, install: DEFAULT_INSTALL_CMD },
6271
+ node: { cmd: DEFAULT_GATE_CMD, install: "npm ci" },
6273
6272
  python: { cmd: "pytest", install: 'pip install -e ".[dev]"' }
6274
6273
  };
6275
6274
  function gateSeedVars(cls, releaseTrack, runtime = "node") {
@@ -6311,7 +6310,6 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack) {
6311
6310
  out.REPO_NAME ??= parsed.name;
6312
6311
  out.REPO_SLUG ??= parsed.slug;
6313
6312
  out.CLASS ??= cls;
6314
- out.INSTALL_CMD ??= DEFAULT_INSTALL_CMD;
6315
6313
  const track = releaseTrack ?? resolveBootstrapReleaseTrack(cls);
6316
6314
  const runtime = out.GATE_RUNTIME === "python" ? "python" : "node";
6317
6315
  for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime))) {
@@ -7022,7 +7020,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
7022
7020
  if (first.mergeStatus !== "failed") return first;
7023
7021
  const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
7024
7022
  if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
7025
- const sleep = deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
7023
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
7026
7024
  await sleep(PR_LAND_MERGE_RETRY_DELAY_MS);
7027
7025
  const retried = await deps.mergeAuto(prNumber, repo);
7028
7026
  if (retried.mergeStatus !== "failed") return retried;
@@ -7031,7 +7029,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
7031
7029
  async function readGhPrStateWithRetry(fetchState, options) {
7032
7030
  const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
7033
7031
  const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
7034
- const sleep = options?.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
7032
+ const sleep = options?.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
7035
7033
  let lastError = "empty state";
7036
7034
  for (let attempt = 0; attempt < retries; attempt++) {
7037
7035
  try {
@@ -7164,7 +7162,7 @@ async function sweepDeferredWorktreesWithRetry(store, deps, opts = {}) {
7164
7162
  }
7165
7163
  return last;
7166
7164
  }
7167
- var defaultSleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
7165
+ var defaultSleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
7168
7166
  async function removeWorktreeWithRecovery(wtPath, deps) {
7169
7167
  const maxAttempts = deps.maxAttempts ?? 3;
7170
7168
  const backoff = deps.backoffMs ?? [250, 1e3];
@@ -8035,7 +8033,7 @@ var execFileP4 = (0, import_node_util6.promisify)(import_node_child_process6.exe
8035
8033
  var DOCKER_TIMEOUT_MS = 15e3;
8036
8034
  var EARLY_EXIT_GRACE_MS = 2e3;
8037
8035
  function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
8038
- return new Promise((resolve6, reject) => {
8036
+ return new Promise((resolve5, reject) => {
8039
8037
  let settled = false;
8040
8038
  const finish = (fn) => {
8041
8039
  if (settled) return;
@@ -8045,7 +8043,7 @@ function waitForProcessStability(child, graceMs = EARLY_EXIT_GRACE_MS) {
8045
8043
  child.removeAllListeners("exit");
8046
8044
  fn();
8047
8045
  };
8048
- const timer = setTimeout(() => finish(resolve6), graceMs);
8046
+ const timer = setTimeout(() => finish(resolve5), graceMs);
8049
8047
  child.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
8050
8048
  child.on("exit", (code, signal) => {
8051
8049
  const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
@@ -8255,10 +8253,10 @@ function pickStagePort(range, isFree) {
8255
8253
  throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
8256
8254
  }
8257
8255
  function isPortFree(port) {
8258
- return new Promise((resolve6) => {
8256
+ return new Promise((resolve5) => {
8259
8257
  const srv = (0, import_node_net.createServer)();
8260
- srv.once("error", () => resolve6(false));
8261
- srv.once("listening", () => srv.close(() => resolve6(true)));
8258
+ srv.once("error", () => resolve5(false));
8259
+ srv.once("listening", () => srv.close(() => resolve5(true)));
8262
8260
  srv.listen(port, "127.0.0.1");
8263
8261
  });
8264
8262
  }
@@ -8469,7 +8467,7 @@ async function killTree(pid) {
8469
8467
  } catch {
8470
8468
  }
8471
8469
  }
8472
- await new Promise((resolve6) => setTimeout(resolve6, 500));
8470
+ await new Promise((resolve5) => setTimeout(resolve5, 500));
8473
8471
  try {
8474
8472
  process.kill(-pid, "SIGKILL");
8475
8473
  } catch {
@@ -8490,7 +8488,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
8490
8488
  } catch (e) {
8491
8489
  last = e.message;
8492
8490
  }
8493
- await new Promise((resolve6) => setTimeout(resolve6, 1e3));
8491
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
8494
8492
  }
8495
8493
  throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
8496
8494
  }
@@ -8907,6 +8905,14 @@ ${line}
8907
8905
  `;
8908
8906
  }
8909
8907
 
8908
+ // src/issue-view-json.ts
8909
+ var DEFAULT_ISSUE_VIEW_FIELDS = "number,title,state,url,labels,author,assignees,milestone,body";
8910
+ function normalizeIssueViewJsonFields(tokens2) {
8911
+ if (!tokens2 || tokens2.length === 0) return DEFAULT_ISSUE_VIEW_FIELDS;
8912
+ const fields = tokens2.flatMap((token) => token.split(/[,\s]+/)).map((field) => field.trim()).filter(Boolean);
8913
+ return fields.length ? fields.join(",") : DEFAULT_ISSUE_VIEW_FIELDS;
8914
+ }
8915
+
8910
8916
  // src/sub-issue.ts
8911
8917
  function parseIssueRef(ref) {
8912
8918
  const trimmed = ref.trim();
@@ -9507,7 +9513,7 @@ function bootstrapPlan(repo, repoClass) {
9507
9513
  { label: `apply branch protection / allowlist: ${protectedBranches}`, gated: true },
9508
9514
  { label: "attach GitHub Project v2 and register Hub registry META", gated: true },
9509
9515
  { label: "seed README.md and architecture.md", gated: true },
9510
- { label: "commit .cursor/rules/<repo>.mdc and .cursor/environment.json", gated: true },
9516
+ { label: "commit .cursor/rules/<repo>.mdc", gated: true },
9511
9517
  { label: `register fanout target on ${repoClass === "content" ? "main" : "development"}`, gated: true }
9512
9518
  ];
9513
9519
  }
@@ -9801,7 +9807,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
9801
9807
  async function predictMergeConflicts(deps, ours, theirs) {
9802
9808
  return runMergeTreePreflight(deps, ours, theirs);
9803
9809
  }
9804
- async function mergeWithToleratedResolution(deps, sourceRef, label, resolve6, extraTolerated = []) {
9810
+ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve5, extraTolerated = []) {
9805
9811
  try {
9806
9812
  await deps.run("git", ["merge", sourceRef, "--no-edit"]);
9807
9813
  return;
@@ -9815,7 +9821,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve6, ex
9815
9821
  unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
9816
9822
  );
9817
9823
  }
9818
- await deps.run("git", ["checkout", `--${resolve6}`, "--", ...unmerged]);
9824
+ await deps.run("git", ["checkout", `--${resolve5}`, "--", ...unmerged]);
9819
9825
  await deps.run("git", ["add", "--", ...unmerged]);
9820
9826
  await deps.run("git", ["commit", "--no-edit"]);
9821
9827
  }
@@ -9981,7 +9987,7 @@ function requireProjectMetaForTrain(load, repo) {
9981
9987
  var CORRELATE_ATTEMPTS = 5;
9982
9988
  var CORRELATE_DELAY_MS = 1500;
9983
9989
  var CORRELATE_SKEW_SLACK_MS = 1e4;
9984
- var defaultSleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
9990
+ var defaultSleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
9985
9991
  function resolveSleep(deps) {
9986
9992
  return deps.sleep ?? defaultSleep2;
9987
9993
  }
@@ -11147,7 +11153,7 @@ function clean2(out) {
11147
11153
  return out.trim();
11148
11154
  }
11149
11155
  function sleeper(deps) {
11150
- return deps.sleep ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
11156
+ return deps.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
11151
11157
  }
11152
11158
  function normalizeHotfixVersion(input) {
11153
11159
  const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
@@ -11582,12 +11588,46 @@ async function findInFlightHotfixVersion(deps, ctx, latestMainTag) {
11582
11588
  return { inFlight: null, superseded };
11583
11589
  }
11584
11590
 
11585
- // src/release-announce.ts
11586
- var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
11591
+ // src/slack-alert.ts
11587
11592
  var SSM_REGION = "eu-central-1";
11588
11593
  var SSM_TOKEN_PARAM = "/mmi-future/_org/slack/SLACK_BOT_TOKEN";
11589
11594
  var SSM_CHANNEL_PARAM = "/mmi-future/_org/slack/SLACK_ALERTS_CHANNEL";
11590
11595
  var SLACK_TIMEOUT_MS = 1e4;
11596
+ async function ssmParameter(deps, name, decrypt) {
11597
+ const args = [
11598
+ "ssm",
11599
+ "get-parameter",
11600
+ "--region",
11601
+ SSM_REGION,
11602
+ "--name",
11603
+ name,
11604
+ "--query",
11605
+ "Parameter.Value",
11606
+ "--output",
11607
+ "text",
11608
+ ...decrypt ? ["--with-decryption"] : []
11609
+ ];
11610
+ const value = (await deps.run("aws", args)).trim();
11611
+ if (!value || value === "None") throw new Error(`SSM parameter ${name} is empty`);
11612
+ return value;
11613
+ }
11614
+ async function postToAlertsChannel(deps, text) {
11615
+ const token = await ssmParameter(deps, SSM_TOKEN_PARAM, true);
11616
+ const channel = await ssmParameter(deps, SSM_CHANNEL_PARAM, false);
11617
+ const fetchImpl = deps.fetchImpl ?? fetch;
11618
+ const res = await fetchImpl("https://slack.com/api/chat.postMessage", {
11619
+ method: "POST",
11620
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8" },
11621
+ body: JSON.stringify({ channel, text, unfurl_links: false }),
11622
+ signal: AbortSignal.timeout(SLACK_TIMEOUT_MS)
11623
+ });
11624
+ const json = await res.json().catch(() => ({}));
11625
+ if (!json.ok) throw new Error(`slack postMessage failed: ${json.error ?? `http ${res.status}`}`);
11626
+ return { status: "posted", note: "posted to the alerts channel" };
11627
+ }
11628
+
11629
+ // src/release-announce.ts
11630
+ var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
11591
11631
  var MAX_BULLETS = 6;
11592
11632
  var MAX_SUMMARY_LINES = 8;
11593
11633
  function neutralizeHubProductBrands(text) {
@@ -11662,24 +11702,6 @@ function formatAnnouncement(args) {
11662
11702
  function summaryFileLines(content) {
11663
11703
  return content.split("\n").map((l) => l.trim()).filter(Boolean).slice(0, MAX_SUMMARY_LINES).map((l) => escapeMrkdwn(neutralizeHubProductBrands(l.replace(/^[•*-]\s*/, "")))).filter(Boolean).map((l) => `\u2022 ${l}`);
11664
11704
  }
11665
- async function ssmParameter(deps, name, decrypt) {
11666
- const args = [
11667
- "ssm",
11668
- "get-parameter",
11669
- "--region",
11670
- SSM_REGION,
11671
- "--name",
11672
- name,
11673
- "--query",
11674
- "Parameter.Value",
11675
- "--output",
11676
- "text",
11677
- ...decrypt ? ["--with-decryption"] : []
11678
- ];
11679
- const value = (await deps.run("aws", args)).trim();
11680
- if (!value || value === "None") throw new Error(`SSM parameter ${name} is empty`);
11681
- return value;
11682
- }
11683
11705
  async function announceRelease(deps, args) {
11684
11706
  if (args.repo !== ANNOUNCE_REPO) {
11685
11707
  return { status: "skipped", note: `announce is Hub-only \u2014 ${args.repo} skipped` };
@@ -11701,18 +11723,8 @@ async function announceRelease(deps, args) {
11701
11723
  }
11702
11724
  if (lines.length === 0) lines = curateReleaseNotes(view.body ?? "");
11703
11725
  if (lines.length === 0) lines = ["\u2022 maintenance release \u2014 see the notes for details"];
11704
- const token = await ssmParameter(deps, SSM_TOKEN_PARAM, true);
11705
- const channel = await ssmParameter(deps, SSM_CHANNEL_PARAM, false);
11706
11726
  const text = formatAnnouncement({ repo: args.repo, tag: args.tag, lines, releaseUrl });
11707
- const fetchImpl = deps.fetchImpl ?? fetch;
11708
- const res = await fetchImpl("https://slack.com/api/chat.postMessage", {
11709
- method: "POST",
11710
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json; charset=utf-8" },
11711
- body: JSON.stringify({ channel, text, unfurl_links: false }),
11712
- signal: AbortSignal.timeout(SLACK_TIMEOUT_MS)
11713
- });
11714
- const json = await res.json().catch(() => ({}));
11715
- if (!json.ok) throw new Error(`slack postMessage failed: ${json.error ?? `http ${res.status}`}`);
11727
+ await postToAlertsChannel(deps, text);
11716
11728
  return { status: "announced", note: `announced ${args.tag} to the alerts channel` };
11717
11729
  } catch (e) {
11718
11730
  return { status: "failed", note: `announce failed (release unaffected): ${e.message}` };
@@ -12269,7 +12281,6 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
12269
12281
  });
12270
12282
  }
12271
12283
  }
12272
- checks.push({ ok: await contentExists2(deps, repo, baseBranch, ".cursor/environment.json"), label: "Cursor environment committed" });
12273
12284
  const readme = await contentText(deps, repo, baseBranch, "README.md");
12274
12285
  checks.push({
12275
12286
  ok: readme !== null && readme.includes("## Agent context"),
@@ -12678,26 +12689,112 @@ async function wikiPublish(deps, opts) {
12678
12689
  deps.err("wiki publish: <pages-dir> is required");
12679
12690
  return false;
12680
12691
  }
12681
- const script = deps.scriptPath();
12682
- if (!deps.scriptExists(script)) {
12683
- deps.err(`wiki publish: publish script not found at ${script} \u2014 run from the repo checkout root`);
12684
- return false;
12685
- }
12686
12692
  const minted = await deps.mint(opts.repo);
12687
12693
  if (!minted.ok) {
12688
12694
  deps.err(`wiki publish failed: could not mint a scoped wiki token for ${opts.repo}: ${minted.error}`);
12689
12695
  return false;
12690
12696
  }
12691
- const env = { ...process.env, WIKI_REPO: opts.repo, WIKI_TOKEN: minted.mint.token };
12692
- const code = deps.spawn("node", [script, opts.pagesDir], env);
12693
- if (code !== 0) {
12694
- deps.err(`wiki publish failed: publish script exited ${code} (token was scoped to ${opts.repo}, contents:write, expires ${minted.mint.expiresAt})`);
12697
+ const ok = deps.publish({ repo: opts.repo, token: minted.mint.token, pagesDir: opts.pagesDir });
12698
+ if (!ok) {
12695
12699
  return false;
12696
12700
  }
12697
12701
  deps.log(`wiki published for ${opts.repo} (scoped token, expired/discarded)`);
12698
12702
  return true;
12699
12703
  }
12700
12704
 
12705
+ // src/wiki-publish-git.ts
12706
+ var import_node_child_process8 = require("node:child_process");
12707
+ var import_node_fs13 = require("node:fs");
12708
+ var import_node_os5 = require("node:os");
12709
+ var import_node_path14 = require("node:path");
12710
+ var WIKI_STAMP_FILE = ".wiki-state.json";
12711
+ function redactWikiSecrets(msg) {
12712
+ return msg.replace(/x-access-token:[^@\s]+@/gi, "x-access-token:***@").replace(/(AUTHORIZATION:\s*basic\s+)[A-Za-z0-9+/=]+/gi, "$1***");
12713
+ }
12714
+ function publishWikiViaGit(deps, opts) {
12715
+ const dir = deps.mkdtemp();
12716
+ const url = `https://github.com/${opts.repo}.wiki.git`;
12717
+ const authArgs = [
12718
+ "-c",
12719
+ `http.extraheader=AUTHORIZATION: basic ${Buffer.from(`x-access-token:${opts.token}`).toString("base64")}`
12720
+ ];
12721
+ try {
12722
+ deps.gitRun(["init", "-q"], dir);
12723
+ deps.gitRun(["remote", "add", "origin", url], dir);
12724
+ if (deps.gitProbe([...authArgs, "fetch", "--depth", "1", "origin", "master"], dir)) {
12725
+ deps.gitRun(["reset", "--hard", "FETCH_HEAD"], dir);
12726
+ }
12727
+ let copied = 0;
12728
+ for (const f of deps.readdir(opts.pagesDir)) {
12729
+ if (f.endsWith(".md")) {
12730
+ deps.copyFile((0, import_node_path14.join)(opts.pagesDir, f), (0, import_node_path14.join)(dir, f));
12731
+ copied++;
12732
+ }
12733
+ }
12734
+ if (!copied) {
12735
+ deps.err(`wiki publish: no .md pages found in ${opts.pagesDir}`);
12736
+ return false;
12737
+ }
12738
+ if (deps.fileExists((0, import_node_path14.join)(opts.pagesDir, WIKI_STAMP_FILE))) {
12739
+ deps.copyFile((0, import_node_path14.join)(opts.pagesDir, WIKI_STAMP_FILE), (0, import_node_path14.join)(dir, WIKI_STAMP_FILE));
12740
+ }
12741
+ deps.gitRun(["add", "-A"], dir);
12742
+ if (deps.gitProbe(["diff", "--cached", "--quiet"], dir)) {
12743
+ deps.log("wiki publish: already up to date");
12744
+ return true;
12745
+ }
12746
+ deps.gitRun(
12747
+ [
12748
+ "-c",
12749
+ "user.email=mmi-github-app[bot]@users.noreply.github.com",
12750
+ "-c",
12751
+ "user.name=mmi-github-app[bot]",
12752
+ "commit",
12753
+ "-m",
12754
+ "docs(wiki): refresh MMI Future project wiki"
12755
+ ],
12756
+ dir
12757
+ );
12758
+ try {
12759
+ deps.gitRun([...authArgs, "push", "origin", "HEAD:master"], dir);
12760
+ } catch (e) {
12761
+ deps.err(
12762
+ `wiki publish failed: push to ${opts.repo}.wiki.git rejected: ${redactWikiSecrets(e.message)} \u2014 if the wiki advanced before this push, re-run wiki publish to pick up the latest wiki state`
12763
+ );
12764
+ return false;
12765
+ }
12766
+ deps.log(`wiki publish: published ${copied} page(s) to ${opts.repo} wiki`);
12767
+ return true;
12768
+ } catch (e) {
12769
+ deps.err(`wiki publish failed: ${redactWikiSecrets(e.message)}`);
12770
+ return false;
12771
+ } finally {
12772
+ deps.rm(dir);
12773
+ }
12774
+ }
12775
+ function realWikiGitPublishDeps() {
12776
+ return {
12777
+ mkdtemp: () => (0, import_node_fs13.mkdtempSync)((0, import_node_path14.join)((0, import_node_os5.tmpdir)(), "wiki-")),
12778
+ readdir: (dir) => (0, import_node_fs13.readdirSync)(dir),
12779
+ copyFile: (src, dest) => (0, import_node_fs13.copyFileSync)(src, dest),
12780
+ fileExists: (p) => (0, import_node_fs13.existsSync)(p),
12781
+ rm: (p) => (0, import_node_fs13.rmSync)(p, { recursive: true, force: true }),
12782
+ gitProbe: (args, cwd) => {
12783
+ try {
12784
+ (0, import_node_child_process8.execFileSync)("git", args, { cwd, stdio: "pipe" });
12785
+ return true;
12786
+ } catch {
12787
+ return false;
12788
+ }
12789
+ },
12790
+ gitRun: (args, cwd) => {
12791
+ (0, import_node_child_process8.execFileSync)("git", args, { cwd, stdio: "inherit" });
12792
+ },
12793
+ log: (msg) => console.log(msg),
12794
+ err: (msg) => console.error(msg)
12795
+ };
12796
+ }
12797
+
12701
12798
  // src/project-readiness.ts
12702
12799
  function stagesForTrack(meta) {
12703
12800
  return branchesForTrack(resolveReleaseTrack(meta)).map((b) => b === "development" ? "dev" : b);
@@ -13811,7 +13908,7 @@ function writeError(res) {
13811
13908
  }
13812
13909
 
13813
13910
  // src/secrets.ts
13814
- var import_node_child_process8 = require("node:child_process");
13911
+ var import_node_child_process9 = require("node:child_process");
13815
13912
  var OWNER2 = "mutmutco";
13816
13913
  var SSM_ROOT = "/mmi-future";
13817
13914
  var PROJECT_TIER_SEGMENT = "dev";
@@ -14542,7 +14639,7 @@ async function secretsCopy(deps, opts) {
14542
14639
  return true;
14543
14640
  }
14544
14641
  function defaultSpawn(command, args, env) {
14545
- const r = (0, import_node_child_process8.spawnSync)(command, args, { stdio: "inherit", env });
14642
+ const r = (0, import_node_child_process9.spawnSync)(command, args, { stdio: "inherit", env });
14546
14643
  if (r.error) throw r.error;
14547
14644
  return r.status ?? 1;
14548
14645
  }
@@ -14602,8 +14699,8 @@ async function secretsUse(deps, key, opts) {
14602
14699
  }
14603
14700
 
14604
14701
  // src/secrets-commands.ts
14605
- var import_node_fs13 = require("node:fs");
14606
- var import_node_path14 = require("node:path");
14702
+ var import_node_fs14 = require("node:fs");
14703
+ var import_node_path15 = require("node:path");
14607
14704
  var RAILS_CREDENTIALS_DECRYPT_TIMEOUT_MS = 3e4;
14608
14705
  var DEFAULT_RAILS_CREDENTIALS_FILE = "config/credentials.yml.enc";
14609
14706
  var DEFAULT_RAILS_MASTER_KEY_FILE = "config/master.key";
@@ -14611,18 +14708,18 @@ function collectMap(value, previous = []) {
14611
14708
  return [...previous, value];
14612
14709
  }
14613
14710
  async function decryptRailsCredentials(input) {
14614
- const appDir = (0, import_node_path14.resolve)(input.appDir ?? process.cwd());
14711
+ const appDir = (0, import_node_path15.resolve)(input.appDir ?? process.cwd());
14615
14712
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
14616
14713
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
14617
- const credentialsPath = (0, import_node_path14.resolve)(appDir, credentialsFile);
14618
- const masterKeyPath = (0, import_node_path14.resolve)(appDir, masterKeyFile);
14714
+ const credentialsPath = (0, import_node_path15.resolve)(appDir, credentialsFile);
14715
+ const masterKeyPath = (0, import_node_path15.resolve)(appDir, masterKeyFile);
14619
14716
  const env = {
14620
14717
  ...process.env,
14621
14718
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
14622
14719
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
14623
14720
  };
14624
- if ((0, import_node_fs13.existsSync)(masterKeyPath)) {
14625
- env.RAILS_MASTER_KEY = (0, import_node_fs13.readFileSync)(masterKeyPath, "utf8").trim();
14721
+ if ((0, import_node_fs14.existsSync)(masterKeyPath)) {
14722
+ env.RAILS_MASTER_KEY = (0, import_node_fs14.readFileSync)(masterKeyPath, "utf8").trim();
14626
14723
  }
14627
14724
  const script = [
14628
14725
  'require "json"',
@@ -14688,7 +14785,7 @@ function registerSecretsCommands(program3) {
14688
14785
  secrets.command("org-catalog").description("MASTER-ONLY: set the _org/<provider>/* catalog declarations from a JSON file (#2244)").requiredOption("--file <path>", 'JSON file: { "entries": { "<provider>/<KEY>": {key,purpose,group,owner,provider,stages[],consumers[]} } }').action((o) => withSecrets(async (d) => {
14689
14786
  let body;
14690
14787
  try {
14691
- body = (0, import_node_fs13.readFileSync)((0, import_node_path14.resolve)(o.file), "utf8");
14788
+ body = (0, import_node_fs14.readFileSync)((0, import_node_path15.resolve)(o.file), "utf8");
14692
14789
  } catch (e) {
14693
14790
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
14694
14791
  }
@@ -14763,7 +14860,7 @@ function registerSecretsCommands(program3) {
14763
14860
  {
14764
14861
  ...d,
14765
14862
  decryptRailsCredentials,
14766
- removeFile: (path2) => (0, import_node_fs13.unlinkSync)((0, import_node_path14.resolve)(o.appDir ?? process.cwd(), path2))
14863
+ removeFile: (path2) => (0, import_node_fs14.unlinkSync)((0, import_node_path15.resolve)(o.appDir ?? process.cwd(), path2))
14767
14864
  },
14768
14865
  {
14769
14866
  repo: o.repo,
@@ -14852,11 +14949,11 @@ function registerEdgeCommands(program3) {
14852
14949
  }
14853
14950
 
14854
14951
  // src/doctor-run.ts
14855
- var import_node_fs17 = require("node:fs");
14856
- var import_node_child_process10 = require("node:child_process");
14952
+ var import_node_fs18 = require("node:fs");
14953
+ var import_node_child_process11 = require("node:child_process");
14857
14954
  var import_promises2 = require("node:fs/promises");
14858
- var import_node_path17 = require("node:path");
14859
- var import_node_os6 = require("node:os");
14955
+ var import_node_path18 = require("node:path");
14956
+ var import_node_os7 = require("node:os");
14860
14957
 
14861
14958
  // src/plugin-guard.ts
14862
14959
  function buildPluginGuardDecision(i) {
@@ -14868,140 +14965,20 @@ function buildPluginGuardDecision(i) {
14868
14965
  function buildGuardSessionStartLine(state, opts = {}) {
14869
14966
  if (state === "healthy" || state === "not-org") return { exitCode: 0 };
14870
14967
  const recovery = opts.recovery ?? "mmi-cli plugin-heal";
14968
+ const restartHint = opts.restartHint ?? "restart your agent host / reload plugins";
14871
14969
  const reason = state === "no-install" ? "MMI plugin is not installed for this user/session" : "MMI plugin is installed but its marketplace/cache is unresolved";
14872
14970
  return {
14873
- line: `[mmi-guard] ${reason}; run ${recovery} and restart Claude Code / reload plugins.`,
14971
+ line: `[mmi-guard] ${reason}; run ${recovery} and ${restartHint}.`,
14874
14972
  exitCode: 1
14875
14973
  };
14876
14974
  }
14877
14975
 
14878
14976
  // src/cursor-plugin-seed.ts
14879
- var import_node_child_process9 = require("node:child_process");
14880
- var import_node_fs14 = require("node:fs");
14881
- var import_node_os5 = require("node:os");
14882
- var import_node_path15 = require("node:path");
14977
+ var import_node_child_process10 = require("node:child_process");
14978
+ var import_node_fs15 = require("node:fs");
14979
+ var import_node_os6 = require("node:os");
14980
+ var import_node_path16 = require("node:path");
14883
14981
  var import_node_util7 = require("node:util");
14884
- function isSemverVersion(v) {
14885
- return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
14886
- }
14887
- var MMI_HUB_REPO = "mutmutco/MMI-Hub";
14888
- var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
14889
- var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
14890
- var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process9.execFile);
14891
- function gitFetchReleaseTagArgs(hubCheckout, tag) {
14892
- return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
14893
- }
14894
- function ghReleaseTarballApiArgs(tag) {
14895
- return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
14896
- }
14897
- function cursorUserGlobalStatePath() {
14898
- if (process.platform === "win32") {
14899
- const base = process.env.APPDATA || (0, import_node_path15.join)((0, import_node_os5.homedir)(), "AppData", "Roaming");
14900
- return (0, import_node_path15.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
14901
- }
14902
- if (process.platform === "darwin") {
14903
- return (0, import_node_path15.join)((0, import_node_os5.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
14904
- }
14905
- return (0, import_node_path15.join)((0, import_node_os5.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
14906
- }
14907
- async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
14908
- const dbPath = cursorUserGlobalStatePath();
14909
- if (!(0, import_node_fs14.existsSync)(dbPath)) return void 0;
14910
- try {
14911
- const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
14912
- timeout: 5e3
14913
- });
14914
- const value = stdout.trim().toLowerCase();
14915
- if (value === "true") return true;
14916
- if (value === "false") return false;
14917
- return void 0;
14918
- } catch {
14919
- return void 0;
14920
- }
14921
- }
14922
- function syncDirContents(src, dest) {
14923
- (0, import_node_fs14.mkdirSync)(dest, { recursive: true });
14924
- for (const name of (0, import_node_fs14.readdirSync)(dest)) {
14925
- (0, import_node_fs14.rmSync)((0, import_node_path15.join)(dest, name), { recursive: true, force: true });
14926
- }
14927
- (0, import_node_fs14.cpSync)(src, dest, { recursive: true });
14928
- }
14929
- function releaseTag(releasedVersion) {
14930
- return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
14931
- }
14932
- async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
14933
- const tarFile = (0, import_node_path15.join)(tmpRoot, "archive.tar");
14934
- try {
14935
- await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
14936
- await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
14937
- timeout: 6e4
14938
- });
14939
- await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
14940
- const pluginMmi = (0, import_node_path15.join)(tmpRoot, "plugins", "mmi");
14941
- return (0, import_node_fs14.existsSync)((0, import_node_path15.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
14942
- } catch {
14943
- return void 0;
14944
- }
14945
- }
14946
- async function downloadPluginMmiViaGh(tag, tmpRoot) {
14947
- const tarPath = (0, import_node_path15.join)(tmpRoot, "repo.tgz");
14948
- try {
14949
- (0, import_node_fs14.mkdirSync)(tmpRoot, { recursive: true });
14950
- const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
14951
- timeout: 12e4,
14952
- maxBuffer: 100 * 1024 * 1024,
14953
- encoding: "buffer",
14954
- windowsHide: true
14955
- });
14956
- (0, import_node_fs14.writeFileSync)(tarPath, stdout);
14957
- await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
14958
- const top = (0, import_node_fs14.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
14959
- if (!top) return void 0;
14960
- const pluginMmi = (0, import_node_path15.join)(tmpRoot, top, "plugins", "mmi");
14961
- return (0, import_node_fs14.existsSync)((0, import_node_path15.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
14962
- } catch {
14963
- return void 0;
14964
- }
14965
- }
14966
- async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
14967
- (0, import_node_fs14.mkdirSync)(tmpRoot, { recursive: true });
14968
- const tag = releaseTag(releasedVersion);
14969
- if (hubCheckout) {
14970
- const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
14971
- if (fromHub) return fromHub;
14972
- }
14973
- return downloadPluginMmiViaGh(tag, (0, import_node_path15.join)(tmpRoot, "gh"));
14974
- }
14975
- function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
14976
- if (!isSemverVersion(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
14977
- return pins.filter((pin) => {
14978
- if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
14979
- if (isSemverVersion(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
14980
- return false;
14981
- });
14982
- }
14983
- function cursorPluginCacheSeedTargets(pins, releasedVersion, cacheRoot, cacheRootExists = false) {
14984
- const needing = cursorPluginPinsNeedingSeed(pins, releasedVersion);
14985
- if (needing.length > 0) return needing.map((pin) => pin.path);
14986
- if (pins.length === 0 && cacheRoot && cacheRootExists && isSemverVersion(releasedVersion)) {
14987
- return [(0, import_node_path15.join)(cacheRoot, `v${releasedVersion.replace(/^v/, "")}`)];
14988
- }
14989
- return [];
14990
- }
14991
- async function applyCursorPluginCacheSeed(input) {
14992
- if (!isSemverVersion(input.releasedVersion)) return false;
14993
- const targets = cursorPluginCacheSeedTargets(input.pins, input.releasedVersion, input.cacheRoot, input.cacheRootExists);
14994
- if (targets.length === 0) return false;
14995
- const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
14996
- const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
14997
- if (!source) return false;
14998
- input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
14999
- for (const dest of targets) {
15000
- syncDirContents(source, dest);
15001
- }
15002
- (0, import_node_fs14.rmSync)(tmpRoot, { recursive: true, force: true });
15003
- return true;
15004
- }
15005
14982
 
15006
14983
  // src/doctor.ts
15007
14984
  var GH_PROJECT_LOGIN_FIX = 'run: gh auth login --hostname github.com --git-protocol https --web --scopes "project"';
@@ -15285,8 +15262,8 @@ function buildCursorPluginCacheCleanupCheck(input) {
15285
15262
  label: CURSOR_PLUGIN_CACHE_CLEANUP_LABEL,
15286
15263
  fix: CURSOR_PLUGIN_CACHE_CLEANUP_FIX
15287
15264
  };
15288
- if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base;
15289
- const semverPins = input.pins.filter((pin) => isSemverVersion2(pin.version));
15265
+ if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
15266
+ const semverPins = input.pins.filter((pin) => isSemverVersion(pin.version));
15290
15267
  if (semverPins.length === 0) return base;
15291
15268
  const activeVersion = semverPins.map((pin) => pin.version).reduce((a, b) => compareVersions(a, b) >= 0 ? a : b);
15292
15269
  const protectedVersions = new Set(
@@ -15336,7 +15313,7 @@ function codexEnabledPluginVersionFromList(jsonText, pluginId = MMI_PLUGIN_ID) {
15336
15313
  const parsed = JSON.parse(jsonText);
15337
15314
  const rows = Array.isArray(parsed?.installed) ? parsed.installed : [];
15338
15315
  const row = rows.find((r) => r?.pluginId === pluginId && r.installed === true && r.enabled === true);
15339
- return isSemverVersion2(row?.version) ? row.version.trim().replace(/^v/, "") : void 0;
15316
+ return isSemverVersion(row?.version) ? row.version.trim().replace(/^v/, "") : void 0;
15340
15317
  } catch {
15341
15318
  return void 0;
15342
15319
  }
@@ -15347,9 +15324,9 @@ function buildCodexActiveCacheCheck(input) {
15347
15324
  label: CODEX_ACTIVE_CACHE_LABEL,
15348
15325
  fix: CODEX_PLUGIN_RECOVERY
15349
15326
  };
15350
- if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base;
15327
+ if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
15351
15328
  if (input.codexPresent === false) return base;
15352
- if (isSemverVersion2(input.codexActiveVersion)) {
15329
+ if (isSemverVersion(input.codexActiveVersion)) {
15353
15330
  if (compareVersions(input.codexActiveVersion, input.releasedVersion) < 0) {
15354
15331
  return {
15355
15332
  ...base,
@@ -15361,11 +15338,11 @@ function buildCodexActiveCacheCheck(input) {
15361
15338
  }
15362
15339
  return { ...base, activeCacheVersion: input.codexActiveVersion, releasedVersion: input.releasedVersion };
15363
15340
  }
15364
- if (isSemverVersion2(input.codexRecordVersion) && compareVersions(input.codexRecordVersion, input.releasedVersion) < 0) {
15341
+ if (isSemverVersion(input.codexRecordVersion) && compareVersions(input.codexRecordVersion, input.releasedVersion) < 0) {
15365
15342
  return base;
15366
15343
  }
15367
15344
  const activeCacheVersion = highestSemver(input.codexCacheVersions ?? []);
15368
- const cacheCurrent = isSemverVersion2(activeCacheVersion) && compareVersions(activeCacheVersion, input.releasedVersion) >= 0;
15345
+ const cacheCurrent = isSemverVersion(activeCacheVersion) && compareVersions(activeCacheVersion, input.releasedVersion) >= 0;
15369
15346
  if (cacheCurrent) {
15370
15347
  return { ...base, activeCacheVersion, releasedVersion: input.releasedVersion };
15371
15348
  }
@@ -15444,7 +15421,7 @@ function buildSessionSkillRootCheck(input) {
15444
15421
  const sessionVersion = parseSessionSkillRootVersion(input.pluginRoot);
15445
15422
  if (!sessionVersion) return base;
15446
15423
  const activeCacheVersion = highestSemver(input.cacheVersions ?? []);
15447
- if (!isSemverVersion2(activeCacheVersion)) return { ...base, sessionVersion };
15424
+ if (!isSemverVersion(activeCacheVersion)) return { ...base, sessionVersion };
15448
15425
  if (compareVersions(sessionVersion, activeCacheVersion) >= 0) {
15449
15426
  return { ...base, sessionVersion, activeCacheVersion };
15450
15427
  }
@@ -15526,7 +15503,8 @@ var PLUGIN_SURFACE_HEAL = {
15526
15503
  ],
15527
15504
  pluginRunner: "codex",
15528
15505
  fix: (surface) => `${CODEX_RECOVERY} # then ${reloadAction(surface)}`,
15529
- updateRecipe: [CODEX_RECOVERY, "codex plugin list # verify mmi@mutmutco shows the released version"]
15506
+ updateRecipe: [CODEX_RECOVERY, "codex plugin list # verify mmi@mutmutco shows the released version"],
15507
+ guardRecovery: "$env:MMI_AGENT_SURFACE='codex'; mmi-cli plugin-heal"
15530
15508
  },
15531
15509
  cursor: {
15532
15510
  delivery: "cursor-cache",
@@ -15610,7 +15588,7 @@ function renderSurfaceGuide(label, steps) {
15610
15588
  }
15611
15589
  function highestSemver(versions) {
15612
15590
  return versions.reduce((best, v) => {
15613
- if (!isSemverVersion2(v)) return best;
15591
+ if (!isSemverVersion(v)) return best;
15614
15592
  if (best === void 0) return v;
15615
15593
  return compareVersions(v, best) > 0 ? v : best;
15616
15594
  }, void 0);
@@ -15622,12 +15600,12 @@ function buildPluginUpdateReport(input) {
15622
15600
  const opencodePlugin = highestSemver(input.opencodePluginVersions ?? []);
15623
15601
  return {
15624
15602
  versions: {
15625
- ...isSemverVersion2(input.cliVersion) ? { cli: input.cliVersion } : {},
15603
+ ...isSemverVersion(input.cliVersion) ? { cli: input.cliVersion } : {},
15626
15604
  ...claudePlugin ? { claudePlugin } : {},
15627
15605
  ...codexMarketplace ? { codexMarketplace } : {},
15628
15606
  ...codexActiveCache ? { codexActiveCache } : {},
15629
15607
  ...opencodePlugin ? { opencodePlugin } : {},
15630
- ...isSemverVersion2(input.releasedVersion) ? { released: input.releasedVersion } : {},
15608
+ ...isSemverVersion(input.releasedVersion) ? { released: input.releasedVersion } : {},
15631
15609
  ...input.releasedVersionSource ? { releasedSource: input.releasedVersionSource } : {}
15632
15610
  },
15633
15611
  recipes: PLUGIN_UPDATE_RECIPES
@@ -15651,14 +15629,15 @@ function renderPluginVersionBlock(report) {
15651
15629
  }
15652
15630
  function buildDoctorJsonPayload(input) {
15653
15631
  return {
15654
- ok: input.checks.every((c) => c.ok),
15632
+ ok: doctorExitCode(input.checks) === 0,
15633
+ hasAdvisories: input.checks.some((c) => !c.ok && isAdvisoryDoctorCheck(c)),
15655
15634
  checks: input.checks,
15656
15635
  updateReport: input.updateReport,
15657
15636
  ...input.resources.length ? { resources: input.resources } : {}
15658
15637
  };
15659
15638
  }
15660
15639
  var INSTALLED_PLUGIN_VERSION_LABEL = "installed MMI plugin version (vs latest release)";
15661
- function isSemverVersion2(v) {
15640
+ function isSemverVersion(v) {
15662
15641
  return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
15663
15642
  }
15664
15643
  function staleRecordCommand(surface) {
@@ -15681,7 +15660,7 @@ function buildInstalledPluginVersionCheck(input) {
15681
15660
  fix: pluginRecoveryFix(input.surface),
15682
15661
  pluginId
15683
15662
  };
15684
- if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base;
15663
+ if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
15685
15664
  if (input.surface === "cursor") return base;
15686
15665
  const stale = [];
15687
15666
  let sawRecord = false;
@@ -15691,7 +15670,7 @@ function buildInstalledPluginVersionCheck(input) {
15691
15670
  if (!Array.isArray(records) || records.length === 0) continue;
15692
15671
  sawRecord = true;
15693
15672
  const installedVersion = bestRecord(records).version;
15694
- if (!isSemverVersion2(installedVersion)) continue;
15673
+ if (!isSemverVersion(installedVersion)) continue;
15695
15674
  if (compareVersions(installedVersion, input.releasedVersion) >= 0) {
15696
15675
  currentVersion = currentVersion ?? installedVersion;
15697
15676
  } else {
@@ -15746,14 +15725,14 @@ function buildManagedPluginDriftCheck(input) {
15746
15725
  for (const source of input.sources) {
15747
15726
  if (source.directVersions && descriptor.id in source.directVersions) {
15748
15727
  const version2 = source.directVersions[descriptor.id];
15749
- if (isSemverVersion2(version2)) versions.push({ surface: source.surface, version: normalize(version2) });
15728
+ if (isSemverVersion(version2)) versions.push({ surface: source.surface, version: normalize(version2) });
15750
15729
  continue;
15751
15730
  }
15752
15731
  const records = source.installed?.plugins?.[descriptor.id];
15753
15732
  if (!Array.isArray(records) || records.length === 0) continue;
15754
15733
  const recordVersion = bestRecord(records).version;
15755
- const version = isSemverVersion2(recordVersion) ? recordVersion : highestSemver(source.cacheVersions?.[descriptor.id] ?? []);
15756
- if (!isSemverVersion2(version)) continue;
15734
+ const version = isSemverVersion(recordVersion) ? recordVersion : highestSemver(source.cacheVersions?.[descriptor.id] ?? []);
15735
+ if (!isSemverVersion(version)) continue;
15757
15736
  versions.push({ surface: source.surface, version: normalize(version) });
15758
15737
  }
15759
15738
  if (versions.length < 2) continue;
@@ -15768,8 +15747,8 @@ var OPENCODE_VERSION_LABEL = "installed OpenCode MMI adapter version (vs latest
15768
15747
  function buildOpencodeVersionCheck(input) {
15769
15748
  const fix = pluginRecoveryFix("opencode");
15770
15749
  const base = { ok: true, label: OPENCODE_VERSION_LABEL, fix };
15771
- if (!input.isOrgRepo || !isSemverVersion2(input.releasedVersion)) return base;
15772
- if (!isSemverVersion2(input.installedVersion)) {
15750
+ if (!input.isOrgRepo || !isSemverVersion(input.releasedVersion)) return base;
15751
+ if (!isSemverVersion(input.installedVersion)) {
15773
15752
  return { ...base, ok: false, severityOverride: "hard", releasedVersion: input.releasedVersion };
15774
15753
  }
15775
15754
  if (compareVersions(input.installedVersion, input.releasedVersion) >= 0) {
@@ -15786,7 +15765,7 @@ function pickOpencodeActiveVersion(input) {
15786
15765
  }
15787
15766
  function planOpencodePluginCacheQuarantine(input) {
15788
15767
  if (!input.cacheExists) return { quarantine: false };
15789
- if (!isSemverVersion2(input.cacheVersion) || !isSemverVersion2(input.releasedVersion)) return { quarantine: false };
15768
+ if (!isSemverVersion(input.cacheVersion) || !isSemverVersion(input.releasedVersion)) return { quarantine: false };
15790
15769
  if (compareVersions(input.cacheVersion, input.releasedVersion) < 0) {
15791
15770
  return { quarantine: true, cacheVersion: input.cacheVersion, releasedVersion: input.releasedVersion };
15792
15771
  }
@@ -16022,8 +16001,8 @@ function buildCursorPluginInstallCheck(input) {
16022
16001
  };
16023
16002
  }
16024
16003
  }
16025
- if (input.cacheRootExists && isSemverVersion2(input.releasedVersion) && input.pins.some((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
16026
- const stale = input.pins.filter((pin) => isSemverVersion2(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
16004
+ if (input.cacheRootExists && isSemverVersion(input.releasedVersion) && input.pins.some((pin) => isSemverVersion(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0)) {
16005
+ const stale = input.pins.filter((pin) => isSemverVersion(pin.version) && compareVersions(pin.version, input.releasedVersion) < 0).map((pin) => `${pin.version} at ${pin.name}`).join(", ");
16027
16006
  return {
16028
16007
  ...base,
16029
16008
  ok: false,
@@ -16217,18 +16196,19 @@ function isAdvisoryDoctorCheck(check) {
16217
16196
  function doctorExitCode(checks) {
16218
16197
  return checks.some((c) => !c.ok && !isAdvisoryDoctorCheck(c)) ? 1 : 0;
16219
16198
  }
16220
- function renderPluginUpdateReportStaleOnly(report) {
16199
+ function renderPluginUpdateReportStaleOnly(report, allChecksPass = false) {
16221
16200
  const v = report.versions;
16222
16201
  const released = v.released;
16223
16202
  if (!released) return [];
16224
- const isStale = (current) => Boolean(current && isSemverVersion2(current) && compareVersions(current, released) < 0);
16203
+ const isStale = (current) => Boolean(current && isSemverVersion(current) && compareVersions(current, released) < 0);
16225
16204
  const blocks = [];
16226
16205
  for (const surface of PLUGIN_GUIDE_SURFACES) {
16227
16206
  if (!surface.versionKeys.some((k) => isStale(v[k]))) continue;
16228
16207
  blocks.push(...renderSurfaceGuide(surface.label, report.recipes[surface.key]));
16229
16208
  }
16230
16209
  if (!blocks.length) return [];
16231
- return ["Update commands (stale surfaces):", ...blocks];
16210
+ const heading = allChecksPass ? "Optional updates available (all checks passed):" : "Update commands (stale surfaces):";
16211
+ return [heading, ...blocks];
16232
16212
  }
16233
16213
  var DOCTOR_VERBOSE_HINT = "Run mmi-cli doctor --verbose for the full audit checklist + version report.";
16234
16214
  function doctorCheckGlyph(check) {
@@ -16274,7 +16254,7 @@ function doctorHumanLines(input) {
16274
16254
  if (input.shouldApply && input.pluginReloadRequired) {
16275
16255
  lines.push("", `\u21BB ${input.reloadHint ?? "reload"} so the healed plugin/MCP installs load.`);
16276
16256
  }
16277
- const stale = renderPluginUpdateReportStaleOnly(input.updateReport);
16257
+ const stale = renderPluginUpdateReportStaleOnly(input.updateReport, gaps.length === 0);
16278
16258
  if (stale.length) {
16279
16259
  lines.push("", ...stale);
16280
16260
  } else if (!input.verbose) {
@@ -16344,7 +16324,7 @@ function buildPluginResolvabilityCheck(input) {
16344
16324
  return {
16345
16325
  ok: false,
16346
16326
  label: PLUGIN_RESOLVABILITY_LABEL,
16347
- fix: "run: mmi-cli plugin-heal",
16327
+ fix: `run: ${PLUGIN_SURFACE_HEAL[surfaceToken(surface) ?? "claude"]?.recovery ?? PLUGIN_SURFACE_HEAL.claude.recovery}`,
16348
16328
  state
16349
16329
  };
16350
16330
  default: {
@@ -16354,6 +16334,176 @@ function buildPluginResolvabilityCheck(input) {
16354
16334
  }
16355
16335
  }
16356
16336
 
16337
+ // src/cursor-plugin-seed.ts
16338
+ function isSemverVersion2(v) {
16339
+ return typeof v === "string" && /^v?\d+\.\d+\.\d+/.test(v.trim());
16340
+ }
16341
+ var MMI_HUB_REPO = "mutmutco/MMI-Hub";
16342
+ var CURSOR_THIRD_PARTY_STATE_KEY = "cursor/thirdPartyExtensibilityEnabled";
16343
+ var PLUGIN_JSON_REL = ".cursor-plugin/plugin.json";
16344
+ var execFileBuffer = (0, import_node_util7.promisify)(import_node_child_process10.execFile);
16345
+ function gitFetchReleaseTagArgs(hubCheckout, tag) {
16346
+ return ["-C", hubCheckout, "fetch", "origin", "tag", tag, "--quiet"];
16347
+ }
16348
+ function ghReleaseTarballApiArgs(tag) {
16349
+ return ["api", `repos/${MMI_HUB_REPO}/tarball/refs/tags/${tag}`];
16350
+ }
16351
+ function cursorUserGlobalStatePath() {
16352
+ if (process.platform === "win32") {
16353
+ const base = process.env.APPDATA || (0, import_node_path16.join)((0, import_node_os6.homedir)(), "AppData", "Roaming");
16354
+ return (0, import_node_path16.join)(base, "Cursor", "User", "globalStorage", "state.vscdb");
16355
+ }
16356
+ if (process.platform === "darwin") {
16357
+ return (0, import_node_path16.join)((0, import_node_os6.homedir)(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb");
16358
+ }
16359
+ return (0, import_node_path16.join)((0, import_node_os6.homedir)(), ".config", "Cursor", "User", "globalStorage", "state.vscdb");
16360
+ }
16361
+ async function readCursorThirdPartyExtensibilityEnabled(execFileP5) {
16362
+ const dbPath = cursorUserGlobalStatePath();
16363
+ if (!(0, import_node_fs15.existsSync)(dbPath)) return void 0;
16364
+ try {
16365
+ const { stdout } = await execFileP5("sqlite3", [dbPath, `SELECT value FROM ItemTable WHERE key = '${CURSOR_THIRD_PARTY_STATE_KEY}';`], {
16366
+ timeout: 5e3
16367
+ });
16368
+ const value = stdout.trim().toLowerCase();
16369
+ if (value === "true") return true;
16370
+ if (value === "false") return false;
16371
+ return void 0;
16372
+ } catch {
16373
+ return void 0;
16374
+ }
16375
+ }
16376
+ function syncDirContents(src, dest) {
16377
+ (0, import_node_fs15.mkdirSync)(dest, { recursive: true });
16378
+ for (const name of (0, import_node_fs15.readdirSync)(dest)) {
16379
+ (0, import_node_fs15.rmSync)((0, import_node_path16.join)(dest, name), { recursive: true, force: true });
16380
+ }
16381
+ (0, import_node_fs15.cpSync)(src, dest, { recursive: true });
16382
+ }
16383
+ function releaseTag(releasedVersion) {
16384
+ return releasedVersion.startsWith("v") ? releasedVersion : `v${releasedVersion}`;
16385
+ }
16386
+ async function extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5) {
16387
+ const tarFile = (0, import_node_path16.join)(tmpRoot, "archive.tar");
16388
+ try {
16389
+ await execFileP5("git", gitFetchReleaseTagArgs(hubCheckout, tag), { timeout: 6e4 });
16390
+ await execFileP5("git", ["-C", hubCheckout, "archive", "--format=tar", `--output=${tarFile}`, tag, "plugins/mmi"], {
16391
+ timeout: 6e4
16392
+ });
16393
+ await execFileP5("tar", ["-xf", tarFile, "-C", tmpRoot], { timeout: 6e4 });
16394
+ const pluginMmi = (0, import_node_path16.join)(tmpRoot, "plugins", "mmi");
16395
+ return (0, import_node_fs15.existsSync)((0, import_node_path16.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
16396
+ } catch {
16397
+ return void 0;
16398
+ }
16399
+ }
16400
+ async function downloadPluginMmiViaGh(tag, tmpRoot) {
16401
+ const tarPath = (0, import_node_path16.join)(tmpRoot, "repo.tgz");
16402
+ try {
16403
+ (0, import_node_fs15.mkdirSync)(tmpRoot, { recursive: true });
16404
+ const { stdout } = await execFileBuffer("gh", ghReleaseTarballApiArgs(tag), {
16405
+ timeout: 12e4,
16406
+ maxBuffer: 100 * 1024 * 1024,
16407
+ encoding: "buffer",
16408
+ windowsHide: true
16409
+ });
16410
+ (0, import_node_fs15.writeFileSync)(tarPath, stdout);
16411
+ await execFileBuffer("tar", ["-xzf", tarPath, "-C", tmpRoot], { timeout: 12e4, windowsHide: true });
16412
+ const top = (0, import_node_fs15.readdirSync)(tmpRoot).find((entry) => entry !== "repo.tgz");
16413
+ if (!top) return void 0;
16414
+ const pluginMmi = (0, import_node_path16.join)(tmpRoot, top, "plugins", "mmi");
16415
+ return (0, import_node_fs15.existsSync)((0, import_node_path16.join)(pluginMmi, PLUGIN_JSON_REL)) ? pluginMmi : void 0;
16416
+ } catch {
16417
+ return void 0;
16418
+ }
16419
+ }
16420
+ async function resolvePluginMmiSource(releasedVersion, hubCheckout, tmpRoot, execFileP5) {
16421
+ (0, import_node_fs15.mkdirSync)(tmpRoot, { recursive: true });
16422
+ const tag = releaseTag(releasedVersion);
16423
+ if (hubCheckout) {
16424
+ const fromHub = await extractPluginMmiFromHubCheckout(hubCheckout, tag, tmpRoot, execFileP5);
16425
+ if (fromHub) return fromHub;
16426
+ }
16427
+ return downloadPluginMmiViaGh(tag, (0, import_node_path16.join)(tmpRoot, "gh"));
16428
+ }
16429
+ function cursorPluginPinsNeedingSeed(pins, releasedVersion) {
16430
+ if (!isSemverVersion2(releasedVersion)) return pins.filter((pin) => !pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty);
16431
+ return pins.filter((pin) => {
16432
+ if (!pin.hasPluginJson || !pin.hasHooksJson || pin.isEmpty) return true;
16433
+ if (isSemverVersion2(pin.version) && compareVersions(pin.version, releasedVersion) < 0) return true;
16434
+ return false;
16435
+ });
16436
+ }
16437
+ function cursorPluginCacheSeedTargets(pins, releasedVersion, cacheRoot, cacheRootExists = false) {
16438
+ const needing = cursorPluginPinsNeedingSeed(pins, releasedVersion);
16439
+ if (needing.length > 0) return needing.map((pin) => pin.path);
16440
+ if (pins.length === 0 && cacheRoot && cacheRootExists && isSemverVersion2(releasedVersion)) {
16441
+ return [(0, import_node_path16.join)(cacheRoot, `v${releasedVersion.replace(/^v/, "")}`)];
16442
+ }
16443
+ return [];
16444
+ }
16445
+ async function applyCursorPluginCacheSeed(input) {
16446
+ if (!isSemverVersion2(input.releasedVersion)) return false;
16447
+ const targets = cursorPluginCacheSeedTargets(input.pins, input.releasedVersion, input.cacheRoot, input.cacheRootExists);
16448
+ if (targets.length === 0) return false;
16449
+ const tmpRoot = await input.mkdtemp("mmi-cursor-seed-");
16450
+ const source = await resolvePluginMmiSource(input.releasedVersion, input.hubCheckout, tmpRoot, input.execFileP);
16451
+ if (!source) return false;
16452
+ input.log(` \u21BB seeding Cursor MMI plugin cache \u2192 ${input.releasedVersion}\u2026`);
16453
+ for (const dest of targets) {
16454
+ syncDirContents(source, dest);
16455
+ }
16456
+ (0, import_node_fs15.rmSync)(tmpRoot, { recursive: true, force: true });
16457
+ return true;
16458
+ }
16459
+ var CURSOR_ENABLED_PIN_LABEL = "Cursor Team Marketplace enabled plugin pin";
16460
+ function cursorSplitBrainCapabilities() {
16461
+ const skills = OPENCODE_WORKFLOW_COMMANDS.map((c) => `/${c}`).join(", ");
16462
+ return `org skills (${skills}) and SessionStart hooks`;
16463
+ }
16464
+ function cursorEnabledPinRecovery() {
16465
+ return "refresh the MMI Team Marketplace in Cursor Dashboard \u2192 Settings \u2192 Plugins (Update), or run `mmi-cli doctor --apply` to seed the enabled pin; then restart Cursor";
16466
+ }
16467
+ var CURSOR_FOUND_CACHED_MMI_RE = /Found cached plugin:\s*mmi\s+at\s+.*?[\\/]([0-9a-f]{6,40})\b/gi;
16468
+ var CURSOR_MMI_LOADED_RE = /loadClaudePlugin\s+mmi@mutmutco\s+loaded/i;
16469
+ var CURSOR_GIT_AUTH_FAIL_RE = /unable to get password from user/i;
16470
+ var CURSOR_SAFE_PIN_NAME_RE = /^[0-9a-zA-Z._-]+$/;
16471
+ function parseCursorPluginLog(text) {
16472
+ let enabledPin;
16473
+ for (const m of text.matchAll(CURSOR_FOUND_CACHED_MMI_RE)) enabledPin = m[1];
16474
+ const loadSucceeded = CURSOR_MMI_LOADED_RE.test(text);
16475
+ const loadFailed = CURSOR_GIT_AUTH_FAIL_RE.test(text) && !loadSucceeded;
16476
+ return { enabledPin, loadSucceeded, loadFailed };
16477
+ }
16478
+ function buildCursorEnabledPinCheck(input) {
16479
+ const label = CURSOR_ENABLED_PIN_LABEL;
16480
+ const healthy = { ok: true, label, fix: "" };
16481
+ if (!input.isOrgRepo) return healthy;
16482
+ if (!input.enabledPin && !input.loadFailed) return healthy;
16483
+ if (input.loadSucceeded && !input.loadFailed) return healthy;
16484
+ const caps = cursorSplitBrainCapabilities();
16485
+ if (input.loadFailed) {
16486
+ return {
16487
+ ok: false,
16488
+ severityOverride: "hard",
16489
+ label,
16490
+ fix: `Cursor Team Marketplace failed to load mmi@mutmutco${input.enabledPin ? ` (${input.enabledPin})` : ""}: mmi-cli is on PATH and the Hub session is valid, but the MMI plugin did not load \u2014 ${caps} are UNAVAILABLE in Cursor. ${cursorEnabledPinRecovery()}`
16491
+ };
16492
+ }
16493
+ const enabledDir = input.pins.find((p) => p.name === input.enabledPin);
16494
+ const complete = !!enabledDir && !enabledDir.isEmpty && enabledDir.hasPluginJson && enabledDir.hasHooksJson;
16495
+ if (!complete) {
16496
+ const others = input.pins.filter((p) => p.name !== input.enabledPin && CURSOR_SAFE_PIN_NAME_RE.test(p.name)).map((p) => p.name);
16497
+ return {
16498
+ ok: false,
16499
+ severityOverride: "hard",
16500
+ label,
16501
+ fix: `Cursor is configured to load mmi@mutmutco at commit ${input.enabledPin}, but that pin is ${enabledDir ? "incomplete" : "missing"} in the cache${others.length ? ` (a complete pin at ${others.join(", ")} is present but will NOT be loaded)` : ""} \u2014 ${caps} are unavailable in Cursor. ${cursorEnabledPinRecovery()}`
16502
+ };
16503
+ }
16504
+ return healthy;
16505
+ }
16506
+
16357
16507
  // src/mcp-reconcile.ts
16358
16508
  var PLAYWRIGHT_MCP_SPEC = {
16359
16509
  name: "playwright",
@@ -16662,9 +16812,9 @@ ${block}
16662
16812
  }
16663
16813
 
16664
16814
  // src/cli-doctor-shared.ts
16665
- var import_node_fs15 = require("node:fs");
16666
- var import_node_path16 = require("node:path");
16667
16815
  var import_node_fs16 = require("node:fs");
16816
+ var import_node_path17 = require("node:path");
16817
+ var import_node_fs17 = require("node:fs");
16668
16818
  var GC_GH_TIMEOUT_MS = 2e4;
16669
16819
  async function awsCallerArn() {
16670
16820
  try {
@@ -16710,7 +16860,7 @@ async function localBranchHeads() {
16710
16860
  }
16711
16861
  async function currentRepoWorktreeGitRoot(repoRoot) {
16712
16862
  const gitCommonDir = (await execFileP2("git", ["rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
16713
- return gitCommonDir ? (0, import_node_path16.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
16863
+ return gitCommonDir ? (0, import_node_path17.resolve)(repoRoot, gitCommonDir, "worktrees") : "";
16714
16864
  }
16715
16865
  async function worktreeBranches() {
16716
16866
  const { stdout } = await execFileP2("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
@@ -16730,18 +16880,18 @@ function resolveGitdirForWorktreeFile(worktreePath, content) {
16730
16880
  const match = /^gitdir:\s*(.+)\s*$/im.exec(content);
16731
16881
  if (!match?.[1]) return void 0;
16732
16882
  const raw = match[1].trim();
16733
- return (0, import_node_path16.isAbsolute)(raw) ? raw : (0, import_node_path16.resolve)(worktreePath, raw);
16883
+ return (0, import_node_path17.isAbsolute)(raw) ? raw : (0, import_node_path17.resolve)(worktreePath, raw);
16734
16884
  }
16735
16885
  function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
16736
16886
  if (!worktreeGitRoot) return false;
16737
16887
  try {
16738
- const entries = (0, import_node_fs16.readdirSync)(worktreeGitRoot, { withFileTypes: true });
16888
+ const entries = (0, import_node_fs17.readdirSync)(worktreeGitRoot, { withFileTypes: true });
16739
16889
  for (const ent of entries) {
16740
16890
  if (!ent.isDirectory()) continue;
16741
16891
  try {
16742
- const gitdirPath = (0, import_node_fs15.readFileSync)((0, import_node_path16.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
16743
- const resolvedGitdir = (0, import_node_path16.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path16.resolve)(worktreeGitRoot, ent.name, gitdirPath);
16744
- if (sameWorktreeMetadataPath((0, import_node_path16.dirname)(resolvedGitdir), worktreePath)) return true;
16892
+ const gitdirPath = (0, import_node_fs16.readFileSync)((0, import_node_path17.join)(worktreeGitRoot, ent.name, "gitdir"), "utf8").trim();
16893
+ const resolvedGitdir = (0, import_node_path17.isAbsolute)(gitdirPath) ? gitdirPath : (0, import_node_path17.resolve)(worktreeGitRoot, ent.name, gitdirPath);
16894
+ if (sameWorktreeMetadataPath((0, import_node_path17.dirname)(resolvedGitdir), worktreePath)) return true;
16745
16895
  } catch {
16746
16896
  }
16747
16897
  }
@@ -16751,7 +16901,7 @@ function metadataOwnsMissingWorktreeDir(worktreePath, worktreeGitRoot) {
16751
16901
  }
16752
16902
  function pathExistsKnown(path2) {
16753
16903
  try {
16754
- (0, import_node_fs16.statSync)(path2);
16904
+ (0, import_node_fs17.statSync)(path2);
16755
16905
  return true;
16756
16906
  } catch (e) {
16757
16907
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
@@ -16760,10 +16910,10 @@ function pathExistsKnown(path2) {
16760
16910
  }
16761
16911
  }
16762
16912
  function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
16763
- const gitPath = (0, import_node_path16.join)(path2, ".git");
16913
+ const gitPath = (0, import_node_path17.join)(path2, ".git");
16764
16914
  let st;
16765
16915
  try {
16766
- st = (0, import_node_fs16.lstatSync)(gitPath);
16916
+ st = (0, import_node_fs17.lstatSync)(gitPath);
16767
16917
  } catch (e) {
16768
16918
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
16769
16919
  if (code === "ENOENT" || code === "ENOTDIR") {
@@ -16780,7 +16930,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
16780
16930
  if (st.isDirectory()) return { path: path2, gitType: "dir" };
16781
16931
  if (!st.isFile()) return { path: path2, gitType: "other" };
16782
16932
  try {
16783
- const gitFileContent = (0, import_node_fs15.readFileSync)(gitPath, "utf8");
16933
+ const gitFileContent = (0, import_node_fs16.readFileSync)(gitPath, "utf8");
16784
16934
  const gitdir = resolveGitdirForWorktreeFile(path2, gitFileContent);
16785
16935
  const gitDirExists = gitdir ? pathExistsKnown(gitdir) : false;
16786
16936
  return {
@@ -16797,7 +16947,7 @@ function inspectSiblingWorktreeDir(path2, worktreeGitRoot) {
16797
16947
  }
16798
16948
  function inspectDeadWorktreeDirContent(path2) {
16799
16949
  try {
16800
- return { entries: (0, import_node_fs16.readdirSync)(path2) };
16950
+ return { entries: (0, import_node_fs17.readdirSync)(path2) };
16801
16951
  } catch (e) {
16802
16952
  const code = typeof e === "object" && e && "code" in e ? String(e.code ?? "") : "";
16803
16953
  return { error: code ? `unable to inspect directory contents (${code})` : "unable to inspect directory contents" };
@@ -16816,8 +16966,8 @@ async function siblingWorktreeDirs() {
16816
16966
  const worktreeGitRoot = await currentRepoWorktreeGitRoot(repoRoot);
16817
16967
  const siblingRoot = siblingMmiWorktreesRoot(repoRoot);
16818
16968
  try {
16819
- const entries = (0, import_node_fs16.readdirSync)(siblingRoot, { withFileTypes: true });
16820
- return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path16.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
16969
+ const entries = (0, import_node_fs17.readdirSync)(siblingRoot, { withFileTypes: true });
16970
+ return entries.filter((ent) => ent.isDirectory()).map((ent) => inspectSiblingWorktreeDir((0, import_node_path17.join)(siblingRoot, ent.name), worktreeGitRoot)).filter((entry) => Boolean(entry));
16821
16971
  } catch {
16822
16972
  return [];
16823
16973
  }
@@ -16867,7 +17017,7 @@ async function fetchHubVersionInfo(baseUrl) {
16867
17017
  }
16868
17018
  function readRepoVersion() {
16869
17019
  try {
16870
- return JSON.parse((0, import_node_fs17.readFileSync)((0, import_node_path17.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
17020
+ return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
16871
17021
  } catch {
16872
17022
  return void 0;
16873
17023
  }
@@ -16933,15 +17083,15 @@ function reexecAttemptOutcome(strategy, code) {
16933
17083
  return "result";
16934
17084
  }
16935
17085
  function spawnReexecAttempt(command, args, env) {
16936
- return new Promise((resolve6) => {
17086
+ return new Promise((resolve5) => {
16937
17087
  let settled = false;
16938
17088
  const done = (code) => {
16939
17089
  if (!settled) {
16940
17090
  settled = true;
16941
- resolve6(code);
17091
+ resolve5(code);
16942
17092
  }
16943
17093
  };
16944
- const child = (0, import_node_child_process10.spawn)(command, args, { stdio: "inherit", env });
17094
+ const child = (0, import_node_child_process11.spawn)(command, args, { stdio: "inherit", env });
16945
17095
  child.on("error", () => done(-1));
16946
17096
  child.on("exit", (code) => done(code ?? 0));
16947
17097
  });
@@ -17016,30 +17166,42 @@ async function applyPluginHeal(token, surface, log, opts) {
17016
17166
  }
17017
17167
  var installedPluginsPath = (surface = detectSurface(process.env)) => {
17018
17168
  const homeDir = surface === "codex" ? ".codex" : ".claude";
17019
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), homeDir, "plugins", "installed_plugins.json");
17169
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), homeDir, "plugins", "installed_plugins.json");
17020
17170
  };
17021
17171
  function readInstalledPlugins(surface = detectSurface(process.env)) {
17022
17172
  try {
17023
- return JSON.parse((0, import_node_fs17.readFileSync)(installedPluginsPath(surface), "utf8"));
17173
+ return JSON.parse((0, import_node_fs18.readFileSync)(installedPluginsPath(surface), "utf8"));
17024
17174
  } catch {
17025
17175
  return null;
17026
17176
  }
17027
17177
  }
17178
+ function marketplaceCloneCandidates(surface, home) {
17179
+ if (surface === "codex") {
17180
+ return [
17181
+ (0, import_node_path18.join)(home, ".codex", ".tmp", "marketplaces", "mutmutco"),
17182
+ (0, import_node_path18.join)(home, ".codex", "plugins", "marketplaces", "mutmutco")
17183
+ ];
17184
+ }
17185
+ return [(0, import_node_path18.join)(home, ".claude", "plugins", "marketplaces", "mutmutco")];
17186
+ }
17187
+ function marketplaceClonePresent(surface, home, exists = import_node_fs18.existsSync) {
17188
+ return marketplaceCloneCandidates(surface, home).some(exists);
17189
+ }
17028
17190
  function snapshotPluginGuardInput(surface = detectSurface(process.env), isOrgRepo = false) {
17029
17191
  const homeDir = surface === "codex" ? ".codex" : ".claude";
17030
17192
  const installed = readInstalledPlugins(surface);
17031
17193
  return {
17032
17194
  isOrgRepo,
17033
17195
  installRecordPresent: hasUserInstallRecord(installed, MMI_PLUGIN_ID) || hasProjectInstallRecord(installed, MMI_PLUGIN_ID, process.cwd()),
17034
- marketplaceClonePresent: (0, import_node_fs17.existsSync)((0, import_node_path17.join)((0, import_node_os6.homedir)(), homeDir, "plugins", "marketplaces", "mutmutco")),
17035
- pluginCachePresent: (0, import_node_fs17.existsSync)((0, import_node_path17.join)((0, import_node_os6.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
17196
+ marketplaceClonePresent: marketplaceClonePresent(surface, (0, import_node_os7.homedir)()),
17197
+ pluginCachePresent: (0, import_node_fs18.existsSync)((0, import_node_path18.join)((0, import_node_os7.homedir)(), homeDir, "plugins", "cache", "mutmutco", "mmi"))
17036
17198
  };
17037
17199
  }
17038
17200
  function installedPluginSources() {
17039
17201
  return ["claude", "codex"].map((surface) => {
17040
- const recordPath = (0, import_node_path17.join)((0, import_node_os6.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
17202
+ const recordPath = (0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "installed_plugins.json");
17041
17203
  try {
17042
- return { surface, installed: JSON.parse((0, import_node_fs17.readFileSync)(recordPath, "utf8")), recordPath };
17204
+ return { surface, installed: JSON.parse((0, import_node_fs18.readFileSync)(recordPath, "utf8")), recordPath };
17043
17205
  } catch {
17044
17206
  return { surface, installed: null, recordPath };
17045
17207
  }
@@ -17052,8 +17214,8 @@ function managedPluginSurfaceSources() {
17052
17214
  const segments = parseManagedPluginId(descriptor.id);
17053
17215
  if (!segments) continue;
17054
17216
  try {
17055
- cacheVersions[descriptor.id] = (0, import_node_fs17.readdirSync)(
17056
- (0, import_node_path17.join)((0, import_node_os6.homedir)(), `.${surface}`, "plugins", "cache", segments.marketplace, segments.name),
17217
+ cacheVersions[descriptor.id] = (0, import_node_fs18.readdirSync)(
17218
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), `.${surface}`, "plugins", "cache", segments.marketplace, segments.name),
17057
17219
  { withFileTypes: true }
17058
17220
  ).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
17059
17221
  } catch {
@@ -17073,8 +17235,8 @@ function managedPluginCursorDirectVersions() {
17073
17235
  const segments = parseManagedPluginId(descriptor.id);
17074
17236
  if (!segments) continue;
17075
17237
  try {
17076
- const versions = (0, import_node_fs17.readdirSync)(
17077
- (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cursor", "plugins", "cache", segments.marketplace, segments.name),
17238
+ const versions = (0, import_node_fs18.readdirSync)(
17239
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", segments.marketplace, segments.name),
17078
17240
  { withFileTypes: true }
17079
17241
  ).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name);
17080
17242
  out[descriptor.id] = highestSemver(versions);
@@ -17093,7 +17255,7 @@ function managedPluginOpencodeDirectVersions() {
17093
17255
  }
17094
17256
  function readOpencodePackageVersionFrom(packageJsonPath) {
17095
17257
  try {
17096
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(packageJsonPath, "utf8"));
17258
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(packageJsonPath, "utf8"));
17097
17259
  return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
17098
17260
  } catch {
17099
17261
  return void 0;
@@ -17103,14 +17265,14 @@ function readOpencodePackageVersion(packageName) {
17103
17265
  const [scope, name] = packageName.startsWith("@") ? packageName.slice(1).split("/", 2) : [void 0, packageName];
17104
17266
  if (!name) return void 0;
17105
17267
  const diskCandidates = [
17106
- (0, import_node_path17.join)(opencodeConfigDir(), "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"),
17107
- (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cache", "opencode", "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json")
17268
+ (0, import_node_path18.join)(opencodeConfigDir(), "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"),
17269
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json")
17108
17270
  ];
17109
17271
  const diskVersion = diskCandidates.map(readOpencodePackageVersionFrom).find((v) => v !== void 0);
17110
- const packagesRoot = (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cache", "opencode", "packages", ...scope ? [`@${scope}`] : []);
17272
+ const packagesRoot = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", ...scope ? [`@${scope}`] : []);
17111
17273
  let cacheVersion;
17112
17274
  try {
17113
- const cacheVersions = (0, import_node_fs17.readdirSync)(packagesRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith(`${name}@`) && !e.name.startsWith(".")).map((e) => readOpencodePackageVersionFrom((0, import_node_path17.join)(packagesRoot, e.name, "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"))).filter((v) => Boolean(v));
17275
+ const cacheVersions = (0, import_node_fs18.readdirSync)(packagesRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith(`${name}@`) && !e.name.startsWith(".")).map((e) => readOpencodePackageVersionFrom((0, import_node_path18.join)(packagesRoot, e.name, "node_modules", ...scope ? [`@${scope}`] : [], name, "package.json"))).filter((v) => Boolean(v));
17114
17276
  if (cacheVersions.length) cacheVersion = cacheVersions.reduce((lowest, v) => compareVersions(v, lowest) < 0 ? v : lowest);
17115
17277
  } catch {
17116
17278
  }
@@ -17118,7 +17280,7 @@ function readOpencodePackageVersion(packageName) {
17118
17280
  }
17119
17281
  function readClaudeSettings() {
17120
17282
  try {
17121
- return JSON.parse((0, import_node_fs17.readFileSync)((0, import_node_path17.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
17283
+ return JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path18.join)(process.cwd(), ".claude", "settings.json"), "utf8"));
17122
17284
  } catch {
17123
17285
  return null;
17124
17286
  }
@@ -17140,7 +17302,7 @@ function writeProjectInstallRecord(record) {
17140
17302
  const list = file.plugins[MMI_PLUGIN_ID] ?? [];
17141
17303
  list.push(record);
17142
17304
  file.plugins[MMI_PLUGIN_ID] = list;
17143
- (0, import_node_fs17.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
17305
+ (0, import_node_fs18.writeFileSync)(installedPluginsPath(), `${JSON.stringify(file, null, 2)}
17144
17306
  `, "utf8");
17145
17307
  return true;
17146
17308
  } catch {
@@ -17153,9 +17315,9 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
17153
17315
  if (!file) return false;
17154
17316
  if (!file.plugins) file.plugins = {};
17155
17317
  const path2 = installedPluginsPath();
17156
- (0, import_node_fs17.copyFileSync)(path2, `${path2}.bak`);
17318
+ (0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
17157
17319
  file.plugins[pluginId] = records;
17158
- (0, import_node_fs17.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
17320
+ (0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify(file, null, 2)}
17159
17321
  `, "utf8");
17160
17322
  return true;
17161
17323
  } catch {
@@ -17163,22 +17325,22 @@ function backupAndWriteInstalledPlugins(records, pluginId) {
17163
17325
  }
17164
17326
  }
17165
17327
  function opencodeConfigDir() {
17166
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".config", "opencode");
17328
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "opencode");
17167
17329
  }
17168
17330
  function opencodeConfigPath() {
17169
- return (0, import_node_path17.join)(opencodeConfigDir(), "opencode.jsonc");
17331
+ return (0, import_node_path18.join)(opencodeConfigDir(), "opencode.jsonc");
17170
17332
  }
17171
17333
  function opencodeCommandsDir() {
17172
- return (0, import_node_path17.join)(opencodeConfigDir(), "commands");
17334
+ return (0, import_node_path18.join)(opencodeConfigDir(), "commands");
17173
17335
  }
17174
17336
  function opencodeSkillsPath() {
17175
- return (0, import_node_path17.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
17337
+ return (0, import_node_path18.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "skills");
17176
17338
  }
17177
17339
  function opencodeConfigSnapshot() {
17178
17340
  const path2 = opencodeConfigPath();
17179
- if (!(0, import_node_fs17.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
17341
+ if (!(0, import_node_fs18.existsSync)(path2)) return { path: path2, hasConfig: false, hasPluginField: false, parseOk: true };
17180
17342
  try {
17181
- const raw = (0, import_node_fs17.readFileSync)(path2, "utf8");
17343
+ const raw = (0, import_node_fs18.readFileSync)(path2, "utf8");
17182
17344
  const parsed = JSON.parse(stripJsonc(raw));
17183
17345
  const hasPluginField = Object.prototype.hasOwnProperty.call(parsed, "plugin");
17184
17346
  const skillsPaths = Array.isArray(parsed.skills?.paths) ? parsed.skills.paths.filter((p) => typeof p === "string") : void 0;
@@ -17201,9 +17363,9 @@ function writeOpencodeConfigPlugin(snapshot) {
17201
17363
  const plan = planOpencodeConfigWrite(snapshot.hasConfig ? snapshot.raw : void 0);
17202
17364
  if (plan.action === "already") return true;
17203
17365
  if (!plan.text || plan.action === "unsafe") return false;
17204
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(path2), { recursive: true });
17205
- if (snapshot.hasConfig) (0, import_node_fs17.copyFileSync)(path2, `${path2}.bak`);
17206
- (0, import_node_fs17.writeFileSync)(path2, plan.text, "utf8");
17366
+ (0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(path2), { recursive: true });
17367
+ if (snapshot.hasConfig) (0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
17368
+ (0, import_node_fs18.writeFileSync)(path2, plan.text, "utf8");
17207
17369
  return true;
17208
17370
  } catch {
17209
17371
  return false;
@@ -17218,9 +17380,9 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
17218
17380
  const normalized = skillsPath.replace(/\\/g, "/");
17219
17381
  if (!paths.some((p) => p.replace(/\\/g, "/") === normalized)) paths.push(skillsPath.replace(/\\/g, "/"));
17220
17382
  parsed.skills = { ...skills, paths };
17221
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(snapshot.path), { recursive: true });
17222
- if (snapshot.hasConfig && (0, import_node_fs17.existsSync)(snapshot.path)) (0, import_node_fs17.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
17223
- (0, import_node_fs17.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
17383
+ (0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(snapshot.path), { recursive: true });
17384
+ if (snapshot.hasConfig && (0, import_node_fs18.existsSync)(snapshot.path)) (0, import_node_fs18.copyFileSync)(snapshot.path, `${snapshot.path}.bak`);
17385
+ (0, import_node_fs18.writeFileSync)(snapshot.path, `${JSON.stringify(parsed, null, 2)}
17224
17386
  `, "utf8");
17225
17387
  return true;
17226
17388
  } catch {
@@ -17229,7 +17391,7 @@ function writeOpencodeSkillsPath(snapshot, skillsPath) {
17229
17391
  }
17230
17392
  function opencodeExistingCommands() {
17231
17393
  try {
17232
- return (0, import_node_fs17.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
17394
+ return (0, import_node_fs18.readdirSync)(opencodeCommandsDir(), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name.slice(0, -3).toLowerCase());
17233
17395
  } catch {
17234
17396
  return [];
17235
17397
  }
@@ -17237,9 +17399,9 @@ function opencodeExistingCommands() {
17237
17399
  function writeOpencodeCommandFiles() {
17238
17400
  try {
17239
17401
  const dir = opencodeCommandsDir();
17240
- (0, import_node_fs17.mkdirSync)(dir, { recursive: true });
17402
+ (0, import_node_fs18.mkdirSync)(dir, { recursive: true });
17241
17403
  for (const command of OPENCODE_WORKFLOW_COMMANDS) {
17242
- (0, import_node_fs17.writeFileSync)((0, import_node_path17.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
17404
+ (0, import_node_fs18.writeFileSync)((0, import_node_path18.join)(dir, `${command}.md`), opencodeCommandMarkdown(command), "utf8");
17243
17405
  }
17244
17406
  return true;
17245
17407
  } catch {
@@ -17248,12 +17410,12 @@ function writeOpencodeCommandFiles() {
17248
17410
  }
17249
17411
  function readOpencodeAdapterDiskVersion() {
17250
17412
  const candidates = [
17251
- (0, import_node_path17.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
17252
- (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
17413
+ (0, import_node_path18.join)(opencodeConfigDir(), "node_modules", "@mutmutco", "opencode-mmi", "package.json"),
17414
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "node_modules", "@mutmutco", "opencode-mmi", "package.json")
17253
17415
  ];
17254
17416
  for (const path2 of candidates) {
17255
17417
  try {
17256
- const parsed = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
17418
+ const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
17257
17419
  if (typeof parsed.version === "string" && parsed.version.trim()) return parsed.version.trim();
17258
17420
  } catch {
17259
17421
  continue;
@@ -17269,7 +17431,7 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
17269
17431
  try {
17270
17432
  const specs = opencodeMmiPluginSpecs(snapshot);
17271
17433
  log(` \u21BB force-refreshing OpenCode MMI npm plugin(s): ${specs.join(", ")}\u2026`);
17272
- (0, import_node_fs17.mkdirSync)(opencodeConfigDir(), { recursive: true });
17434
+ (0, import_node_fs18.mkdirSync)(opencodeConfigDir(), { recursive: true });
17273
17435
  await runHostBin("npm", ["install", "--prefix", opencodeConfigDir(), "--force", ...specs], { timeout: NPM_UPDATE_TIMEOUT_MS });
17274
17436
  return true;
17275
17437
  } catch {
@@ -17277,12 +17439,12 @@ async function forceInstallOpencodeMmiPlugins(snapshot, log) {
17277
17439
  }
17278
17440
  }
17279
17441
  function opencodePackagesRoot() {
17280
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cache", "opencode", "packages", "@mutmutco");
17442
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cache", "opencode", "packages", "@mutmutco");
17281
17443
  }
17282
17444
  function opencodePluginCacheDirs() {
17283
17445
  const root = opencodePackagesRoot();
17284
17446
  try {
17285
- return (0, import_node_fs17.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("opencode-mmi@") && !e.name.startsWith(".")).map((e) => (0, import_node_path17.join)(root, e.name));
17447
+ return (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("opencode-mmi@") && !e.name.startsWith(".")).map((e) => (0, import_node_path18.join)(root, e.name));
17286
17448
  } catch {
17287
17449
  return [];
17288
17450
  }
@@ -17290,7 +17452,7 @@ function opencodePluginCacheDirs() {
17290
17452
  function readOpencodeCacheDirVersion(cacheDir) {
17291
17453
  try {
17292
17454
  const parsed = JSON.parse(
17293
- (0, import_node_fs17.readFileSync)((0, import_node_path17.join)(cacheDir, "node_modules", "@mutmutco", "opencode-mmi", "package.json"), "utf8")
17455
+ (0, import_node_fs18.readFileSync)((0, import_node_path18.join)(cacheDir, "node_modules", "@mutmutco", "opencode-mmi", "package.json"), "utf8")
17294
17456
  );
17295
17457
  return typeof parsed.version === "string" && parsed.version.trim() ? parsed.version.trim() : void 0;
17296
17458
  } catch {
@@ -17313,9 +17475,9 @@ function quarantineStaleOpencodePluginCaches(releasedVersion, log) {
17313
17475
  });
17314
17476
  if (!plan.quarantine) continue;
17315
17477
  try {
17316
- const quarantineRoot = (0, import_node_path17.join)(opencodePackagesRoot(), ".mmi-quarantine", stamp);
17317
- (0, import_node_fs17.mkdirSync)(quarantineRoot, { recursive: true });
17318
- (0, import_node_fs17.renameSync)(cacheDir, (0, import_node_path17.join)(quarantineRoot, (0, import_node_path17.basename)(cacheDir)));
17478
+ const quarantineRoot = (0, import_node_path18.join)(opencodePackagesRoot(), ".mmi-quarantine", stamp);
17479
+ (0, import_node_fs18.mkdirSync)(quarantineRoot, { recursive: true });
17480
+ (0, import_node_fs18.renameSync)(cacheDir, (0, import_node_path18.join)(quarantineRoot, (0, import_node_path18.basename)(cacheDir)));
17319
17481
  log(` \u21BB quarantined stale OpenCode plugin cache ${plan.cacheVersion} (< ${plan.releasedVersion}) \u2014 OpenCode reinstalls the current adapter on next start`);
17320
17482
  moved = true;
17321
17483
  } catch {
@@ -17341,30 +17503,30 @@ function opencodePluginVersionsForReport() {
17341
17503
  }
17342
17504
  function opencodeDesktopLogsRoot() {
17343
17505
  if (process.platform === "win32") {
17344
- const base = process.env.APPDATA || (0, import_node_path17.join)((0, import_node_os6.homedir)(), "AppData", "Roaming");
17345
- return (0, import_node_path17.join)(base, "ai.opencode.desktop", "logs");
17506
+ const base = process.env.APPDATA || (0, import_node_path18.join)((0, import_node_os7.homedir)(), "AppData", "Roaming");
17507
+ return (0, import_node_path18.join)(base, "ai.opencode.desktop", "logs");
17346
17508
  }
17347
17509
  if (process.platform === "darwin") {
17348
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
17510
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "Library", "Application Support", "ai.opencode.desktop", "logs");
17349
17511
  }
17350
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".config", "ai.opencode.desktop", "logs");
17512
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "ai.opencode.desktop", "logs");
17351
17513
  }
17352
17514
  function opencodeDesktopBootstrapSnapshot() {
17353
17515
  const root = opencodeDesktopLogsRoot();
17354
17516
  try {
17355
- const sessionDirs = (0, import_node_fs17.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path17.join)(root, entry.name)).sort((a, b) => (0, import_node_fs17.statSync)(b).mtimeMs - (0, import_node_fs17.statSync)(a).mtimeMs);
17517
+ const sessionDirs = (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => (0, import_node_path18.join)(root, entry.name)).sort((a, b) => (0, import_node_fs18.statSync)(b).mtimeMs - (0, import_node_fs18.statSync)(a).mtimeMs);
17356
17518
  const newest = sessionDirs[0];
17357
17519
  if (!newest) return [];
17358
- const logPath = (0, import_node_path17.join)(newest, "renderer.log");
17359
- const text = (0, import_node_fs17.readFileSync)(logPath, "utf8");
17360
- return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs17.existsSync)(directory)).map((directory) => ({ directory, logPath }));
17520
+ const logPath = (0, import_node_path18.join)(newest, "renderer.log");
17521
+ const text = (0, import_node_fs18.readFileSync)(logPath, "utf8");
17522
+ return opencodeAgentDirectoriesFromLog(text).filter((directory) => !(0, import_node_fs18.existsSync)(directory)).map((directory) => ({ directory, logPath }));
17361
17523
  } catch {
17362
17524
  return [];
17363
17525
  }
17364
17526
  }
17365
17527
  function opencodeLegacyConfigSnapshot() {
17366
- const legacyPath = (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".opencode", "opencode.json");
17367
- if (!(0, import_node_fs17.existsSync)(legacyPath)) return {};
17528
+ const legacyPath = (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".opencode", "opencode.json");
17529
+ if (!(0, import_node_fs18.existsSync)(legacyPath)) return {};
17368
17530
  const content = readTextFile(legacyPath);
17369
17531
  if (content == null) return {};
17370
17532
  const plugins = parseOpencodeLegacyConfigPlugins(content);
@@ -17376,45 +17538,80 @@ function opencodeLegacyConfigSnapshot() {
17376
17538
  function quarantineOpencodeLegacyConfig(legacyPath) {
17377
17539
  try {
17378
17540
  const backupPath = `${legacyPath}.bak`;
17379
- if ((0, import_node_fs17.existsSync)(backupPath)) return false;
17380
- (0, import_node_fs17.renameSync)(legacyPath, backupPath);
17541
+ if ((0, import_node_fs18.existsSync)(backupPath)) return false;
17542
+ (0, import_node_fs18.renameSync)(legacyPath, backupPath);
17381
17543
  return true;
17382
17544
  } catch {
17383
17545
  return false;
17384
17546
  }
17385
17547
  }
17386
17548
  function cursorPluginCacheRoot() {
17387
- return (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
17549
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".cursor", "plugins", "cache", "mutmutco", "mmi");
17550
+ }
17551
+ function cursorLogsRoot() {
17552
+ if (process.platform === "win32") return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "AppData", "Roaming", "Cursor", "logs");
17553
+ if (process.platform === "darwin") return (0, import_node_path18.join)((0, import_node_os7.homedir)(), "Library", "Application Support", "Cursor", "logs");
17554
+ return (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".config", "Cursor", "logs");
17555
+ }
17556
+ function safeReaddirNames(dir) {
17557
+ try {
17558
+ return (0, import_node_fs18.readdirSync)(dir, { withFileTypes: true }).map((e) => e.name);
17559
+ } catch {
17560
+ return [];
17561
+ }
17562
+ }
17563
+ function readCursorPluginLogText() {
17564
+ const root = cursorLogsRoot();
17565
+ const sessions = safeReaddirNames(root).sort().reverse();
17566
+ for (const session of sessions.slice(0, 5)) {
17567
+ const sessionDir = (0, import_node_path18.join)(root, session);
17568
+ const texts = [];
17569
+ for (const win of safeReaddirNames(sessionDir).filter((n) => n.startsWith("window"))) {
17570
+ const dir = (0, import_node_path18.join)(sessionDir, win, "exthost", "anysphere.cursor-agent-exec");
17571
+ for (const file of safeReaddirNames(dir)) {
17572
+ if (!/^Cursor Plugins.*\.log$/i.test(file)) continue;
17573
+ const path2 = (0, import_node_path18.join)(dir, file);
17574
+ try {
17575
+ texts.push({ text: (0, import_node_fs18.readFileSync)(path2, "utf8"), mtime: (0, import_node_fs18.statSync)(path2).mtimeMs });
17576
+ } catch {
17577
+ }
17578
+ }
17579
+ }
17580
+ if (texts.length) {
17581
+ return texts.sort((a, b) => b.mtime - a.mtime).map((t) => t.text).join("\n");
17582
+ }
17583
+ }
17584
+ return void 0;
17388
17585
  }
17389
17586
  function cursorPluginCachePinSnapshots() {
17390
17587
  const root = cursorPluginCacheRoot();
17391
17588
  try {
17392
- return (0, import_node_fs17.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
17393
- const path2 = (0, import_node_path17.join)(root, entry.name);
17394
- const pluginJson = (0, import_node_path17.join)(path2, ".cursor-plugin", "plugin.json");
17395
- const hooksJson = (0, import_node_path17.join)(path2, "hooks", "hooks.json");
17396
- const cliBundle = (0, import_node_path17.join)(path2, "cli", "dist", "index.cjs");
17397
- const cursorHook = (0, import_node_path17.join)(path2, "scripts", "cursor-hook.mjs");
17589
+ return (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
17590
+ const path2 = (0, import_node_path18.join)(root, entry.name);
17591
+ const pluginJson = (0, import_node_path18.join)(path2, ".cursor-plugin", "plugin.json");
17592
+ const hooksJson = (0, import_node_path18.join)(path2, "hooks", "hooks.json");
17593
+ const cliBundle = (0, import_node_path18.join)(path2, "cli", "dist", "index.cjs");
17594
+ const cursorHook = (0, import_node_path18.join)(path2, "scripts", "cursor-hook.mjs");
17398
17595
  let version;
17399
17596
  try {
17400
- const raw = JSON.parse((0, import_node_fs17.readFileSync)(pluginJson, "utf8"));
17597
+ const raw = JSON.parse((0, import_node_fs18.readFileSync)(pluginJson, "utf8"));
17401
17598
  version = typeof raw.version === "string" ? raw.version : void 0;
17402
17599
  } catch {
17403
17600
  version = void 0;
17404
17601
  }
17405
17602
  let isEmpty = true;
17406
17603
  try {
17407
- isEmpty = (0, import_node_fs17.readdirSync)(path2).length === 0;
17604
+ isEmpty = (0, import_node_fs18.readdirSync)(path2).length === 0;
17408
17605
  } catch {
17409
17606
  isEmpty = true;
17410
17607
  }
17411
17608
  return {
17412
17609
  name: entry.name,
17413
17610
  path: path2,
17414
- hasPluginJson: (0, import_node_fs17.existsSync)(pluginJson),
17415
- hasHooksJson: (0, import_node_fs17.existsSync)(hooksJson),
17416
- hasCliBundle: (0, import_node_fs17.existsSync)(cliBundle),
17417
- hasCursorHookScript: (0, import_node_fs17.existsSync)(cursorHook),
17611
+ hasPluginJson: (0, import_node_fs18.existsSync)(pluginJson),
17612
+ hasHooksJson: (0, import_node_fs18.existsSync)(hooksJson),
17613
+ hasCliBundle: (0, import_node_fs18.existsSync)(cliBundle),
17614
+ hasCursorHookScript: (0, import_node_fs18.existsSync)(cursorHook),
17418
17615
  isEmpty,
17419
17616
  version
17420
17617
  };
@@ -17424,19 +17621,19 @@ function cursorPluginCachePinSnapshots() {
17424
17621
  }
17425
17622
  }
17426
17623
  function hubCheckoutForCursorSeed() {
17427
- const manifest = (0, import_node_path17.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
17428
- return (0, import_node_fs17.existsSync)(manifest) ? process.cwd() : void 0;
17624
+ const manifest = (0, import_node_path18.join)(process.cwd(), "plugins", "mmi", ".cursor-plugin", "plugin.json");
17625
+ return (0, import_node_fs18.existsSync)(manifest) ? process.cwd() : void 0;
17429
17626
  }
17430
17627
  function mmiPluginCacheRootSnapshots() {
17431
17628
  const roots = [
17432
- { surface: "claude", root: (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
17433
- { surface: "codex", root: (0, import_node_path17.join)((0, import_node_os6.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
17629
+ { surface: "claude", root: (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".claude", "plugins", "cache", "mutmutco", "mmi") },
17630
+ { surface: "codex", root: (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".codex", "plugins", "cache", "mutmutco", "mmi") }
17434
17631
  ];
17435
17632
  return roots.flatMap(({ surface, root }) => {
17436
17633
  try {
17437
- const entries = (0, import_node_fs17.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
17634
+ const entries = (0, import_node_fs18.readdirSync)(root, { withFileTypes: true }).map((entry) => ({
17438
17635
  name: entry.name,
17439
- path: (0, import_node_path17.join)(root, entry.name),
17636
+ path: (0, import_node_path18.join)(root, entry.name),
17440
17637
  isDirectory: entry.isDirectory()
17441
17638
  }));
17442
17639
  return [{ surface, root, entries }];
@@ -17447,7 +17644,7 @@ function mmiPluginCacheRootSnapshots() {
17447
17644
  }
17448
17645
  function hasNestedMmiChild(versionDir) {
17449
17646
  try {
17450
- return (0, import_node_fs17.statSync)((0, import_node_path17.join)(versionDir, "mmi")).isDirectory();
17647
+ return (0, import_node_fs18.statSync)((0, import_node_path18.join)(versionDir, "mmi")).isDirectory();
17451
17648
  } catch {
17452
17649
  return false;
17453
17650
  }
@@ -17458,10 +17655,10 @@ function nestedPluginTreeSnapshot() {
17458
17655
  );
17459
17656
  }
17460
17657
  function uniqueQuarantineTarget(path2) {
17461
- if (!(0, import_node_fs17.existsSync)(path2)) return path2;
17658
+ if (!(0, import_node_fs18.existsSync)(path2)) return path2;
17462
17659
  for (let i = 1; i < 100; i += 1) {
17463
17660
  const candidate = `${path2}-${i}`;
17464
- if (!(0, import_node_fs17.existsSync)(candidate)) return candidate;
17661
+ if (!(0, import_node_fs18.existsSync)(candidate)) return candidate;
17465
17662
  }
17466
17663
  return `${path2}-${Date.now()}`;
17467
17664
  }
@@ -17470,10 +17667,10 @@ function quarantinePluginCacheDirs(plan) {
17470
17667
  const failed = [];
17471
17668
  for (const move of plan) {
17472
17669
  try {
17473
- if (!(0, import_node_fs17.existsSync)(move.from)) continue;
17670
+ if (!(0, import_node_fs18.existsSync)(move.from)) continue;
17474
17671
  const target = uniqueQuarantineTarget(move.to);
17475
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(target), { recursive: true });
17476
- (0, import_node_fs17.renameSync)(move.from, target);
17672
+ (0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
17673
+ (0, import_node_fs18.renameSync)(move.from, target);
17477
17674
  moved += 1;
17478
17675
  } catch {
17479
17676
  failed.push(move);
@@ -17492,23 +17689,23 @@ async function robocopyMirrorEmpty(emptyDir, target) {
17492
17689
  }
17493
17690
  async function clearNestedPluginTreeDir(targetPath) {
17494
17691
  try {
17495
- if (!(0, import_node_fs17.existsSync)(targetPath)) return true;
17692
+ if (!(0, import_node_fs18.existsSync)(targetPath)) return true;
17496
17693
  if (isWin) {
17497
- const emptyDir = (0, import_node_path17.join)((0, import_node_os6.tmpdir)(), `mmi-empty-${Date.now()}`);
17498
- (0, import_node_fs17.mkdirSync)(emptyDir, { recursive: true });
17694
+ const emptyDir = (0, import_node_path18.join)((0, import_node_os7.tmpdir)(), `mmi-empty-${Date.now()}`);
17695
+ (0, import_node_fs18.mkdirSync)(emptyDir, { recursive: true });
17499
17696
  try {
17500
17697
  await robocopyMirrorEmpty(emptyDir, targetPath);
17501
- (0, import_node_fs17.rmSync)(targetPath, { recursive: true, force: true });
17698
+ (0, import_node_fs18.rmSync)(targetPath, { recursive: true, force: true });
17502
17699
  } finally {
17503
17700
  try {
17504
- (0, import_node_fs17.rmSync)(emptyDir, { recursive: true, force: true });
17701
+ (0, import_node_fs18.rmSync)(emptyDir, { recursive: true, force: true });
17505
17702
  } catch {
17506
17703
  }
17507
17704
  }
17508
- return !(0, import_node_fs17.existsSync)(targetPath);
17705
+ return !(0, import_node_fs18.existsSync)(targetPath);
17509
17706
  }
17510
- (0, import_node_fs17.rmSync)(targetPath, { recursive: true, force: true });
17511
- return !(0, import_node_fs17.existsSync)(targetPath);
17707
+ (0, import_node_fs18.rmSync)(targetPath, { recursive: true, force: true });
17708
+ return !(0, import_node_fs18.existsSync)(targetPath);
17512
17709
  } catch {
17513
17710
  return false;
17514
17711
  }
@@ -17521,79 +17718,79 @@ async function applyNestedPluginTreeCleanup(paths, log) {
17521
17718
  }
17522
17719
  return true;
17523
17720
  }
17524
- var gitignorePath = () => (0, import_node_path17.join)(process.cwd(), ".gitignore");
17721
+ var gitignorePath = () => (0, import_node_path18.join)(process.cwd(), ".gitignore");
17525
17722
  function readTextFile(path2) {
17526
17723
  try {
17527
- if (!(0, import_node_fs17.existsSync)(path2)) return null;
17528
- return (0, import_node_fs17.readFileSync)(path2, "utf8");
17724
+ if (!(0, import_node_fs18.existsSync)(path2)) return null;
17725
+ return (0, import_node_fs18.readFileSync)(path2, "utf8");
17529
17726
  } catch {
17530
17727
  return null;
17531
17728
  }
17532
17729
  }
17533
17730
  function mcpDirExists(path2) {
17534
17731
  try {
17535
- return (0, import_node_fs17.existsSync)(path2);
17732
+ return (0, import_node_fs18.existsSync)(path2);
17536
17733
  } catch {
17537
17734
  return false;
17538
17735
  }
17539
17736
  }
17540
17737
  function mcpConfigTargets() {
17541
17738
  const cwd = process.cwd();
17542
- const home = (0, import_node_os6.homedir)();
17543
- const cursorProjectDir = (0, import_node_path17.join)(cwd, ".cursor");
17544
- const cursorUserDir = (0, import_node_path17.join)(home, ".cursor");
17545
- const codexDir = (0, import_node_path17.join)(home, ".codex");
17739
+ const home = (0, import_node_os7.homedir)();
17740
+ const cursorProjectDir = (0, import_node_path18.join)(cwd, ".cursor");
17741
+ const cursorUserDir = (0, import_node_path18.join)(home, ".cursor");
17742
+ const codexDir = (0, import_node_path18.join)(home, ".codex");
17546
17743
  return [
17547
17744
  // Claude Code project MCP — reconciled if present, never conjured (org seeds .cursor/mcp.json, not this).
17548
17745
  {
17549
17746
  host: "claude-code",
17550
17747
  label: "Claude Code project MCP",
17551
- path: (0, import_node_path17.join)(cwd, ".mcp.json"),
17748
+ path: (0, import_node_path18.join)(cwd, ".mcp.json"),
17552
17749
  format: "json",
17553
17750
  present: true,
17554
17751
  // parent is the repo root (cwd); the caller gates the whole reconcile on isOrgRepo
17555
17752
  isRepoFile: true,
17556
17753
  createWhenFileAbsent: false,
17557
- content: readTextFile((0, import_node_path17.join)(cwd, ".mcp.json"))
17754
+ content: readTextFile((0, import_node_path18.join)(cwd, ".mcp.json"))
17558
17755
  },
17559
17756
  // Cursor project MCP — the bootstrap-seeded surface; restored if its .cursor dir exists but the file is gone.
17560
17757
  {
17561
17758
  host: "cursor-project",
17562
17759
  label: "Cursor project MCP",
17563
- path: (0, import_node_path17.join)(cursorProjectDir, "mcp.json"),
17760
+ path: (0, import_node_path18.join)(cursorProjectDir, "mcp.json"),
17564
17761
  format: "json",
17565
17762
  present: mcpDirExists(cursorProjectDir),
17566
17763
  isRepoFile: true,
17567
17764
  createWhenFileAbsent: true,
17568
- content: readTextFile((0, import_node_path17.join)(cursorProjectDir, "mcp.json"))
17765
+ content: readTextFile((0, import_node_path18.join)(cursorProjectDir, "mcp.json"))
17569
17766
  },
17570
17767
  // Cursor user MCP — global; written only when Cursor is installed (~/.cursor exists).
17571
17768
  {
17572
17769
  host: "cursor-user",
17573
17770
  label: "Cursor user MCP",
17574
- path: (0, import_node_path17.join)(cursorUserDir, "mcp.json"),
17771
+ path: (0, import_node_path18.join)(cursorUserDir, "mcp.json"),
17575
17772
  format: "json",
17576
17773
  present: mcpDirExists(cursorUserDir),
17577
17774
  isRepoFile: false,
17578
17775
  createWhenFileAbsent: true,
17579
- content: readTextFile((0, import_node_path17.join)(cursorUserDir, "mcp.json"))
17776
+ content: readTextFile((0, import_node_path18.join)(cursorUserDir, "mcp.json"))
17580
17777
  },
17581
17778
  // Codex user config (TOML) — global; written only when Codex is installed (~/.codex exists).
17582
17779
  {
17583
17780
  host: "codex",
17584
17781
  label: "Codex user config",
17585
- path: (0, import_node_path17.join)(codexDir, "config.toml"),
17782
+ path: (0, import_node_path18.join)(codexDir, "config.toml"),
17586
17783
  format: "toml",
17587
17784
  present: mcpDirExists(codexDir),
17588
17785
  isRepoFile: false,
17589
17786
  createWhenFileAbsent: true,
17590
- content: readTextFile((0, import_node_path17.join)(codexDir, "config.toml"))
17787
+ content: readTextFile((0, import_node_path18.join)(codexDir, "config.toml"))
17591
17788
  }
17592
17789
  ];
17593
17790
  }
17594
17791
  function writeMcpConfigFile(path2, content) {
17595
17792
  try {
17596
- (0, import_node_fs17.writeFileSync)(path2, content, "utf8");
17793
+ (0, import_node_fs18.writeFileSync)(path2, content, "utf8");
17597
17794
  return true;
17598
17795
  } catch {
17599
17796
  return false;
@@ -17673,7 +17870,7 @@ function strayBrowserArtifactPaths() {
17673
17870
  const cwd = process.cwd();
17674
17871
  return STRAY_BROWSER_ARTIFACT_DIRS.filter((rel) => {
17675
17872
  try {
17676
- return (0, import_node_fs17.existsSync)((0, import_node_path17.join)(cwd, rel));
17873
+ return (0, import_node_fs18.existsSync)((0, import_node_path18.join)(cwd, rel));
17677
17874
  } catch {
17678
17875
  return false;
17679
17876
  }
@@ -17681,14 +17878,14 @@ function strayBrowserArtifactPaths() {
17681
17878
  }
17682
17879
  function readGitignore() {
17683
17880
  try {
17684
- return (0, import_node_fs17.readFileSync)(gitignorePath(), "utf8");
17881
+ return (0, import_node_fs18.readFileSync)(gitignorePath(), "utf8");
17685
17882
  } catch {
17686
17883
  return null;
17687
17884
  }
17688
17885
  }
17689
17886
  function writeGitignore(content) {
17690
17887
  try {
17691
- (0, import_node_fs17.writeFileSync)(gitignorePath(), content, "utf8");
17888
+ (0, import_node_fs18.writeFileSync)(gitignorePath(), content, "utf8");
17692
17889
  return true;
17693
17890
  } catch {
17694
17891
  return false;
@@ -17730,7 +17927,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
17730
17927
  const semverPrefix = /^\d+\.\d+\.\d+/;
17731
17928
  const isBehind = (installed2, released) => Boolean(installed2 && released && semverPrefix.test(installed2) && semverPrefix.test(released) && compareVersions(installed2, released) < 0);
17732
17929
  const opencodeAdapterStale = isBehind(opencodeInstalledVersionForDoctor(), releasedVersion);
17733
- const cursorCacheStale = (0, import_node_fs17.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
17930
+ const cursorCacheStale = (0, import_node_fs18.existsSync)(cursorPluginCacheRoot()) && (cursorPluginCachePinSnapshots() ?? []).some((p) => isBehind(p.version, releasedVersion));
17734
17931
  const healPlan = doctorHealPlan({
17735
17932
  isOrgRepo,
17736
17933
  surface,
@@ -17765,7 +17962,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
17765
17962
  let onPath = pathProbe;
17766
17963
  if (!onPath) {
17767
17964
  const root = process.env.CLAUDE_PLUGIN_ROOT;
17768
- if (root && (0, import_node_fs17.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
17965
+ if (root && (0, import_node_fs18.existsSync)(`${root}/bin/mmi-cli${isWin ? ".cmd" : ""}`)) onPath = true;
17769
17966
  }
17770
17967
  checks.push({ ok: onPath, label: "mmi-cli on PATH", fix: "auto-provisioned at session start \u2014 reopen the session, or install the MMI plugin" });
17771
17968
  const reloadHint = reloadAction(surface);
@@ -17912,7 +18109,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
17912
18109
  checks.push(gitignoreCheck);
17913
18110
  checks.push(buildRepoLocalWorktreeCheck({
17914
18111
  isOrgRepo,
17915
- hasRepoLocalWorktrees: (0, import_node_fs17.existsSync)((0, import_node_path17.join)(process.cwd(), ".worktrees"))
18112
+ hasRepoLocalWorktrees: (0, import_node_fs18.existsSync)((0, import_node_path18.join)(process.cwd(), ".worktrees"))
17916
18113
  }));
17917
18114
  let driftCheck = buildPluginConfigDriftCheck({ isOrgRepo, installed, surface });
17918
18115
  if (!driftCheck.ok && driftCheck.recordsToWrite && repairLocal) {
@@ -18202,7 +18399,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
18202
18399
  }
18203
18400
  checks.push(nestedPluginTreeCheck);
18204
18401
  const cursorCacheRoot = cursorPluginCacheRoot();
18205
- const cursorCacheRootExists = (0, import_node_fs17.existsSync)(cursorCacheRoot);
18402
+ const cursorCacheRootExists = (0, import_node_fs18.existsSync)(cursorCacheRoot);
18206
18403
  let cursorPins = cursorPluginCachePinSnapshots() ?? [];
18207
18404
  checks.push(
18208
18405
  buildCursorPluginCacheCleanupCheck({
@@ -18230,7 +18427,7 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
18230
18427
  cacheRootExists: cursorCacheRootExists,
18231
18428
  hubCheckout: hubCheckoutForCursorSeed(),
18232
18429
  execFileP: execFileP2,
18233
- mkdtemp: (prefix) => (0, import_promises2.mkdtemp)((0, import_node_path17.join)((0, import_node_os6.tmpdir)(), prefix)),
18430
+ mkdtemp: (prefix) => (0, import_promises2.mkdtemp)((0, import_node_path18.join)((0, import_node_os7.tmpdir)(), prefix)),
18234
18431
  log: (m) => io.err(m)
18235
18432
  });
18236
18433
  if (seeded) {
@@ -18263,6 +18460,11 @@ async function runDoctor(opts, io = consoleIo, readOrigin) {
18263
18460
  }
18264
18461
  }
18265
18462
  checks.push(cursorPluginCheck);
18463
+ if (!opts.banner) {
18464
+ const cursorLogText = readCursorPluginLogText();
18465
+ const enabledPinSignal = cursorLogText ? parseCursorPluginLog(cursorLogText) : {};
18466
+ checks.push(buildCursorEnabledPinCheck({ isOrgRepo, pins: cursorPins, ...enabledPinSignal }));
18467
+ }
18266
18468
  const cursorThirdPartyEnabled = await readCursorThirdPartyExtensibilityEnabled(execFileP2);
18267
18469
  checks.push(
18268
18470
  buildCursorThirdPartyExtensibilityCheck({
@@ -18420,29 +18622,33 @@ function mergeGuardHook(settings) {
18420
18622
  next.hooks = hooks;
18421
18623
  return next;
18422
18624
  }
18423
- var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path17.join)((0, import_node_os6.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
18625
+ var userScopeSettingsPath = (surface = detectSurface(process.env)) => (0, import_node_path18.join)((0, import_node_os7.homedir)(), surface === "codex" ? ".codex" : ".claude", "settings.json");
18424
18626
  function ensureUserScopeGuardHook(opts = {}) {
18425
18627
  const path2 = opts.settingsPath ?? userScopeSettingsPath();
18426
18628
  try {
18427
18629
  let current = null;
18428
- if ((0, import_node_fs17.existsSync)(path2)) {
18630
+ if ((0, import_node_fs18.existsSync)(path2)) {
18429
18631
  try {
18430
- current = JSON.parse((0, import_node_fs17.readFileSync)(path2, "utf8"));
18632
+ current = JSON.parse((0, import_node_fs18.readFileSync)(path2, "utf8"));
18431
18633
  } catch {
18432
18634
  return "failed";
18433
18635
  }
18434
18636
  }
18435
18637
  if (settingsHasGuardHook(current)) return "already";
18436
18638
  const merged = mergeGuardHook(current);
18437
- (0, import_node_fs17.mkdirSync)((0, import_node_path17.dirname)(path2), { recursive: true });
18438
- if ((0, import_node_fs17.existsSync)(path2)) (0, import_node_fs17.copyFileSync)(path2, `${path2}.bak`);
18439
- (0, import_node_fs17.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
18639
+ (0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(path2), { recursive: true });
18640
+ if ((0, import_node_fs18.existsSync)(path2)) (0, import_node_fs18.copyFileSync)(path2, `${path2}.bak`);
18641
+ (0, import_node_fs18.writeFileSync)(path2, `${JSON.stringify(merged, null, 2)}
18440
18642
  `, "utf8");
18441
18643
  return "written";
18442
18644
  } catch {
18443
18645
  return "failed";
18444
18646
  }
18445
18647
  }
18648
+ function guardRecoveryCommand(surface) {
18649
+ const token = surfaceToken(surface);
18650
+ return token ? PLUGIN_SURFACE_HEAL[token]?.guardRecovery : void 0;
18651
+ }
18446
18652
  async function runGuard(opts = {}, readOrigin) {
18447
18653
  void opts;
18448
18654
  try {
@@ -18450,7 +18656,7 @@ async function runGuard(opts = {}, readOrigin) {
18450
18656
  const isOrgRepo = readOrigin ? await isOrgRepoRoot(readOrigin) : await isOrgRepoRoot();
18451
18657
  const input = snapshotPluginGuardInput(surface, isOrgRepo);
18452
18658
  const { state } = buildPluginGuardDecision(input);
18453
- const { line, exitCode } = buildGuardSessionStartLine(state);
18659
+ const { line, exitCode } = buildGuardSessionStartLine(state, { recovery: guardRecoveryCommand(surface) });
18454
18660
  if (line) console.error(line);
18455
18661
  process.exitCode = exitCode;
18456
18662
  } catch {
@@ -18570,7 +18776,7 @@ async function applyGcPlan(plan, remote) {
18570
18776
  cleanupBranch: (branch, expectedHeadOid) => cleanupPrMergeLocalBranch(branch.branch, {
18571
18777
  beforeWorktrees,
18572
18778
  startingPath: branch.worktreePath,
18573
- pathExists: (p) => (0, import_node_fs18.existsSync)(p),
18779
+ pathExists: (p) => (0, import_node_fs19.existsSync)(p),
18574
18780
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
18575
18781
  teardownWorktreeStage,
18576
18782
  deferredStore,
@@ -18593,7 +18799,7 @@ async function applyGcPlan(plan, remote) {
18593
18799
  for (const wt of plan.worktreeDirs) {
18594
18800
  try {
18595
18801
  const cleanupTarget = resolveSafeSiblingWorktreeCleanupTarget(wt.path, siblingRoot, {
18596
- realpath: (path2) => (0, import_node_fs18.realpathSync)(path2)
18802
+ realpath: (path2) => (0, import_node_fs19.realpathSync)(path2)
18597
18803
  });
18598
18804
  if (!cleanupTarget.ok) {
18599
18805
  result.failed.push(`${wt.path}: ${cleanupTarget.reason}`);
@@ -18636,13 +18842,13 @@ var program2 = new Command();
18636
18842
  program2.name("mmi-cli").description("MMI Future CLI \u2014 org rules delivery, Jervaise-only continuity. The engine the plugin SessionStart hook drives.").version(resolveClientVersion()).showHelpAfterError("(run `mmi-cli commands` to list every subcommand + its flags, or `mmi-cli commands --json` to ground against it)");
18637
18843
  var rules = program2.command("rules").description("org-managed .gitignore delivery");
18638
18844
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
18639
- const path2 = (0, import_node_path18.join)(process.cwd(), ".gitignore");
18640
- const current = (0, import_node_fs18.existsSync)(path2) ? (0, import_node_fs18.readFileSync)(path2, "utf8") : null;
18845
+ const path2 = (0, import_node_path19.join)(process.cwd(), ".gitignore");
18846
+ const current = (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null;
18641
18847
  const plan = planManagedGitignore(current);
18642
18848
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
18643
18849
  if (opts.write) {
18644
18850
  if (plan.changed) {
18645
- (0, import_node_fs18.writeFileSync)(path2, plan.content, "utf8");
18851
+ (0, import_node_fs19.writeFileSync)(path2, plan.content, "utf8");
18646
18852
  console.log(`mmi-cli rules gitignore: updated .gitignore (${drift})`);
18647
18853
  } else {
18648
18854
  console.log("mmi-cli rules gitignore: up to date");
@@ -18784,8 +18990,8 @@ function runWorktreeInstall(command, cwd, quiet) {
18784
18990
  const [bin, ...args] = command.split(" ");
18785
18991
  const file = isWin2 ? "cmd.exe" : bin;
18786
18992
  const spawnArgs = isWin2 ? ["/c", bin, ...args] : args;
18787
- return new Promise((resolve6, reject) => {
18788
- const child = (0, import_node_child_process11.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
18993
+ return new Promise((resolve5, reject) => {
18994
+ const child = (0, import_node_child_process12.spawn)(file, spawnArgs, { cwd, stdio: quiet ? "ignore" : "inherit", windowsHide: true });
18789
18995
  const timer = setTimeout(() => {
18790
18996
  try {
18791
18997
  child.kill();
@@ -18799,7 +19005,7 @@ function runWorktreeInstall(command, cwd, quiet) {
18799
19005
  });
18800
19006
  child.on("exit", (code) => {
18801
19007
  clearTimeout(timer);
18802
- if (code === 0) resolve6();
19008
+ if (code === 0) resolve5();
18803
19009
  else reject(new Error(`${command} exited ${code} in ${cwd}`));
18804
19010
  });
18805
19011
  });
@@ -18807,7 +19013,7 @@ function runWorktreeInstall(command, cwd, quiet) {
18807
19013
  async function primaryCheckoutRoot(worktreeRoot) {
18808
19014
  try {
18809
19015
  const out = (await execFileP2("git", ["-C", worktreeRoot, "rev-parse", "--path-format=absolute", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
18810
- return out ? (0, import_node_path18.dirname)(out) : void 0;
19016
+ return out ? (0, import_node_path19.dirname)(out) : void 0;
18811
19017
  } catch {
18812
19018
  return void 0;
18813
19019
  }
@@ -18822,26 +19028,26 @@ function makeProvisionDeps(worktreeRoot, quiet, log) {
18822
19028
  function acquireWorktreeSetupLock(worktreeRoot) {
18823
19029
  const lockPath = repoRuntimeStatePath(worktreeRoot, "worktree-setup.lock");
18824
19030
  const take = () => {
18825
- const fd = (0, import_node_fs18.openSync)(lockPath, "wx");
19031
+ const fd = (0, import_node_fs19.openSync)(lockPath, "wx");
18826
19032
  try {
18827
- (0, import_node_fs18.writeSync)(fd, String(Date.now()));
19033
+ (0, import_node_fs19.writeSync)(fd, String(Date.now()));
18828
19034
  } finally {
18829
- (0, import_node_fs18.closeSync)(fd);
19035
+ (0, import_node_fs19.closeSync)(fd);
18830
19036
  }
18831
19037
  return () => {
18832
19038
  try {
18833
- (0, import_node_fs18.rmSync)(lockPath, { force: true });
19039
+ (0, import_node_fs19.rmSync)(lockPath, { force: true });
18834
19040
  } catch {
18835
19041
  }
18836
19042
  };
18837
19043
  };
18838
19044
  try {
18839
- (0, import_node_fs18.mkdirSync)((0, import_node_path18.dirname)(lockPath), { recursive: true });
19045
+ (0, import_node_fs19.mkdirSync)((0, import_node_path19.dirname)(lockPath), { recursive: true });
18840
19046
  return take();
18841
19047
  } catch {
18842
19048
  try {
18843
- if (Date.now() - (0, import_node_fs18.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
18844
- (0, import_node_fs18.rmSync)(lockPath, { force: true });
19049
+ if (Date.now() - (0, import_node_fs19.statSync)(lockPath).mtimeMs > WORKTREE_SETUP_LOCK_TTL_MS) {
19050
+ (0, import_node_fs19.rmSync)(lockPath, { force: true });
18845
19051
  return take();
18846
19052
  }
18847
19053
  } catch {
@@ -18980,7 +19186,7 @@ function scheduleRelatedDiscovery(o) {
18980
19186
  try {
18981
19187
  const args = ["issue", "discover-related", "--number", String(o.number), "--title", o.title, "--body", o.body];
18982
19188
  if (o.repo) args.push("--repo", o.repo);
18983
- (0, import_node_child_process11.spawn)(process.execPath, [process.argv[1], ...args], {
19189
+ (0, import_node_child_process12.spawn)(process.execPath, [process.argv[1], ...args], {
18984
19190
  detached: true,
18985
19191
  stdio: "ignore",
18986
19192
  windowsHide: true,
@@ -19073,8 +19279,8 @@ tenant.command("sweep-rc").description("discover (and optionally retire) running
19073
19279
  async function resolveDnsBounded(host, timeoutMs = 3e3) {
19074
19280
  const { lookup } = await import("node:dns/promises");
19075
19281
  const probe = lookup(host).then(() => true).catch((e) => dnsErrorToResolution(e?.code));
19076
- const timeout = new Promise((resolve6) => {
19077
- setTimeout(() => resolve6(void 0), timeoutMs).unref?.();
19282
+ const timeout = new Promise((resolve5) => {
19283
+ setTimeout(() => resolve5(void 0), timeoutMs).unref?.();
19078
19284
  });
19079
19285
  return Promise.race([probe, timeout]);
19080
19286
  }
@@ -19336,7 +19542,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
19336
19542
  const vars = rawValues("--var");
19337
19543
  if (o.secretsFile) {
19338
19544
  try {
19339
- vars.push(`secrets=${(0, import_node_fs18.readFileSync)(o.secretsFile, "utf8")}`);
19545
+ vars.push(`secrets=${(0, import_node_fs19.readFileSync)(o.secretsFile, "utf8")}`);
19340
19546
  } catch (e) {
19341
19547
  return fail(`project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
19342
19548
  }
@@ -19624,18 +19830,20 @@ issue.command("create").description("create an issue (type \u2192 label) and pri
19624
19830
  ...parentLinkFields(parent, parentLinkError)
19625
19831
  }));
19626
19832
  });
19627
- issue.command("view <number>").description("read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json <fields>", "comma-separated gh --json field list (overrides the default field set)").action(async (number, o) => {
19833
+ issue.command("view <number>").description("read an issue as structured JSON \u2014 the mmi-cli path for non-board issue reads (#2347)").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"').action(async (number, o) => {
19628
19834
  const n = Number(number);
19629
19835
  if (!Number.isInteger(n) || n <= 0) return fail("issue view: <number> must be a positive integer");
19630
19836
  const repo = await resolveRepo(o.repo);
19631
19837
  if (!repo) return fail("issue view: could not resolve repo (pass --repo <owner/repo>)");
19632
- const fields = o.json && o.json.trim() ? o.json.trim() : "number,title,state,url,labels,author,assignees,milestone,body";
19838
+ const fields = normalizeIssueViewJsonFields(o.json);
19633
19839
  try {
19634
19840
  const data = await ghJson(["issue", "view", String(n), "--repo", repo, "--json", fields]);
19635
19841
  console.log(JSON.stringify(data));
19636
19842
  } catch (e) {
19637
19843
  const err = e;
19638
- return fail(`issue view: ${(err.stderr || err.message || String(e)).trim()}`);
19844
+ const raw = (err.stderr || err.message || String(e)).trim();
19845
+ const argvNote = o.json ? ` (received --json argv: ${JSON.stringify(o.json)}, normalized to "${fields}")` : "";
19846
+ return fail(`issue view: ${raw}${argvNote}`);
19639
19847
  }
19640
19848
  });
19641
19849
  issue.command("discover-related").description("find related issues for an existing issue and post only high-confidence links").requiredOption("--number <number>", "created issue number").requiredOption("--title <title>", "created issue title").requiredOption("--body <body>", "created issue body").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json", "print candidates instead of posting").action(async (o) => {
@@ -19904,9 +20112,9 @@ pr.command("view <number>").description("read a PR as structured JSON (merged st
19904
20112
  }
19905
20113
  });
19906
20114
  async function listCiWorkflowPaths(cwd = process.cwd()) {
19907
- const wfDir = (0, import_node_path18.join)(cwd, ".github", "workflows");
19908
- if (!(0, import_node_fs18.existsSync)(wfDir)) return [];
19909
- return (0, import_node_fs18.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
20115
+ const wfDir = (0, import_node_path19.join)(cwd, ".github", "workflows");
20116
+ if (!(0, import_node_fs19.existsSync)(wfDir)) return [];
20117
+ return (0, import_node_fs19.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).map((name) => `.github/workflows/${name}`);
19910
20118
  }
19911
20119
  async function resolveMergeCiPolicyForCheckout(repoOpt) {
19912
20120
  const repo = repoOpt ?? await resolveRepo();
@@ -19925,7 +20133,7 @@ function ciAuditDeps() {
19925
20133
  // Continuous CI delivery (#1550): the gate re-seed renders from the Hub's on-disk seed templates. The
19926
20134
  // reconcile runs IN the Hub checkout, so this is local-file I/O (no network fetch). Path is relative to
19927
20135
  // the repo root (e.g. skills/bootstrap/seeds/gate.template.yml).
19928
- readSeedFile: (path2) => (0, import_node_fs18.existsSync)(path2) ? (0, import_node_fs18.readFileSync)(path2, "utf8") : null
20136
+ readSeedFile: (path2) => (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null
19929
20137
  };
19930
20138
  }
19931
20139
  pr.command("ci-policy").description("report merge CI policy: wait-for-checks vs no-ci (for grind/build agents)").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").action(async (o) => {
@@ -19955,7 +20163,7 @@ pr.command("checks-wait <number>").description("bounded wait for PR checks; skip
19955
20163
  const result = await waitForPrChecks({
19956
20164
  resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo),
19957
20165
  pollChecks: () => pollGhPrChecks(number, repoArgs),
19958
- sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms))
20166
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
19959
20167
  });
19960
20168
  if (o.json) printLine(JSON.stringify(result));
19961
20169
  else printLine(`pr checks-wait: ${result.status}${result.reason ? ` \u2014 ${result.reason}` : ""}${result.detail ? ` (${result.detail})` : ""}`);
@@ -19982,7 +20190,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
19982
20190
  waitForChecks: (prNumber, repo) => waitForPrChecks({
19983
20191
  resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
19984
20192
  pollChecks: () => pollGhPrChecks(prNumber, repo ? ["--repo", repo] : []),
19985
- sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms))
20193
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
19986
20194
  }),
19987
20195
  // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying — a
19988
20196
  // fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
@@ -20033,7 +20241,7 @@ pr.command("land <number>").description("agent merge path (#1440): train probe \
20033
20241
  while (Date.now() < deadlineMs) {
20034
20242
  const stateRead = await readGhPrStateWithRetry(async () => (await execFileP2("gh", ["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], { timeout: GC_GH_TIMEOUT_MS2 })).stdout, { retries: 2, delayMs: 1e3 });
20035
20243
  if (stateRead.ok && stateRead.state === "MERGED") return true;
20036
- await new Promise((resolve6) => setTimeout(resolve6, PR_LAND_POLL_MS));
20244
+ await new Promise((resolve5) => setTimeout(resolve5, PR_LAND_POLL_MS));
20037
20245
  }
20038
20246
  return false;
20039
20247
  }
@@ -20077,7 +20285,7 @@ async function remoteBranchExists2(branch, options = {}) {
20077
20285
  }
20078
20286
  var COMPOSE_TIMEOUT_MS = 12e4;
20079
20287
  function spawnDeferredGcSweep() {
20080
- spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
20288
+ spawnDetachedSelf(["gc", "sweep-deferred", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
20081
20289
  }
20082
20290
  async function createDeferredWorktreeStore() {
20083
20291
  try {
@@ -20093,7 +20301,7 @@ async function createDeferredWorktreeStore() {
20093
20301
  },
20094
20302
  write: async (entries) => {
20095
20303
  try {
20096
- await (0, import_promises3.mkdir)((0, import_node_path18.dirname)(registryPath), { recursive: true });
20304
+ await (0, import_promises3.mkdir)((0, import_node_path19.dirname)(registryPath), { recursive: true });
20097
20305
  await (0, import_promises3.writeFile)(registryPath, serializeDeferredWorktrees(entries), "utf8");
20098
20306
  } catch {
20099
20307
  }
@@ -20107,13 +20315,13 @@ var realWorktreeDirRemover = {
20107
20315
  probe: (p) => {
20108
20316
  let st;
20109
20317
  try {
20110
- st = (0, import_node_fs18.lstatSync)(p);
20318
+ st = (0, import_node_fs19.lstatSync)(p);
20111
20319
  } catch {
20112
20320
  return null;
20113
20321
  }
20114
20322
  if (st.isSymbolicLink()) return "link";
20115
20323
  try {
20116
- (0, import_node_fs18.readlinkSync)(p);
20324
+ (0, import_node_fs19.readlinkSync)(p);
20117
20325
  return "link";
20118
20326
  } catch {
20119
20327
  }
@@ -20121,7 +20329,7 @@ var realWorktreeDirRemover = {
20121
20329
  },
20122
20330
  readdir: (p) => {
20123
20331
  try {
20124
- return (0, import_node_fs18.readdirSync)(p);
20332
+ return (0, import_node_fs19.readdirSync)(p);
20125
20333
  } catch {
20126
20334
  return [];
20127
20335
  }
@@ -20130,9 +20338,9 @@ var realWorktreeDirRemover = {
20130
20338
  // leaving the target); a file symlink with unlink. rmdir first, fall back to unlink.
20131
20339
  detachLink: (p) => {
20132
20340
  try {
20133
- (0, import_node_fs18.rmdirSync)(p);
20341
+ (0, import_node_fs19.rmdirSync)(p);
20134
20342
  } catch {
20135
- (0, import_node_fs18.unlinkSync)(p);
20343
+ (0, import_node_fs19.unlinkSync)(p);
20136
20344
  }
20137
20345
  },
20138
20346
  removeTree: (p) => (0, import_promises3.rm)(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
@@ -20147,7 +20355,7 @@ async function resolvePrimaryCheckout(execGit) {
20147
20355
  function worktreeRemoveDeps(execGit) {
20148
20356
  return {
20149
20357
  git: execGit,
20150
- sleep: (ms) => new Promise((resolve6) => setTimeout(resolve6, ms)),
20358
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
20151
20359
  removeWorktreeDir: async (worktreePath) => removeWorktreeTree(worktreePath, await resolvePrimaryCheckout(execGit), realWorktreeDirRemover)
20152
20360
  };
20153
20361
  }
@@ -20162,9 +20370,9 @@ async function worktreeHasStageState(worktreePath) {
20162
20370
  }
20163
20371
  }
20164
20372
  function stageStateFileBelongsToWorktree(statePath, worktreePath) {
20165
- if (!(0, import_node_fs18.existsSync)(statePath)) return false;
20373
+ if (!(0, import_node_fs19.existsSync)(statePath)) return false;
20166
20374
  try {
20167
- const state = JSON.parse((0, import_node_fs18.readFileSync)(statePath, "utf8"));
20375
+ const state = JSON.parse((0, import_node_fs19.readFileSync)(statePath, "utf8"));
20168
20376
  const recordedCwd = typeof state.identity?.cwd === "string" ? state.identity.cwd : typeof state.cwd === "string" ? state.cwd : "";
20169
20377
  return Boolean(recordedCwd && isPathUnderDirectory(recordedCwd, worktreePath));
20170
20378
  } catch {
@@ -20230,7 +20438,7 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
20230
20438
  localCleanup = await cleanupPrMergeLocalBranch(headRef, {
20231
20439
  beforeWorktrees,
20232
20440
  startingPath,
20233
- pathExists: (p) => (0, import_node_fs18.existsSync)(p),
20441
+ pathExists: (p) => (0, import_node_fs19.existsSync)(p),
20234
20442
  execGit: async (args) => (await execFileP2("git", args, { timeout: GIT_TIMEOUT_MS })).stdout,
20235
20443
  teardownWorktreeStage,
20236
20444
  deferredStore,
@@ -20451,7 +20659,7 @@ function stageScopedRunOpts(o) {
20451
20659
  };
20452
20660
  }
20453
20661
  function printLine(value) {
20454
- (0, import_node_fs18.writeSync)(1, `${value}
20662
+ (0, import_node_fs19.writeSync)(1, `${value}
20455
20663
  `);
20456
20664
  }
20457
20665
  function stageKeepAlive() {
@@ -20465,8 +20673,8 @@ async function resolveStage() {
20465
20673
  const portRange = portRangeMeta && typeof portRangeMeta.start === "number" && typeof portRangeMeta.end === "number" ? [portRangeMeta.start, portRangeMeta.end] : void 0;
20466
20674
  return decideStage({
20467
20675
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
20468
- hasCompose: (0, import_node_fs18.existsSync)((0, import_node_path18.join)(process.cwd(), "docker-compose.yml")),
20469
- hasEnvExample: (0, import_node_fs18.existsSync)((0, import_node_path18.join)(process.cwd(), ".env.example"))
20676
+ hasCompose: (0, import_node_fs19.existsSync)((0, import_node_path19.join)(process.cwd(), "docker-compose.yml")),
20677
+ hasEnvExample: (0, import_node_fs19.existsSync)((0, import_node_path19.join)(process.cwd(), ".env.example"))
20470
20678
  });
20471
20679
  }
20472
20680
  async function fetchStageVaultEnvMerge() {
@@ -20900,21 +21108,14 @@ ${r.repo}: applied=[${r.applied.join("; ")}] skipped=[${r.skipped.join("; ")}]${
20900
21108
  if (!audit.ok) process.exitCode = 1;
20901
21109
  });
20902
21110
  var wiki = program2.command("wiki").description("release wiki lane \u2014 publish generated pages via a short-lived, repo-scoped minted token");
20903
- wiki.command("publish <pages-dir>").description("mint a 1h repo-scoped contents:write token (master-gated) and publish the generated .md pages to <repo>.wiki.git via scripts/wiki-publish.mjs; the token stays in memory (never printed/committed). Fails loud on the credential gap \u2014 never a silent skip").option("--repo <owner/repo>", "target repo (defaults to the cwd repo)").action(async (pagesDir, o) => {
21111
+ wiki.command("publish <pages-dir>").description("mint a 1h repo-scoped contents:write token (master-gated) and publish the generated .md pages to <repo>.wiki.git; the token stays in memory (never printed/committed). Runs from any org repo checkout \u2014 the publisher ships inside mmi-cli, no repo-local script required. Fails loud on the credential gap \u2014 never a silent skip").option("--repo <owner/repo>", "target repo (defaults to the cwd repo)").action(async (pagesDir, o) => {
20904
21112
  const repo = await resolveRepo(o.repo);
20905
21113
  if (!repo) return fail("wiki publish: could not determine the target repo (pass --repo owner/name)");
20906
21114
  const cfg = await loadConfig();
20907
21115
  const ok = await wikiPublish(
20908
21116
  {
20909
21117
  mint: (r) => mintWikiToken(r, registryClientDeps(cfg)),
20910
- // scripts/wiki-publish.mjs lives at the repo root; Lane B runs there. Resolve against cwd.
20911
- scriptPath: () => (0, import_node_path18.resolve)(process.cwd(), "scripts", "wiki-publish.mjs"),
20912
- scriptExists: (p) => (0, import_node_fs18.existsSync)(p),
20913
- spawn: (command, args, env) => {
20914
- const r = (0, import_node_child_process11.spawnSync)(command, args, { stdio: "inherit", env });
20915
- if (r.error) throw r.error;
20916
- return r.status ?? 1;
20917
- },
21118
+ publish: (o2) => publishWikiViaGit(realWikiGitPublishDeps(), o2),
20918
21119
  log: (msg) => console.log(msg),
20919
21120
  err: (msg) => console.error(msg)
20920
21121
  },
@@ -20941,7 +21142,7 @@ bootstrap.command("verify <repo>").description("audit whether an existing repo i
20941
21142
  client: defaultGitHubClient(),
20942
21143
  projectMeta: meta,
20943
21144
  deployModel: typeof meta?.deployModel === "string" ? meta.deployModel : void 0,
20944
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs18.existsSync)(path2) ? (0, import_node_fs18.readFileSync)(path2, "utf8") : null,
21145
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs19.existsSync)(path2) ? (0, import_node_fs19.readFileSync)(path2, "utf8") : null,
20945
21146
  // requiredGcpApis is stored as an array by a JSON write, but `project set --var KEY=VALUE` stores a raw
20946
21147
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
20947
21148
  requiredGcpApis: (() => {
@@ -20992,12 +21193,12 @@ bootstrap.command("apply <repo>").description("idempotent seed apply from skills
20992
21193
  return fail(`bootstrap apply: ${e.message}`);
20993
21194
  }
20994
21195
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
20995
- if (!(0, import_node_fs18.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
20996
- const manifest = loadBootstrapSeeds((0, import_node_fs18.readFileSync)(manifestPath, "utf8"));
21196
+ if (!(0, import_node_fs19.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; run from the MMI-Hub repo root`);
21197
+ const manifest = loadBootstrapSeeds((0, import_node_fs19.readFileSync)(manifestPath, "utf8"));
20997
21198
  const baseBranch = o.class === "content" ? "main" : "development";
20998
21199
  const slug = parsedRepo.slug;
20999
21200
  const gh = async (args) => execFileP2("gh", args, { timeout: 2e4 });
21000
- const readFile2 = (p) => (0, import_node_fs18.existsSync)(p) ? (0, import_node_fs18.readFileSync)(p, "utf8") : null;
21201
+ const readFile2 = (p) => (0, import_node_fs19.existsSync)(p) ? (0, import_node_fs19.readFileSync)(p, "utf8") : null;
21001
21202
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
21002
21203
  const rawVars = {};
21003
21204
  for (const value of rawValues("--var")) {
@@ -21294,16 +21495,16 @@ access.command("audit").description("audit collaborator roles + train-branch pus
21294
21495
  const repoClass = o.class;
21295
21496
  targets = [{ repo: o.repo, class: repoClass, releaseTrack: repoClass === "content" ? "trunk" : resolveReleaseTrack(meta, void 0, o.repo) }];
21296
21497
  } else {
21297
- const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs18.existsSync)("projects.json") ? (0, import_node_fs18.readFileSync)("projects.json", "utf8") : null;
21498
+ const projectsJson = registryProjects ? JSON.stringify({ projects: registryProjects }) : (0, import_node_fs19.existsSync)("projects.json") ? (0, import_node_fs19.readFileSync)("projects.json", "utf8") : null;
21298
21499
  if (!projectsJson) return failGraceful("access audit: no project registry \u2014 Hub API unreachable and projects.json not found; run from the MMI-Hub repo root or pass --repo <owner/repo>");
21299
- const fanoutJson = (0, import_node_fs18.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs18.readFileSync)(".github/fanout-targets.json", "utf8") : null;
21500
+ const fanoutJson = (0, import_node_fs19.existsSync)(".github/fanout-targets.json") ? (0, import_node_fs19.readFileSync)(".github/fanout-targets.json", "utf8") : null;
21300
21501
  targets = loadAccessTargets(projectsJson, fanoutJson);
21301
21502
  }
21302
21503
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
21303
- const fileMatrix = (0, import_node_fs18.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs18.readFileSync)("access-matrix.json", "utf8")) : {};
21504
+ const fileMatrix = (0, import_node_fs19.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs19.readFileSync)("access-matrix.json", "utf8")) : {};
21304
21505
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
21305
21506
  const derivedContracts = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
21306
- const fileContracts = (0, import_node_fs18.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs18.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
21507
+ const fileContracts = (0, import_node_fs19.existsSync)("data-access-contracts.json") ? loadDataAccessContracts((0, import_node_fs19.readFileSync)("data-access-contracts.json", "utf8")) : { consumers: {} };
21307
21508
  const dataAccess = mergeDataAccessContracts(fileContracts, derivedContracts);
21308
21509
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess);
21309
21510
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
@@ -21340,7 +21541,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
21340
21541
  readBoard,
21341
21542
  // #1813: warm the slice cache out-of-band (detached) so the ~20s live read
21342
21543
  // never costs banner time and next session's glance renders instantly within budget.
21343
- scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
21544
+ scheduleRefresh: () => spawnDetachedSelf(["board", "slice-refresh", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] })
21344
21545
  }),
21345
21546
  doctor: (io) => runDoctor({ banner: true }, io)
21346
21547
  });
@@ -21348,7 +21549,7 @@ program2.command("session-start").description("run the SessionStart verbs (whoam
21348
21549
  for (const line of scratchGcLines(process.cwd())) consoleIo.log(line);
21349
21550
  const worktreeBanner = worktreeAutoProvisionBanner(process.cwd());
21350
21551
  if (worktreeBanner) {
21351
- spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process11.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
21552
+ spawnDetachedSelf(["worktree", "setup", "--quiet"], { spawn: import_node_child_process12.spawn, execPath: process.execPath, scriptPath: process.argv[1] });
21352
21553
  consoleIo.log(worktreeBanner);
21353
21554
  }
21354
21555
  });