@mutmutco/cli 3.8.0 → 3.9.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 +144 -26
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -9750,6 +9750,19 @@ function requireValue(value, label) {
9750
9750
  if (!value) throw new Error(`${label} could not be resolved`);
9751
9751
  return value;
9752
9752
  }
9753
+ function surfaceTrainSubprocessFailure(e, file, args) {
9754
+ if (!(e instanceof Error)) return e;
9755
+ const err = e;
9756
+ const stderr = typeof err.stderr === "string" ? err.stderr.trim() : "";
9757
+ const stdout = typeof err.stdout === "string" ? err.stdout.trim() : "";
9758
+ const cmd = `${file} ${args.join(" ")}`.trim();
9759
+ if (!stderr && !stdout) {
9760
+ err.message = `${cmd} failed with no stderr output (exit ${err.code ?? "?"}) \u2014 likely a transient subprocess/network error. [${err.message}]`;
9761
+ } else if (/^Command failed/i.test(err.message) && stderr) {
9762
+ err.message = `${cmd} failed: ${stderr}`;
9763
+ }
9764
+ return err;
9765
+ }
9753
9766
  function planTrainApplyRepoGuard(applyRepo, cwdRepo, rerun) {
9754
9767
  if (cwdRepo && cwdRepo.toLowerCase() === applyRepo.toLowerCase()) return { ok: true };
9755
9768
  const where = cwdRepo ? `this checkout is ${cwdRepo}` : "this checkout could not be identified";
@@ -9817,14 +9830,14 @@ async function verifyPublishedRelease(deps, repo, tag, expectedTarget, expectedS
9817
9830
  if (tagSha !== expectedSha) {
9818
9831
  throw new Error(`Release ${tag} tag points at ${tagSha}, expected ${expectedSha}`);
9819
9832
  }
9820
- const branchOut = clean(await deps.run("git", ["ls-remote", "origin", `refs/heads/${expectedTarget}`]));
9833
+ const branchOut = clean(await runGitRemoteRead(deps, ["ls-remote", "origin", `refs/heads/${expectedTarget}`]));
9821
9834
  const branchSha = requireValue(branchOut.split(/\s+/)[0] ?? "", `origin/${expectedTarget} sha`);
9822
9835
  if (branchSha !== expectedSha) {
9823
9836
  throw new Error(`origin/${expectedTarget} points at ${branchSha}, expected ${expectedSha}`);
9824
9837
  }
9825
9838
  }
9826
9839
  async function ffOnlyPull(deps, branch) {
9827
- await deps.run("git", ["pull", "--ff-only", "origin", branch]);
9840
+ await runGitRemoteRead(deps, ["pull", "--ff-only", "origin", branch]);
9828
9841
  }
9829
9842
  var RELEASE_TOLERATED_PATHS = [".gitignore"];
9830
9843
  var MERGE_TREE_PREFLIGHT_ATTEMPTS = 3;
@@ -9878,7 +9891,7 @@ function ensurePositiveCount(out, emptyMessage) {
9878
9891
  if (count <= 0) throw new Error(emptyMessage);
9879
9892
  }
9880
9893
  async function remoteBranchExists(deps, branch) {
9881
- const out = clean(await deps.run("git", ["ls-remote", "origin", `refs/heads/${branch}`]));
9894
+ const out = clean(await runGitRemoteRead(deps, ["ls-remote", "origin", `refs/heads/${branch}`]));
9882
9895
  return out.length > 0;
9883
9896
  }
9884
9897
  async function loadReleaseTrackBranchHints(deps) {
@@ -10038,6 +10051,32 @@ var defaultSleep2 = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
10038
10051
  function resolveSleep(deps) {
10039
10052
  return deps.sleep ?? defaultSleep2;
10040
10053
  }
10054
+ var GIT_NETWORK_ATTEMPTS = 3;
10055
+ var GIT_NETWORK_RETRY_DELAY_MS = 1e3;
10056
+ function isPersistentGitPushFailure(e) {
10057
+ const msg = `${e instanceof Error ? e.message : String(e)} ${String(e.stderr ?? "")}`;
10058
+ return /non-fast-forward|\[rejected\]|failed to push|protected branch|hook declined|permission denied|could not read Username|authentication failed|Authentication failed|403 Forbidden|GH006|GH013/i.test(msg);
10059
+ }
10060
+ async function runGitWithRetry(deps, args, shouldRetry) {
10061
+ const sleep = resolveSleep(deps);
10062
+ let lastError;
10063
+ for (let attempt = 1; attempt <= GIT_NETWORK_ATTEMPTS; attempt++) {
10064
+ try {
10065
+ return await deps.run("git", args);
10066
+ } catch (e) {
10067
+ lastError = e;
10068
+ if (attempt >= GIT_NETWORK_ATTEMPTS || !shouldRetry(e)) throw e;
10069
+ await sleep(GIT_NETWORK_RETRY_DELAY_MS * attempt);
10070
+ }
10071
+ }
10072
+ throw lastError;
10073
+ }
10074
+ function runGitRemoteRead(deps, args) {
10075
+ return runGitWithRetry(deps, args, () => true);
10076
+ }
10077
+ function runGitPush(deps, args) {
10078
+ return runGitWithRetry(deps, args, (e) => !isPersistentGitPushFailure(e));
10079
+ }
10041
10080
  var TRAIN_CHECK_RUNS_JQ = "[.check_runs[]|{name:.name,status:.status,conclusion:.conclusion,startedAt:.started_at}]";
10042
10081
  var TRAIN_COMMIT_STATUS_JQ = "[.statuses[]|{context:.context,state:.state}]";
10043
10082
  var TRAIN_PROTECTION_CONTEXTS_JQ = "[.contexts[]]";
@@ -10312,9 +10351,12 @@ Recovery sequence:
10312
10351
  Do not delete or force-move the pushed tag; rerun the train only after confirming the branch, release, and deploy states above.`
10313
10352
  );
10314
10353
  }
10315
- async function ensureTagPushed(deps, tag, sha) {
10316
- const remoteOut = await deps.run("git", ["ls-remote", "origin", `refs/tags/${tag}`]);
10317
- const remoteSha = clean(remoteOut).split(/\s+/)[0] || "";
10354
+ async function probeRemoteTag(deps, tag) {
10355
+ const remoteOut = await runGitRemoteRead(deps, ["ls-remote", "origin", `refs/tags/${tag}`]);
10356
+ return clean(remoteOut).split(/\s+/)[0] || "";
10357
+ }
10358
+ async function ensureTagPushed(deps, tag, sha, probed) {
10359
+ const remoteSha = probed ? probed.remoteSha : await probeRemoteTag(deps, tag);
10318
10360
  let localSha = "";
10319
10361
  try {
10320
10362
  localSha = clean(await deps.run("git", ["rev-parse", "--verify", `refs/tags/${tag}^{commit}`]));
@@ -10335,11 +10377,14 @@ async function ensureTagPushed(deps, tag, sha) {
10335
10377
  throw new Error(`local tag ${tag} already points at ${localSha}, not the intended ${sha} \u2014 delete the stale local tag (git tag -d ${tag}) and rerun`);
10336
10378
  }
10337
10379
  if (!localSha) await deps.run("git", ["tag", tag, sha]);
10338
- await deps.run("git", ["push", "origin", tag]);
10380
+ await runGitPush(deps, ["push", "origin", tag]);
10339
10381
  return { note: `tag ${tag} pushed at ${sha.slice(0, 7)}`, pushed: true };
10340
10382
  }
10341
- async function resolveRcResumeTag(deps, base, sha) {
10342
- const out = await deps.run("git", ["ls-remote", "--tags", "origin", `refs/tags/${base}-rc.*`]);
10383
+ function probeRcResumeTags(deps, base) {
10384
+ return runGitRemoteRead(deps, ["ls-remote", "--tags", "origin", `refs/tags/${base}-rc.*`]);
10385
+ }
10386
+ async function resolveRcResumeTag(deps, base, sha, probed) {
10387
+ const out = probed ?? await runGitRemoteRead(deps, ["ls-remote", "--tags", "origin", `refs/tags/${base}-rc.*`]);
10343
10388
  const onSha = clean(out).split("\n").map((line) => line.trim()).filter(Boolean).map((line) => line.split(/\s+/)).filter(([refSha]) => refSha === sha).map(([, ref]) => ref.replace(/^refs\/tags\//, "").replace(/\^\{\}$/, "")).filter((ref) => new RegExp(`^${base.replace(/\./g, "\\.")}-rc\\.\\d+$`).test(ref));
10344
10389
  const unique = [...new Set(onSha)];
10345
10390
  if (unique.length === 0) return { tag: null };
@@ -10590,6 +10635,49 @@ Recovery sequence:
10590
10635
  ${steps.join("\n")}`
10591
10636
  );
10592
10637
  }
10638
+ async function recoverFailedRcand(deps, cause, preRcSha) {
10639
+ const causeMessage = cause instanceof Error ? cause.message : String(cause);
10640
+ const branch = await currentBranch(deps).catch(() => "");
10641
+ const mergeInProgress = await deps.run("git", ["rev-parse", "-q", "--verify", "MERGE_HEAD"]).then(() => true).catch(() => false);
10642
+ if (mergeInProgress) {
10643
+ try {
10644
+ await deps.run("git", ["merge", "--abort"]);
10645
+ } catch (e) {
10646
+ return new Error(
10647
+ `${causeMessage}
10648
+
10649
+ rcand failed with an in-progress merge on ${branch || "(unknown branch)"}; automatic \`git merge --abort\` failed: ${e instanceof Error ? e.message : String(e)}. Nothing was pushed to origin.
10650
+ Recovery sequence:
10651
+ 1. git merge --abort
10652
+ 2. git checkout development
10653
+ 3. git branch -f rc origin/rc`
10654
+ );
10655
+ }
10656
+ }
10657
+ const rcSha = await deps.run("git", ["rev-parse", "rc"]).then(clean).catch(() => "");
10658
+ const rcDescends = Boolean(preRcSha) && Boolean(rcSha) ? await deps.run("git", ["merge-base", "--is-ancestor", preRcSha, "rc"]).then(() => true).catch(() => false) : false;
10659
+ if (branch === "rc" && rcDescends) {
10660
+ try {
10661
+ await deps.run("git", ["reset", "--hard", preRcSha]);
10662
+ await deps.run("git", ["checkout", "development"]);
10663
+ return new Error(
10664
+ `${causeMessage}
10665
+
10666
+ rcand failed after \`git checkout rc\`; local rc was reset to its pre-merge state (${shaLabel(preRcSha)}) and the checkout returned to development. Nothing was pushed to origin. Rerun \`mmi-cli rcand --apply\`.`
10667
+ );
10668
+ } catch {
10669
+ }
10670
+ }
10671
+ return new Error(
10672
+ `${causeMessage}
10673
+
10674
+ rcand failed leaving the checkout on ${branch || "(unknown)"} (pre-merge rc=${preRcSha ? shaLabel(preRcSha) : "not captured"}). Nothing was pushed to origin.
10675
+ Recovery sequence:
10676
+ 1. git checkout development
10677
+ 2. git branch -f rc origin/rc # discard the unpushed local rc merge
10678
+ 3. mmi-cli rcand --apply`
10679
+ );
10680
+ }
10593
10681
  async function recoverFailedFold(deps, cause, startBranch, preFoldMainSha) {
10594
10682
  const causeMessage = cause instanceof Error ? cause.message : String(cause);
10595
10683
  const probe = await probeFoldFailureState(deps);
@@ -10641,8 +10729,13 @@ async function runFoldStage(deps, startBranch, preFold, fn) {
10641
10729
  throw await recoverFailedFold(deps, e, startBranch, preFold.mainSha);
10642
10730
  }
10643
10731
  }
10644
- async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha) {
10645
- const tagPush = await ensureTagPushed(deps, tag, releaseSha);
10732
+ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe) {
10733
+ let tagPush;
10734
+ try {
10735
+ tagPush = await ensureTagPushed(deps, tag, releaseSha, tagProbe);
10736
+ } catch (e) {
10737
+ throw await recoverFailedFold(deps, e, startBranch, preFold.mainSha);
10738
+ }
10646
10739
  const tagPushSince = (deps.now ?? Date.now)();
10647
10740
  const requiredChecks = await discoverRequiredCheckContexts(deps, ctx, "main");
10648
10741
  let checks;
@@ -10651,7 +10744,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
10651
10744
  } catch (e) {
10652
10745
  throw partialTrainRecoveryError(e, { repo: ctx.repo, tag, stage: "main" });
10653
10746
  }
10654
- await deps.run("git", ["push", "origin", "main"]);
10747
+ await runGitPush(deps, ["push", "origin", "main"]);
10655
10748
  const releaseUrl = clean(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
10656
10749
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
10657
10750
  const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
@@ -10692,14 +10785,24 @@ async function runTrainApplyPipeline(mode, input) {
10692
10785
  const deployModel2 = await preflight(deps, ctx, "rc", meta);
10693
10786
  const releaseBase = requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "cycle"])), "release base");
10694
10787
  await releaseRcBranchLock(deps);
10788
+ const rcTagProbe = await probeRcResumeTags(deps, releaseBase);
10695
10789
  await deps.run("git", ["checkout", "rc"]);
10696
10790
  await ffOnlyPull(deps, "rc");
10697
- await deps.run("git", ["merge", "development", "--no-edit"]);
10698
- const rcSha = requireValue(clean(await deps.run("git", ["rev-parse", "rc"])), "rc sha");
10699
- const resume = await resolveRcResumeTag(deps, releaseBase, rcSha);
10700
- const tag2 = resume.tag ?? requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "rc"])), "rc tag");
10791
+ const preRcSha = requireValue(clean(await deps.run("git", ["rev-parse", "rc"])), "pre-merge rc sha");
10792
+ let rcSha;
10793
+ let resume;
10794
+ let tag2;
10795
+ let tagPush;
10796
+ try {
10797
+ await deps.run("git", ["merge", "development", "--no-edit"]);
10798
+ rcSha = requireValue(clean(await deps.run("git", ["rev-parse", "rc"])), "rc sha");
10799
+ resume = await resolveRcResumeTag(deps, releaseBase, rcSha, rcTagProbe);
10800
+ tag2 = resume.tag ?? requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "rc"])), "rc tag");
10801
+ tagPush = await ensureTagPushed(deps, tag2, rcSha);
10802
+ } catch (e) {
10803
+ throw await recoverFailedRcand(deps, e, preRcSha);
10804
+ }
10701
10805
  const resumeNote = resume.tag ? resume.note : void 0;
10702
- const tagPush = await ensureTagPushed(deps, tag2, rcSha);
10703
10806
  const tagPushSince = (deps.now ?? Date.now)();
10704
10807
  const requiredChecks = await discoverRequiredCheckContexts(deps, ctx, "rc");
10705
10808
  let checks2;
@@ -10709,7 +10812,7 @@ async function runTrainApplyPipeline(mode, input) {
10709
10812
  throw partialTrainRecoveryError(e, { repo: ctx.repo, tag: tag2, stage: "rc" });
10710
10813
  }
10711
10814
  const autoRunSince = (deps.now ?? Date.now)();
10712
- await deps.run("git", ["push", "origin", "rc"]);
10815
+ await runGitPush(deps, ["push", "origin", "rc"]);
10713
10816
  const d2 = await dispatchDeploy(deps, ctx, "rc", "rc", deployModel2, watch, autoRunSince, rcSha);
10714
10817
  return { ...ctx, command, stage: "rc", ref: "rc", tag: tag2, deployModel: deployModel2, promoted: true, checks: checks2, resumeNote, dispatch: d2.note, runId: d2.runId, runUrl: d2.runUrl, workflowRuns: d2.workflowRuns, deployStatus: d2.deployStatus };
10715
10818
  }
@@ -10741,6 +10844,7 @@ async function runTrainApplyPipeline(mode, input) {
10741
10844
  "development -> main merge would conflict on untolerated path(s)",
10742
10845
  "The train is misaligned: reconcile main and development via an approved alignment PR, then rerun release."
10743
10846
  );
10847
+ const tagProbe2 = { remoteSha: await probeRemoteTag(deps, tag2) };
10744
10848
  const preFold2 = {};
10745
10849
  const { versionFold: versionFold2, releaseSha: releaseSha2 } = await runFoldStage(deps, "development", preFold2, async () => {
10746
10850
  await executeMergeToMain(deps, "development", "development -> main", tolerated2, predicted2, preFold2);
@@ -10748,7 +10852,7 @@ async function runTrainApplyPipeline(mode, input) {
10748
10852
  const releaseSha3 = requireValue(clean(await deps.run("git", ["rev-parse", "main"])), "release sha");
10749
10853
  return { versionFold: versionFold3, releaseSha: releaseSha3 };
10750
10854
  });
10751
- const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2 } = await completeMainRelease(deps, ctx, meta, deployModel2, watch, options, tag2, releaseSha2);
10855
+ const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2 } = await completeMainRelease(deps, ctx, meta, deployModel2, watch, options, tag2, releaseSha2, "development", preFold2, tagProbe2);
10752
10856
  const devRollForward2 = await rollDevelopmentForward(deps, ctx, tag2);
10753
10857
  if (directTrack) {
10754
10858
  return {
@@ -10824,15 +10928,16 @@ async function runTrainApplyPipeline(mode, input) {
10824
10928
  );
10825
10929
  }
10826
10930
  const releasedRcSha = clean(await deps.run("git", ["rev-parse", "origin/rc"]));
10931
+ const tag = requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "release"])), "release tag");
10932
+ const tagProbe = { remoteSha: await probeRemoteTag(deps, tag) };
10827
10933
  const preFold = {};
10828
- const { tag, versionFold, releaseSha } = await runFoldStage(deps, "rc", preFold, async () => {
10934
+ const { versionFold, releaseSha } = await runFoldStage(deps, "rc", preFold, async () => {
10829
10935
  await executeMergeToMain(deps, "rc", "rc -> main", tolerated, predicted, preFold);
10830
- const tag2 = requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "release"])), "release tag");
10831
- const versionFold2 = await foldReleaseVersion(deps, deployModel, tag2, foldPaths);
10936
+ const versionFold2 = await foldReleaseVersion(deps, deployModel, tag, foldPaths);
10832
10937
  const releaseSha2 = requireValue(clean(await deps.run("git", ["rev-parse", "main"])), "release sha");
10833
- return { tag: tag2, versionFold: versionFold2, releaseSha: releaseSha2 };
10938
+ return { versionFold: versionFold2, releaseSha: releaseSha2 };
10834
10939
  });
10835
- const { checks, releaseUrl, announceNote, dispatch: d } = await completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha);
10940
+ const { checks, releaseUrl, announceNote, dispatch: d } = await completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, "rc", preFold, tagProbe);
10836
10941
  const retirement = await retireRcRuntime(deps, ctx, deployModel, d.deployStatus, releasedRcSha);
10837
10942
  const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
10838
10943
  const rcAlignment = await pushRcAlignment(deps);
@@ -10866,7 +10971,7 @@ async function runTrainApply(command, deps, options = {}) {
10866
10971
  const watch = options.watch ?? false;
10867
10972
  const ctx = await buildTrainApplyContext(deps);
10868
10973
  await requireCleanTree(deps);
10869
- await deps.run("git", ["fetch", "origin"]);
10974
+ await runGitRemoteRead(deps, ["fetch", "origin"]);
10870
10975
  const meta = requireProjectMetaForTrain(await loadProjectMeta(deps, ctx), ctx.repo);
10871
10976
  const branchHints = await loadReleaseTrackBranchHints(deps);
10872
10977
  const directTrack = isHubControlRepo(ctx.repo) || resolveReleaseTrack(meta, branchHints) === "direct";
@@ -13659,6 +13764,11 @@ function parseFofuEnabledVar(raw) {
13659
13764
  if (raw === "false") return false;
13660
13765
  throw new Error("project set: fofuEnabled must be true or false");
13661
13766
  }
13767
+ function parseRuntimeVaultOnlyVar(raw) {
13768
+ if (raw === "true") return true;
13769
+ if (raw === "false") return false;
13770
+ throw new Error("project set: runtimeVaultOnly must be true or false");
13771
+ }
13662
13772
  function parseConsumesDesignSystemVar(raw) {
13663
13773
  if (raw === "fofu") return raw;
13664
13774
  throw new Error('project set: consumesDesignSystem must be "fofu"');
@@ -13736,6 +13846,7 @@ var SETTABLE_VAR_KEYS = [
13736
13846
  "dsManifestPath",
13737
13847
  "fofuEnabled",
13738
13848
  "consumesDesignSystem",
13849
+ "runtimeVaultOnly",
13739
13850
  "requiredGcpApis",
13740
13851
  "requiredRuntimeSecrets",
13741
13852
  "requiredBuildSecrets",
@@ -13758,6 +13869,7 @@ var SETTABLE_VAR_HINTS = {
13758
13869
  dsManifestPath: "relative path to a package.json, e.g. web/package.json",
13759
13870
  fofuEnabled: "true|false",
13760
13871
  consumesDesignSystem: '"fofu"',
13872
+ runtimeVaultOnly: "true|false",
13761
13873
  repos: 'JSON array, e.g. ["mutmutco/mm-foo"]',
13762
13874
  oauth: "JSON {subdomains,domains,callbackPath,fofuSubdomain}",
13763
13875
  requiredGcpApis: "comma-string",
@@ -13839,6 +13951,8 @@ function buildProjectSetPatch(input) {
13839
13951
  patch[key] = parsePublishRequiredVar(raw);
13840
13952
  } else if (key === "fofuEnabled") {
13841
13953
  patch[key] = parseFofuEnabledVar(raw);
13954
+ } else if (key === "runtimeVaultOnly") {
13955
+ patch[key] = parseRuntimeVaultOnlyVar(raw);
13842
13956
  } else if (key === "consumesDesignSystem") {
13843
13957
  patch[key] = parseConsumesDesignSystemVar(raw);
13844
13958
  } else if (key === "publishDir") {
@@ -20887,7 +21001,11 @@ function trainApplyDeps() {
20887
21001
  return {
20888
21002
  run: async (file, args) => {
20889
21003
  const timeout = file === "node" && args[1] === "prepare" ? NODE_PREPARE_TIMEOUT_MS : file === "npm" ? NPM_TRAIN_TIMEOUT_MS : file !== "gh" ? GIT_TIMEOUT_MS : args[0] === "run" && args[1] === "watch" ? GH_RUN_WATCH_TIMEOUT_MS : GH_TRAIN_TIMEOUT_MS;
20890
- return isWin2 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
21004
+ try {
21005
+ return isWin2 && file === "npm" ? (await execFileP2("cmd.exe", ["/c", "npm", ...args], { timeout })).stdout : (await execFileP2(file, args, { timeout })).stdout;
21006
+ } catch (e) {
21007
+ throw surfaceTrainSubprocessFailure(e, file, args);
21008
+ }
20891
21009
  },
20892
21010
  runSelf: async (args) => (await execFileP2(process.execPath, [process.argv[1], ...args], { timeout: 3e4 })).stdout,
20893
21011
  trainAuthority: async (repo) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",