@mutmutco/cli 2.20.0 → 2.21.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 +38 -12
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -4826,6 +4826,13 @@ function parseManifestVersion(stdout) {
4826
4826
  return void 0;
4827
4827
  }
4828
4828
  }
4829
+ function npmReleasedVersionArgs() {
4830
+ return ["view", "@mutmutco/cli", "version"];
4831
+ }
4832
+ function parseNpmVersion(stdout) {
4833
+ const v = stdout.trim();
4834
+ return /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(v) ? v : void 0;
4835
+ }
4829
4836
  function versionAutoUpdateAction(report, hasPluginRoot) {
4830
4837
  if (report.ok || report.staleAgainst !== "released") return "none";
4831
4838
  return hasPluginRoot ? "plugin-pull" : "npm";
@@ -7183,6 +7190,7 @@ var ORG_SPINE_FILES = [
7183
7190
  function isSpinePath(path2) {
7184
7191
  return ORG_SPINE_FILES.includes(path2);
7185
7192
  }
7193
+ var RELEASE_TOLERATED_PATHS = [".gitignore"];
7186
7194
  async function predictMergeConflicts(deps, ours, theirs) {
7187
7195
  try {
7188
7196
  await deps.run("git", ["merge-tree", "--write-tree", "--name-only", "--no-messages", ours, theirs]);
@@ -7387,7 +7395,7 @@ function resolveContextState(context, checkRuns, statuses) {
7387
7395
  if (r.name !== context) continue;
7388
7396
  if (r.status === "completed") {
7389
7397
  if (r.conclusion === "success") return "success";
7390
- sawFailure = true;
7398
+ if (r.conclusion !== "skipped" && r.conclusion !== "neutral") sawFailure = true;
7391
7399
  }
7392
7400
  }
7393
7401
  for (const s of statuses) {
@@ -7587,8 +7595,9 @@ async function runTrainApply(command, deps, options = {}) {
7587
7595
  const deployModel2 = await preflight(deps, ctx, "main", meta);
7588
7596
  const tag2 = requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "cycle"])), "release tag");
7589
7597
  const foldPaths2 = await resolveFoldPaths(deps, deployModel2);
7598
+ const tolerated2 = [...foldPaths2, ...RELEASE_TOLERATED_PATHS];
7590
7599
  const predicted2 = await predictMergeConflicts(deps, "origin/main", "origin/development");
7591
- const predictedBlocking2 = predicted2.filter((f) => !isSpinePath(f) && !foldPaths2.includes(f));
7600
+ const predictedBlocking2 = predicted2.filter((f) => !isSpinePath(f) && !tolerated2.includes(f));
7592
7601
  if (predictedBlocking2.length > 0) {
7593
7602
  throw new Error(
7594
7603
  `development -> main merge would conflict on non-spine path(s): ${predictedBlocking2.join(", ")} \u2014 no merge was started. The train is misaligned: reconcile main and development via an approved alignment PR, then rerun release.`
@@ -7599,7 +7608,7 @@ async function runTrainApply(command, deps, options = {}) {
7599
7608
  if (predicted2.length === 0) {
7600
7609
  await deps.run("git", ["merge", "development", "--no-edit"]);
7601
7610
  } else {
7602
- await mergeWithSpineResolution(deps, "development", "development -> main", "theirs", foldPaths2);
7611
+ await mergeWithSpineResolution(deps, "development", "development -> main", "theirs", tolerated2);
7603
7612
  }
7604
7613
  const versionFold2 = await foldReleaseVersion(deps, deployModel2, tag2, foldPaths2);
7605
7614
  const releaseSha2 = requireValue(clean(await deps.run("git", ["rev-parse", "main"])), "release sha");
@@ -7643,8 +7652,9 @@ async function runTrainApply(command, deps, options = {}) {
7643
7652
  );
7644
7653
  const deployModel = await preflight(deps, ctx, "main", meta);
7645
7654
  const foldPaths = await resolveFoldPaths(deps, deployModel);
7655
+ const tolerated = [...foldPaths, ...RELEASE_TOLERATED_PATHS];
7646
7656
  const predicted = await predictMergeConflicts(deps, "origin/main", "origin/rc");
7647
- const predictedBlocking = predicted.filter((f) => !isSpinePath(f) && !foldPaths.includes(f));
7657
+ const predictedBlocking = predicted.filter((f) => !isSpinePath(f) && !tolerated.includes(f));
7648
7658
  if (predictedBlocking.length > 0) {
7649
7659
  throw new Error(
7650
7660
  `rc -> main merge would conflict on non-spine path(s): ${predictedBlocking.join(", ")} \u2014 no merge was started. The train is misaligned: reconcile main and rc via an approved alignment PR (do not hand-resolve on main), then rerun release.`
@@ -7663,7 +7673,7 @@ async function runTrainApply(command, deps, options = {}) {
7663
7673
  if (predicted.length === 0) {
7664
7674
  await deps.run("git", ["merge", "rc", "--no-edit"]);
7665
7675
  } else {
7666
- await mergeWithSpineResolution(deps, "rc", "rc -> main", "theirs", foldPaths);
7676
+ await mergeWithSpineResolution(deps, "rc", "rc -> main", "theirs", tolerated);
7667
7677
  }
7668
7678
  const tag = requireValue(clean(await deps.run("node", ["scripts/next-version.mjs", "release"])), "release tag");
7669
7679
  const versionFold = await foldReleaseVersion(deps, deployModel, tag, foldPaths);
@@ -7682,6 +7692,13 @@ async function runTrainApply(command, deps, options = {}) {
7682
7692
  await deps.run("git", ["pull", "--ff-only", "origin", "development"]);
7683
7693
  await deps.run("git", ["merge", "main", "--no-edit"]);
7684
7694
  await deps.run("git", ["push", "origin", "development"]);
7695
+ let rcAlignment;
7696
+ try {
7697
+ await deps.run("git", ["push", "origin", "main:rc"]);
7698
+ rcAlignment = "origin/rc aligned to the released main";
7699
+ } catch (e) {
7700
+ rcAlignment = `rc alignment push failed \u2014 align manually with \`git push origin main:rc\`: ${e instanceof Error ? e.message : String(e)}`;
7701
+ }
7685
7702
  const environments = await buildEnvironments(deps, ctx, deployModel, d.deployStatus, retirement);
7686
7703
  return {
7687
7704
  ...ctx,
@@ -7701,6 +7718,7 @@ async function runTrainApply(command, deps, options = {}) {
7701
7718
  rcRetirement: retirement.status,
7702
7719
  rcRetirementNote: retirement.note,
7703
7720
  rcRetirementCategory: retirement.category,
7721
+ rcAlignment,
7704
7722
  announceNote,
7705
7723
  release: { tag, url: releaseUrl, targetSha: releaseSha },
7706
7724
  environments
@@ -8014,19 +8032,18 @@ async function findHotfixPr(deps, ctx, tag) {
8014
8032
  "list",
8015
8033
  "--repo",
8016
8034
  ctx.repo,
8017
- "--head",
8018
- hotfixBranch(tag),
8019
8035
  "--base",
8020
8036
  "main",
8021
8037
  "--state",
8022
8038
  "all",
8023
8039
  "--limit",
8024
- "1",
8040
+ "50",
8025
8041
  "--json",
8026
- "number,state,url,mergeCommit"
8042
+ "number,state,url,mergeCommit,headRefName"
8027
8043
  ]);
8028
8044
  const rows = JSON.parse(out || "[]");
8029
- return rows[0] ?? null;
8045
+ const branch = hotfixBranch(tag);
8046
+ return rows.filter((r) => typeof r.headRefName === "string" && (r.headRefName === branch || r.headRefName.startsWith(`${branch}-`))).sort((a, b) => (b.number ?? 0) - (a.number ?? 0))[0] ?? null;
8030
8047
  }
8031
8048
  async function resolveHotfixSource(deps, ctx, from) {
8032
8049
  const prMatch = /^#?(\d+)$/.exec(from.trim());
@@ -8162,9 +8179,9 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
8162
8179
  await deps.run("git", ["merge-base", "--is-ancestor", mergedSha, "origin/main"]).catch(() => {
8163
8180
  throw new Error(`merged hotfix SHA ${mergedSha.slice(0, 7)} is not on origin/main \u2014 refusing to tag`);
8164
8181
  });
8182
+ const tagNote = await ensureTagPushed(deps, tag, mergedSha);
8165
8183
  const required = await discoverRequiredCheckContexts(deps, ctx, "main");
8166
8184
  const checks = await waitForRequiredTrainChecks(deps, ctx, mergedSha, required);
8167
- const tagNote = await ensureTagPushed(deps, tag, mergedSha);
8168
8185
  let releaseNote;
8169
8186
  let releaseExists = false;
8170
8187
  try {
@@ -11237,6 +11254,7 @@ async function fetchReleasedVersion() {
11237
11254
  }
11238
11255
  }
11239
11256
  var NPM_UPDATE_TIMEOUT_MS = 12e4;
11257
+ var NPM_VIEW_TIMEOUT_MS = 15e3;
11240
11258
  var PLUGIN_PULL_TIMEOUT_MS = 3e4;
11241
11259
  async function applyVersionAutoUpdate(report, log) {
11242
11260
  const action = versionAutoUpdateAction(report, Boolean(process.env.CLAUDE_PLUGIN_ROOT));
@@ -11260,10 +11278,18 @@ async function applyVersionAutoUpdate(report, log) {
11260
11278
  return report;
11261
11279
  }
11262
11280
  }
11281
+ async function fetchNpmReleasedVersion() {
11282
+ try {
11283
+ const { stdout } = await runHostBin("npm", npmReleasedVersionArgs(), { timeout: NPM_VIEW_TIMEOUT_MS });
11284
+ return parseNpmVersion(stdout);
11285
+ } catch {
11286
+ return void 0;
11287
+ }
11288
+ }
11263
11289
  async function requireFreshTrainCli(commandName) {
11264
11290
  const report = buildVersionLagReport({
11265
11291
  currentVersion: resolveClientVersion(),
11266
- releasedVersion: await fetchReleasedVersion()
11292
+ releasedVersion: await fetchNpmReleasedVersion()
11267
11293
  });
11268
11294
  if (report.ok) return;
11269
11295
  throw new Error(`running mmi-cli ${report.currentVersion} is stale against released ${report.releasedVersion}; run doctor/update first so ${commandName} uses the current train path`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "MMI Future CLI — delivers the org rules (whole-file), plus saga and KB access. The cross-IDE engine the plugin's SessionStart hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",