@mutmutco/cli 3.124.0 → 3.126.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 +53 -44
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -9729,41 +9729,48 @@ function pluginReadGrantNote(login = "<your-github-login>") {
9729
9729
  If the reinstall 404s on ${PLUGIN_READ_REPO}, you lack read on it. An org owner must grant it (idempotent):
9730
9730
  gh api -X PUT repos/${PLUGIN_READ_REPO}/collaborators/${login} -f permission=pull`;
9731
9731
  }
9732
- async function applyPluginHeal(surface, log, opts) {
9733
- const token = surfaceToken(surface);
9734
- if (token !== "claude" && token !== "codex" && token !== "kilo") return false;
9735
- const descriptor = PLUGIN_SURFACE_HEAL[token];
9736
- if (!descriptor || !descriptor.healSteps) return false;
9737
- if (token === "kilo") {
9738
- log(" \u21BB reinstalling the MMI plugin via `kilo plugin` (install \u2192 server() provisions the skills)\u2026");
9739
- for (const step of descriptor.healSteps) {
9740
- const ok = await runPluginCli("kilo", [...step.args], log);
9741
- if (healStepAborts(step, ok)) return false;
9742
- }
9743
- return true;
9744
- }
9745
- const tableSteps = descriptor.healSteps;
9746
- if (!tableSteps) return false;
9747
- const loadedCodexLauncher = token === "codex" ? captureCodexHookLauncher() : void 0;
9748
- const bin = token;
9749
- const refSupported = await marketplaceAddRefSupported(bin);
9732
+ async function runHealSteps(host, tableSteps, deps) {
9733
+ const log = deps.log ?? (() => {
9734
+ });
9735
+ const loadedCodexLauncher = host === "codex" ? captureCodexHookLauncher() : void 0;
9736
+ const needsRefProbe = tableSteps.some((step) => step.args.includes("--ref"));
9737
+ const refSupported = needsRefProbe ? await marketplaceAddRefSupported(host) : true;
9750
9738
  const { steps } = adaptHealStepsForRefSupport(tableSteps, refSupported);
9751
- log(healBannerLine(bin, token, refSupported));
9739
+ if (deps.banner) log(deps.banner(refSupported));
9752
9740
  const pinsPath = (0, import_node_path12.join)((0, import_node_os5.homedir)(), ...KNOWN_MARKETPLACES_RELATIVE);
9753
- const pins = token === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
9741
+ const pins = host === "claude" ? captureMarketplacePins(readKnownMarketplacesFile(pinsPath), [MMI_MARKETPLACE_NAME, JERV_MARKETPLACE_NAME]) : /* @__PURE__ */ new Map();
9742
+ let failure;
9754
9743
  try {
9755
9744
  for (const step of steps) {
9756
- const ok = await runPluginCli(token, [...step.args], log);
9757
- if (healStepAborts(step, ok)) return false;
9745
+ const res = await deps.run(host, [...step.args]);
9746
+ if (healStepAborts(step, res.ok)) {
9747
+ failure = res.detail ?? `\`${host} ${step.args.join(" ")}\` failed`;
9748
+ break;
9749
+ }
9758
9750
  }
9759
9751
  } finally {
9760
9752
  if (restoreCodexHookLauncher(loadedCodexLauncher)) {
9761
9753
  log(" retained the windowless Codex hook bridge for the loaded session; restart removes its need");
9762
9754
  }
9763
9755
  }
9764
- const restored = token === "claude" ? restoreMarketplacePinsOnDisk(pinsPath, pins) : void 0;
9756
+ if (failure !== void 0) return { ok: false, detail: failure, refSupported };
9757
+ const restored = host === "claude" ? restoreMarketplacePinsOnDisk(pinsPath, pins) : void 0;
9765
9758
  if (restored) log(` ${restored}`);
9766
- return true;
9759
+ return { ok: true, refSupported };
9760
+ }
9761
+ async function applyPluginHeal(surface, log, opts) {
9762
+ const token = surfaceToken(surface);
9763
+ if (token !== "claude" && token !== "codex" && token !== "kilo") return false;
9764
+ const descriptor = PLUGIN_SURFACE_HEAL[token];
9765
+ if (!descriptor || !descriptor.healSteps) return false;
9766
+ const outcome = await runHealSteps(token, descriptor.healSteps, {
9767
+ run: async (bin, args) => ({ ok: await runPluginCli(bin, args, log) }),
9768
+ log,
9769
+ // kilo-p1: one non-interactive install verb, and the plugin's server() does the rest on the next
9770
+ // /reload — so it names an install, not a marketplace dance.
9771
+ banner: (refSupported) => token === "kilo" ? " \u21BB reinstalling the MMI plugin via `kilo plugin` (install \u2192 server() provisions the skills)\u2026" : healBannerLine(token, token, refSupported)
9772
+ });
9773
+ return outcome.ok;
9767
9774
  }
9768
9775
  async function healClaudePluginForDoctor(surface = detectSurface(process.env), onStep) {
9769
9776
  if (surfaceToken(surface) !== "claude") {
@@ -9978,7 +9985,7 @@ function planUpdatePass(hasBinary) {
9978
9985
  const skipped = [];
9979
9986
  for (const host of DRIVEN_HOSTS) {
9980
9987
  const descriptor = PLUGIN_SURFACE_HEAL[host];
9981
- const steps = (descriptor.healSteps ?? []).map((step) => [...step.args]);
9988
+ const steps = (descriptor.healSteps ?? []).map((step) => ({ args: [...step.args], gated: step.gated }));
9982
9989
  const label = `${host} plugin`;
9983
9990
  if (steps.length === 0) {
9984
9991
  skipped.push({ host, label, ok: true, changed: false, detail: "skipped: no non-interactive update verb declared" });
@@ -10005,14 +10012,9 @@ async function runUpdatePass(deps) {
10005
10012
  let ok = true;
10006
10013
  let detail = "updated";
10007
10014
  try {
10008
- for (const args of arm.steps) {
10009
- const res = await deps.run(arm.bin, args);
10010
- if (!res.ok) {
10011
- ok = false;
10012
- detail = res.detail ?? `\`${arm.bin} ${args.join(" ")}\` failed`;
10013
- break;
10014
- }
10015
- }
10015
+ const outcome = await runHealSteps(arm.host, arm.steps, { run: deps.run });
10016
+ ok = outcome.ok;
10017
+ if (outcome.detail) detail = outcome.detail;
10016
10018
  } catch (e) {
10017
10019
  ok = false;
10018
10020
  detail = e instanceof Error ? e.message : String(e);
@@ -10037,7 +10039,7 @@ async function runUpdatePass(deps) {
10037
10039
  }
10038
10040
  function describeUpdatePlan(hasBinary) {
10039
10041
  const { arms, skipped } = planUpdatePass(hasBinary);
10040
- const lines = arms.map((arm) => `${arm.label}: ${arm.steps.map((args) => `${arm.bin} ${args.join(" ")}`).join(" && ")}`);
10042
+ const lines = arms.map((arm) => `${arm.label}: ${arm.steps.map((step) => `${arm.bin} ${step.args.join(" ")}`).join(" && ")}`);
10041
10043
  for (const note of [...skipped, ...operatorOwnedHosts()]) lines.push(`${note.label}: ${note.detail}`);
10042
10044
  lines.push("marketplace background updates: set auto-update off (this pass is the update channel)");
10043
10045
  lines.push("mmi-cli: npm install -g @mutmutco/cli@<released> \u2014 the cli-version row, immediately after this pass");
@@ -15838,10 +15840,10 @@ var rollout_plan_default = {
15838
15840
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
15839
15841
  },
15840
15842
  baseline: {
15841
- version: "3.124.0",
15842
- tag: "v3.124.0",
15843
- commit: "3e0ff2b23f29",
15844
- npm: "@mutmutco/cli@3.124.0"
15843
+ version: "3.126.0",
15844
+ tag: "v3.126.0",
15845
+ commit: "1ccba0834c2a",
15846
+ npm: "@mutmutco/cli@3.126.0"
15845
15847
  },
15846
15848
  exitCriterion: "fleet-n-of-n",
15847
15849
  hubOnlyShortcut: "forbidden",
@@ -15858,14 +15860,14 @@ var rollout_plan_default = {
15858
15860
  repo: "mutmutco/mmi-hub",
15859
15861
  role: "canary",
15860
15862
  schedule: "train",
15861
- v3Target: "v3.124.0"
15863
+ v3Target: "v3.126.0"
15862
15864
  }
15863
15865
  ],
15864
15866
  rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
15865
15867
  rollback: {
15866
15868
  independent: true,
15867
- mechanism: "npm dist-tag latest -> 3.124.0 and redeploy the Hub Lambda from tag v3.124.0 (3e0ff2b23f29); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15868
- v3Target: "v3.124.0 (@mutmutco/cli@3.124.0, tag commit 3e0ff2b23f29 \u2014 the preserved latest-v3 distribution, D6b)"
15869
+ mechanism: "npm dist-tag latest -> 3.126.0 and redeploy the Hub Lambda from tag v3.126.0 (1ccba0834c2a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15870
+ v3Target: "v3.126.0 (@mutmutco/cli@3.126.0, tag commit 1ccba0834c2a \u2014 the preserved latest-v3 distribution, D6b)"
15869
15871
  }
15870
15872
  },
15871
15873
  {
@@ -36996,6 +36998,7 @@ var surfaces_default = {
36996
36998
  surfaceId: "mmi-cli"
36997
36999
  },
36998
37000
  hookPolicy: {
37001
+ consoleLauncherPath: "bin/mmi-hook-console.cmd",
36999
37002
  launcherPath: "bin/mmi-hook",
37000
37003
  ownerPath: "scripts/hook-policy.mjs",
37001
37004
  runnerPath: "scripts/hook-run.mjs",
@@ -37659,6 +37662,7 @@ var surfaces_default = {
37659
37662
  "scripts/hook-trace.mjs",
37660
37663
  "bin/mmi-hook",
37661
37664
  "bin/mmi-hook.exe",
37665
+ "bin/mmi-hook-console.cmd",
37662
37666
  "native/windows-hook-launcher.c",
37663
37667
  "scripts/build-windows-hook-launcher.ps1",
37664
37668
  "scripts/probe-windows-hook-window.ps1"
@@ -39612,9 +39616,14 @@ function mmiDoctorDeps(opts = {}) {
39612
39616
  // background channel left to protect.
39613
39617
  runUpdatePass: () => runUpdatePass({
39614
39618
  hasBinary: binaryOnPath,
39619
+ // #4873: `runHostBinLogged`, never a bare `execFileP`. Every host verb here is an npm global shim,
39620
+ // which on Windows is `<bin>.cmd` — Node does no PATHEXT resolution, so a bare-name spawn is
39621
+ // ENOENT for all three arms and the pass can never converge on that platform. That helper is the
39622
+ // CLI's one Windows spawn path (`cmd.exe /c`), and it also carries the ignored-stdin and 16 MB
39623
+ // maxBuffer that #4078 established for exactly these plugin verbs.
39615
39624
  run: async (bin, args) => {
39616
39625
  try {
39617
- await execFileP2(bin, args, { timeout: GH_MUTATION_TIMEOUT_MS });
39626
+ await runHostBinLogged(bin, args, { timeout: GH_MUTATION_TIMEOUT_MS, step: `${bin} ${args.join(" ")}` });
39618
39627
  return { ok: true };
39619
39628
  } catch (e) {
39620
39629
  const err = e;
@@ -41182,10 +41191,10 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
41182
41191
  const track = resolveReleaseTrack(m, void 0, target);
41183
41192
  const stages = branchesForTrack(track).join(" -> ");
41184
41193
  const note = track === "direct" ? " (direct \u2014 no rc; /rcand refuses, /release ships development -> main)" : track === "trunk" ? " (trunk \u2014 main only)" : "";
41194
+ const deployNote = m.deployModel === "registry-publish" ? "\ndeploys are repository-owned publish workflows (registry-publish); no central tenant deploy runs for this repo." : "\ndeploys run centrally (tenant-deploy.yml); product repos carry no deploy files.";
41185
41195
  console.error(
41186
41196
  `${m.name ?? target} \u2014 class ${m.class ?? "?"} \u2014 deploy ${m.deployModel ?? "?"}
41187
- release track: ${track} \u2014 stages: ${stages}${note}
41188
- deploys run centrally (tenant-deploy.yml); product repos carry no deploy files. Inspect nonsecret DEPLOY facts with \`mmi-cli oracle org project deploy get\`; full coords remain OIDC-gated.`
41197
+ release track: ${track} \u2014 stages: ${stages}${note}` + deployNote + " Inspect nonsecret DEPLOY facts with `mmi-cli oracle org project deploy get`; full coords remain OIDC-gated."
41189
41198
  );
41190
41199
  }
41191
41200
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.124.0",
3
+ "version": "3.126.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",