@mutmutco/cli 3.105.6 → 3.105.8

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 +177 -43
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -11392,11 +11392,11 @@ async function installAppFoldDeps(deps) {
11392
11392
  const hasLock = await deps.run("git", ["cat-file", "-e", "HEAD:package-lock.json"]).then(() => true).catch(() => false);
11393
11393
  await deps.run("npm", hasLock ? ["ci"] : ["install"]);
11394
11394
  }
11395
- async function foldReleaseVersion(deps, model, tag, foldPaths) {
11395
+ async function foldReleaseVersion(deps, model, tag, foldPaths, sourceCommit = "HEAD") {
11396
11396
  if (foldPaths.length === 0) return "no version manifest to fold \u2014 the tag is the version";
11397
11397
  const version = tag.replace(/^v/, "");
11398
11398
  if (model === "hub-serverless") {
11399
- await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", "HEAD"]);
11399
+ await deps.run("node", ["scripts/release-distribution.mjs", "prepare", version, "--source-commit", sourceCommit]);
11400
11400
  } else {
11401
11401
  await installAppFoldDeps(deps);
11402
11402
  await deps.run("npm", ["version", version, "--no-git-tag-version", "--allow-same-version"]);
@@ -20516,7 +20516,14 @@ async function runHotfixStart(deps, options) {
20516
20516
  notes.push(`deleted stale origin/${branch} left by the incomplete train`);
20517
20517
  }
20518
20518
  }
20519
- const { sha, label } = await resolveHotfixSource(deps, ctx, options.from);
20519
+ const specs = splitCarrySpecs([options.from]);
20520
+ if (specs.length === 0) throw new Error("hotfix start: --from named no PR or SHA");
20521
+ const sources = [];
20522
+ for (const spec of specs) {
20523
+ const resolved = await resolveHotfixSource(deps, ctx, spec);
20524
+ if (!sources.some((s) => s.sha === resolved.sha)) sources.push(resolved);
20525
+ }
20526
+ const label = sources.map((s) => s.label).join(", ");
20520
20527
  const foldPaths = deployModel === "hub-serverless" || deployModel === "registry-publish" ? await resolveFoldPaths(deps, deployModel) : [];
20521
20528
  const pickTolerated = deployModel === "hub-serverless" ? [...foldPaths, ...HOTFIX_SKILL_TOLERATED_ROOTS] : foldPaths;
20522
20529
  if (deployModel === "hub-serverless") {
@@ -20532,9 +20539,11 @@ async function runHotfixStart(deps, options) {
20532
20539
  const preexistingLocal = clean3(await deps.run("git", ["branch", "--list", branch]));
20533
20540
  await deps.run("git", ["checkout", "-B", branch, "origin/main"]);
20534
20541
  try {
20535
- const autoResolved = await cherryPickWithToleratedPaths(deps, sha, pickTolerated);
20536
- if (autoResolved.length > 0) {
20537
- notes.push(`auto-resolved regenerable cherry-pick conflict(s): ${autoResolved.join(", ")} (regenerated in bump step)`);
20542
+ for (const source of sources) {
20543
+ const autoResolved = await cherryPickWithToleratedPaths(deps, source.sha, pickTolerated);
20544
+ if (autoResolved.length > 0) {
20545
+ notes.push(`auto-resolved regenerable cherry-pick conflict(s) for ${source.label}: ${autoResolved.join(", ")} (regenerated in bump step)`);
20546
+ }
20538
20547
  }
20539
20548
  } catch (e) {
20540
20549
  const recovery = await restoreAfterFailedPort(deps, { startBranch, branch, created: !preexistingLocal });
@@ -20575,10 +20584,12 @@ async function runHotfixStart(deps, options) {
20575
20584
  branch,
20576
20585
  "--title",
20577
20586
  `[hotfix] ${tag}`,
20587
+ // Every picked sha goes in the marker (#4411) — `hotfix release` reads it when --carries is omitted,
20588
+ // so a multi-fix cycle proves ALL of its targets present before tagging, not just the first.
20578
20589
  "--body",
20579
20590
  `Hotfix ${tag}: cherry-pick of ${label} onto origin/main${bumpNote}.
20580
20591
 
20581
- <!-- mmi-hotfix-carries: ${sha} -->
20592
+ <!-- mmi-hotfix-carries: ${sources.map((s) => s.sha).join(",")} -->
20582
20593
 
20583
20594
  Merge this PR (human-initiated), then run \`mmi-cli hotfix release ${tag}\`.`
20584
20595
  ]));
@@ -20621,6 +20632,61 @@ async function watchReleaseRun(deps, ctx, workflow, sha) {
20621
20632
  }
20622
20633
  return { workflow, conclusion: "not-found" };
20623
20634
  }
20635
+ function devFoldBranch(tag) {
20636
+ return `hotfix-fold/${tag}`;
20637
+ }
20638
+ async function portFoldToDevelopment(deps, ctx, deployModel, tag) {
20639
+ const foldPaths = deployModel === "hub-serverless" || deployModel === "registry-publish" ? await resolveFoldPaths(deps, deployModel) : [];
20640
+ if (foldPaths.length === 0) return `fold port skipped (deployModel=${deployModel} folds no version manifest)`;
20641
+ const branch = devFoldBranch(tag);
20642
+ const listed = await deps.run("gh", [
20643
+ "pr",
20644
+ "list",
20645
+ "--repo",
20646
+ ctx.repo,
20647
+ "--head",
20648
+ branch,
20649
+ "--base",
20650
+ "development",
20651
+ "--state",
20652
+ "all",
20653
+ "--limit",
20654
+ "10",
20655
+ "--json",
20656
+ "number,state,url"
20657
+ ]);
20658
+ const existing = JSON.parse(listed || "[]").filter((r) => r.state === "OPEN" || r.state === "MERGED").sort((a, b) => (b.number ?? 0) - (a.number ?? 0))[0];
20659
+ if (existing) return `development fold PR #${existing.number} for ${tag} is ${existing.state} \u2014 reused`;
20660
+ const previousRef = clean3(await deps.run("git", ["rev-parse", "--abbrev-ref", "HEAD"]));
20661
+ try {
20662
+ await deps.run("git", ["fetch", "origin", "development"]);
20663
+ await deps.run("git", ["checkout", "-B", branch, "origin/development"]);
20664
+ const durableSource = clean3(await deps.run("git", ["merge-base", "HEAD", "origin/development"]));
20665
+ const foldNote = await foldReleaseVersion(deps, deployModel, tag, foldPaths, durableSource);
20666
+ const committed = clean3(await deps.run("git", ["rev-list", "--count", "origin/development..HEAD"]));
20667
+ if (committed === "0") return `development already carries ${tag} \u2014 no fold PR needed (${foldNote})`;
20668
+ await deps.run("git", ["push", "-u", "origin", branch]);
20669
+ const prUrl = clean3(await deps.run("gh", [
20670
+ "pr",
20671
+ "create",
20672
+ "--repo",
20673
+ ctx.repo,
20674
+ "--base",
20675
+ "development",
20676
+ "--head",
20677
+ branch,
20678
+ "--title",
20679
+ `chore(release): port the ${tag} version fold to development`,
20680
+ "--body",
20681
+ `Regenerated version fold for ${tag} (#4410). \`main\` carries the released version; without this development declares the previous one and every PR into it fails catalog-lockstep.
20682
+
20683
+ Generated by \`mmi-cli hotfix release\` \u2014 no main-parented commit is merged in, so the squash parents stay clean (#4365/#4371).`
20684
+ ]));
20685
+ return `opened development fold PR ${prUrl} (${foldNote})`;
20686
+ } finally {
20687
+ if (previousRef && previousRef !== "HEAD") await deps.run("git", ["checkout", previousRef]).catch(() => void 0);
20688
+ }
20689
+ }
20624
20690
  async function runHotfixRelease(deps, versionInput, options = {}) {
20625
20691
  const ctx = await buildTrainApplyContext(deps);
20626
20692
  const deployModel = await resolveHotfixDeployModel(deps, ctx);
@@ -20746,6 +20812,12 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
20746
20812
  } else {
20747
20813
  verifyNote = `distribution verify skipped (deployModel=${deployModel}, Hub-only step)`;
20748
20814
  }
20815
+ let foldNote;
20816
+ try {
20817
+ foldNote = await portFoldToDevelopment(deps, ctx, deployModel, tag);
20818
+ } catch (e) {
20819
+ foldNote = `development fold port FAILED: ${e.message ?? e} \u2014 the release stands; port it by hand: git checkout -B ${devFoldBranch(tag)} origin/development && node scripts/release-distribution.mjs prepare ${version}, then open a development-base PR`;
20820
+ }
20749
20821
  return {
20750
20822
  ...ctx,
20751
20823
  command: "hotfix-release",
@@ -20758,6 +20830,7 @@ async function runHotfixRelease(deps, versionInput, options = {}) {
20758
20830
  runs,
20759
20831
  deployNote,
20760
20832
  verifyNote,
20833
+ foldNote,
20761
20834
  announceNote
20762
20835
  };
20763
20836
  }
@@ -29109,6 +29182,21 @@ function derivePollState(buckets) {
29109
29182
  if (anyFailure) return anyPending ? "failing" : "failure";
29110
29183
  return anyPending ? "pending" : "success";
29111
29184
  }
29185
+ function partitionByRequired(entries, requiredContexts) {
29186
+ if (!requiredContexts || requiredContexts.size === 0) {
29187
+ return { relevant: entries.map((e) => e.bucket), ignoredFailures: [] };
29188
+ }
29189
+ const relevant = [];
29190
+ const ignoredFailures = [];
29191
+ for (const entry of entries) {
29192
+ if (requiredContexts.has(entry.name)) {
29193
+ relevant.push(entry.bucket);
29194
+ } else if (entry.bucket === "fail") {
29195
+ ignoredFailures.push(entry.name);
29196
+ }
29197
+ }
29198
+ return { relevant, ignoredFailures };
29199
+ }
29112
29200
  function parseNdjsonLines(stdout) {
29113
29201
  return stdout.split(/\r?\n/).filter((line) => line.trim()).map((line) => JSON.parse(line));
29114
29202
  }
@@ -29147,17 +29235,22 @@ async function fetchHeadCheckRuns(headSha, repo, gh) {
29147
29235
  const runsOut = await gh(["--paginate", `repos/${repo}/commits/${headSha}/check-runs?per_page=100`, "--jq", ".check_runs[] | {id, name, status, conclusion, app_id: .app.id}"]);
29148
29236
  return dedupeLatestCheckRuns(parseNdjsonLines(runsOut));
29149
29237
  }
29150
- async function fetchHeadCheckBuckets(headSha, repo, gh) {
29238
+ async function fetchHeadCheckEntries(headSha, repo, gh) {
29151
29239
  const [runs, statusesOut] = await Promise.all([
29152
29240
  fetchHeadCheckRuns(headSha, repo, gh),
29153
29241
  gh(["--paginate", `repos/${repo}/commits/${headSha}/status?per_page=100`, "--jq", ".statuses[] | {context, state}"])
29154
29242
  ]);
29155
29243
  const statuses = parseNdjsonLines(statusesOut);
29156
- return [...runs.map(classifyCheckRun), ...statuses.map((s) => classifyCommitStatus(s.state))];
29244
+ return [
29245
+ ...runs.map((run) => ({ name: run.name ?? `check-run ${run.id ?? "unknown"}`, bucket: classifyCheckRun(run) })),
29246
+ ...statuses.map((s) => ({ name: s.context ?? "unknown-status", bucket: classifyCommitStatus(s.state) }))
29247
+ ];
29157
29248
  }
29158
- async function pollRestPrChecks(prNumber, repo, gh = defaultGhApi) {
29249
+ async function pollRestPrChecks(prNumber, repo, gh = defaultGhApi, requiredContexts) {
29159
29250
  const snapshot = await fetchRestPrSnapshot(prNumber, repo, gh);
29160
- return derivePollState(await fetchHeadCheckBuckets(snapshot.headSha, repo, gh));
29251
+ const entries = await fetchHeadCheckEntries(snapshot.headSha, repo, gh);
29252
+ const { relevant } = partitionByRequired(entries, requiredContexts);
29253
+ return derivePollState(relevant);
29161
29254
  }
29162
29255
  async function pollRestPrMergeable(prNumber, repo, gh = defaultGhApi) {
29163
29256
  try {
@@ -29258,6 +29351,37 @@ async function diagnoseFailedRestChecks(prNumber, repo, gh = defaultGhApi) {
29258
29351
  return null;
29259
29352
  }
29260
29353
  }
29354
+ function isNotFoundError2(e) {
29355
+ const msg = `${e?.message ?? e} ${String(e?.stderr ?? "")}`;
29356
+ return /HTTP 404|Not Found|\(404\)/i.test(msg);
29357
+ }
29358
+ async function fetchRequiredCheckContexts(repo, branch, gh = defaultGhApi) {
29359
+ const contexts = /* @__PURE__ */ new Set();
29360
+ try {
29361
+ const raw = await gh([`repos/${repo}/branches/${encodeURIComponent(branch)}/protection/required_status_checks`]);
29362
+ const parsed = JSON.parse(raw);
29363
+ if (Array.isArray(parsed.contexts)) {
29364
+ for (const c of parsed.contexts) if (typeof c === "string") contexts.add(c);
29365
+ }
29366
+ } catch (e) {
29367
+ if (!isNotFoundError2(e)) return null;
29368
+ }
29369
+ try {
29370
+ const raw = await gh([`repos/${repo}/rules/branches/${encodeURIComponent(branch)}`]);
29371
+ const parsed = JSON.parse(raw);
29372
+ if (Array.isArray(parsed)) {
29373
+ for (const rule of parsed) {
29374
+ if (rule.type !== "required_status_checks") continue;
29375
+ for (const check of rule.parameters?.required_status_checks ?? []) {
29376
+ if (check.context) contexts.add(check.context);
29377
+ }
29378
+ }
29379
+ }
29380
+ } catch (e) {
29381
+ if (!isNotFoundError2(e)) return null;
29382
+ }
29383
+ return contexts;
29384
+ }
29261
29385
  async function fetchRestCorePool(gh = defaultGhApi) {
29262
29386
  try {
29263
29387
  const parsed = JSON.parse(await gh(["rate_limit"]));
@@ -31048,8 +31172,8 @@ var LOOP_PLAYBOOKS = {
31048
31172
  { label: "Read the next board item", command: "mmi-cli board read" },
31049
31173
  { label: "Claim it and create an isolated worktree", command: "mmi-cli worktree create <issue-number> --claim --from origin/development" },
31050
31174
  { label: "Build and test in the touched package", command: "npm test && npm run build" },
31051
- { label: "Publish the branch", command: "git push -u origin HEAD" },
31052
- { label: "Open the development-base PR", command: 'mmi-cli pr create --title "<title>" --body-file PR_BODY.md --base development' },
31175
+ { label: "Publish the branch", command: "git push origin <branch>:<branch>" },
31176
+ { label: "Open the development-base PR", command: 'mmi-cli pr create --title "<title>" --body-file .jerv/PR_BODY.md --base development' },
31053
31177
  { label: "Wait for checks and land to development", command: "mmi-cli pr checks-wait <PR-number> && mmi-cli pr land <PR-number>" },
31054
31178
  { label: "Release only after the gated train is authorized", command: "mmi-cli release --apply" }
31055
31179
  ]
@@ -31066,8 +31190,8 @@ var LOOP_PLAYBOOKS = {
31066
31190
  "ship-pr": {
31067
31191
  title: "Ship PR",
31068
31192
  steps: [
31069
- { label: "Publish the branch", command: "git push -u origin HEAD" },
31070
- { label: "Open the development-base PR", command: 'mmi-cli pr create --title "<title>" --body-file PR_BODY.md --base development' },
31193
+ { label: "Publish the branch", command: "git push origin <branch>:<branch>" },
31194
+ { label: "Open the development-base PR", command: 'mmi-cli pr create --title "<title>" --body-file .jerv/PR_BODY.md --base development' },
31071
31195
  { label: "Wait for CI checks", command: "mmi-cli pr checks-wait <PR-number>" },
31072
31196
  { label: "Land the PR (merge to development)", command: "mmi-cli pr land <PR-number>" }
31073
31197
  ]
@@ -31075,9 +31199,9 @@ var LOOP_PLAYBOOKS = {
31075
31199
  "hotfix": {
31076
31200
  title: "Hotfix",
31077
31201
  steps: [
31078
- { label: "Create the main-base hotfix PR from an already-merged fix", command: "mmi-cli hotfix start --from <pr#|sha>" },
31202
+ { label: "Create the main-base hotfix PR from every already-merged fix this cycle carries", command: "mmi-cli hotfix start --from <pr#|sha>[,<pr#|sha>...]" },
31079
31203
  { label: "Wait for the hotfix PR checks", command: "mmi-cli pr checks-wait <PR-number>" },
31080
- { label: "After the PR is merged, run the gated release", command: "mmi-cli hotfix release <vX.Y.Z> --carries <pr#|sha>" }
31204
+ { label: "After the PR is merged, run the gated release", command: "mmi-cli hotfix release <vX.Y.Z> --carries <pr#|sha>[,<pr#|sha>...]" }
31081
31205
  ]
31082
31206
  }
31083
31207
  };
@@ -36696,10 +36820,11 @@ withExamples(pr.command("create").description("create a PR and print {number,url
36696
36820
  console.log(JSON.stringify(created));
36697
36821
  }), [
36698
36822
  'mmi-cli pr create --title "Add the schema" --body "Closes #2680"',
36699
- 'mmi-cli pr create --title "Add the schema" --body-file PR_BODY.md --draft'
36823
+ 'mmi-cli pr create --title "Add the schema" --body-file .jerv/PR_BODY.md --draft'
36700
36824
  ], [
36701
36825
  "--head and --base default to the current branch and the repo default; only pass them to override.",
36702
- "Use --body-file for multiline PR bodies instead of shell-escaped inline markdown."
36826
+ "Use --body-file for multiline PR bodies instead of shell-escaped inline markdown.",
36827
+ "Write that file under .jerv/ inside a worktree (#4405): any other untracked path makes `worktree land` refuse cleanup as untracked-files."
36703
36828
  ]);
36704
36829
  pr.command("view <number>").description("read a PR as structured JSON (merged state, head/base, URL, merge commit) ? the mmi-cli read path (#2347). --comments folds in every comment; --context also adds linkedIssues (the issues it closes/references, #2894)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--json [fields...]", 'gh --json field list (overrides the default field set). Accepts commas, spaces, or repeated --json flags ? in PowerShell an unquoted comma list is an array literal, so QUOTE it: --json "state,baseRefName,mergeCommit"').option("--comments", "include every comment (body + comments in one call) ? read the whole PR before landing it (#2894)").option("--context", "full working context in one call: implies --comments and also adds linkedIssues (the issues the PR closes/references) (#2894)").action(async (number, o) => {
36705
36830
  const n = Number(number);
@@ -36786,9 +36911,10 @@ pr.command("checks-wait <number>").description(`bounded wait for PR checks; skip
36786
36911
  const snapshot = await fetchRestPrSnapshot(number, repo).catch(() => null);
36787
36912
  const baseBranch = snapshot?.baseRef ?? "development";
36788
36913
  const ciHeadRef = snapshot && !snapshot.headIsFork && snapshot.headRef ? snapshot.headRef : void 0;
36914
+ const requiredContexts = await fetchRequiredCheckContexts(repo, baseBranch).catch(() => null);
36789
36915
  const result = await waitForPrChecks({
36790
36916
  resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef),
36791
- pollChecks: () => pollRestPrChecks(number, repo),
36917
+ pollChecks: () => pollRestPrChecks(number, repo, void 0, requiredContexts),
36792
36918
  pollMergeable: () => pollRestPrMergeable(number, repo),
36793
36919
  pollRateLimit: () => fetchRestCorePool(),
36794
36920
  // #3388: on a confirmed red, read the failing runs' annotations so a wall-clock budget kill stops
@@ -36854,31 +36980,37 @@ pr.command("land <number>").description("agent merge path (#1440): train probe ?
36854
36980
  },
36855
36981
  fetchTrainAuthority: async (repo) => fetchTrainAuthority(repo, registryClientDeps(await loadConfig())),
36856
36982
  resolveCiPolicy: (repo) => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
36857
- waitForChecks: (prNumber, repo) => waitForPrChecks({
36858
- resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
36859
- // #3024: REST-only wait loop ? runPrLand's resolveRepo already guarantees `repo` is set.
36860
- pollChecks: () => pollRestPrChecks(prNumber, repo),
36861
- // #2970: `pr land` only ever lands PRs based on development (resolveRepo above already rejects any
36862
- // other base), so the fast-fail message's base branch is always 'development' here.
36863
- pollMergeable: () => pollRestPrMergeable(prNumber, repo),
36864
- pollRateLimit: () => fetchRestCorePool(),
36865
- // #3388: `pr land` is the batch path ? the one most likely to self-DOS the shared runner and
36866
- // then read its own wall-clock kill as a broken diff.
36867
- diagnoseFailure: () => diagnoseFailedRestChecks(prNumber, repo),
36868
- baseBranch: "development",
36869
- sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
36870
- log: (message) => console.warn(message),
36871
- // `pr land` inherits the same (raised) checks budget, so it needs the same liveness ? otherwise the
36872
- // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
36873
- progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr land: waiting on checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
36874
- }),
36983
+ waitForChecks: async (prNumber, repo) => {
36984
+ const requiredContexts = await fetchRequiredCheckContexts(repo, "development").catch(() => null);
36985
+ return waitForPrChecks({
36986
+ resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
36987
+ // #3024: REST-only wait loop ? runPrLand's resolveRepo already guarantees `repo` is set.
36988
+ pollChecks: () => pollRestPrChecks(prNumber, repo, void 0, requiredContexts),
36989
+ // #2970: `pr land` only ever lands PRs based on development (resolveRepo above already rejects any
36990
+ // other base), so the fast-fail message's base branch is always 'development' here.
36991
+ pollMergeable: () => pollRestPrMergeable(prNumber, repo),
36992
+ pollRateLimit: () => fetchRestCorePool(),
36993
+ // #3388: `pr land` is the batch path ? the one most likely to self-DOS the shared runner and
36994
+ // then read its own wall-clock kill as a broken diff.
36995
+ diagnoseFailure: () => diagnoseFailedRestChecks(prNumber, repo),
36996
+ baseBranch: "development",
36997
+ sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms)),
36998
+ log: (message) => console.warn(message),
36999
+ // `pr land` inherits the same (raised) checks budget, so it needs the same liveness ? otherwise the
37000
+ // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
37001
+ progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr land: waiting on checks ? ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
37002
+ });
37003
+ },
36875
37004
  // #2425: re-probe used ONLY to decide whether a failed `gh pr merge --auto` is worth retrying ? a
36876
37005
  // fresh read of state/mergeable/checks, independent of the merge call's own (possibly stale) error.
37006
+ // #4396: same required-status scoping as the wait loop above ? a non-required red must not read
37007
+ // as "not ready to retry" any more than it should have blocked the wait itself.
36877
37008
  probeMergeReady: async (prNumber, repo) => {
36878
- const [snapshot, checks] = await Promise.all([
37009
+ const [snapshot, requiredContexts] = await Promise.all([
36879
37010
  fetchRestPrSnapshot(prNumber, repo).catch(() => null),
36880
- pollRestPrChecks(prNumber, repo).catch(() => "error")
37011
+ fetchRequiredCheckContexts(repo, "development").catch(() => null)
36881
37012
  ]);
37013
+ const checks = await pollRestPrChecks(prNumber, repo, void 0, requiredContexts).catch(() => "error");
36882
37014
  return {
36883
37015
  open: snapshot?.state === "open",
36884
37016
  mergeable: snapshot?.mergeable === "MERGEABLE",
@@ -36961,9 +37093,10 @@ jsonParity(pr.command("merge <number>").description("merge a PR (squash by defau
36961
37093
  if (o.wait) {
36962
37094
  const repo = await requireRepo(o.repo);
36963
37095
  const baseBranch = await fetchRestPrSnapshot(number, repo).then((s) => s.baseRef).catch(() => "development");
37096
+ const requiredContexts = await fetchRequiredCheckContexts(repo, baseBranch).catch(() => null);
36964
37097
  const wait = await waitForPrChecks({
36965
37098
  resolvePolicy: () => resolveMergeCiPolicyForCheckout(o.repo, ciHeadRef),
36966
- pollChecks: () => pollRestPrChecks(number, repo),
37099
+ pollChecks: () => pollRestPrChecks(number, repo, void 0, requiredContexts),
36967
37100
  pollMergeable: () => pollRestPrMergeable(number, repo),
36968
37101
  pollRateLimit: () => fetchRestCorePool(),
36969
37102
  diagnoseFailure: () => diagnoseFailedRestChecks(number, repo),
@@ -37456,7 +37589,8 @@ function renderHotfixRelease(r) {
37456
37589
  ...r.runs.map((run) => ` - ${run.workflow}: ${run.conclusion}${run.url ? ` (${run.url})` : ""}`),
37457
37590
  ` - ${r.verifyNote}`,
37458
37591
  ...r.announceNote ? [` - announce: ${r.announceNote}`] : [],
37459
- ` - next: mmi-cli hotfix status ${r.tag} (no back-merge ? development already has the fix; the next /release back-merge aligns the version manifests)`
37592
+ ` - fold: ${r.foldNote}`,
37593
+ ` - next: mmi-cli hotfix status ${r.tag} (no back-merge of the FIX ? development already has it; the version fold above is ported for you, #4410)`
37460
37594
  ].join("\n");
37461
37595
  }
37462
37596
  function renderHotfixStatus(r) {
@@ -37488,7 +37622,7 @@ var hotfixCmd = program2.command("hotfix").description("stepwise hotfix orchestr
37488
37622
  const steps = trainPlan("hotfix");
37489
37623
  console.log(o.json ? JSON.stringify({ command: "hotfix", steps }, null, 2) : renderSteps("mmi-cli hotfix: dry-run plan", steps));
37490
37624
  });
37491
- hotfixCmd.command("start").description("cherry-pick a merged development PR (or SHA) onto hotfix/vX.Y.Z from origin/main, bump the distribution, open the main-base PR").requiredOption("--from <pr#|sha>", "merged development PR number or commit SHA to cherry-pick").option("--json", "machine-readable output").action(async (o) => runHotfixSub("start", () => runHotfixStart(trainApplyDeps(), { from: o.from }), o.json, renderHotfixStart));
37625
+ hotfixCmd.command("start").description("cherry-pick one or more merged development PRs (or SHAs) onto hotfix/vX.Y.Z from origin/main, bump the distribution, open the main-base PR").requiredOption("--from <pr#|sha[,pr#|sha...]>", "merged development PR number(s) or commit SHA(s) to cherry-pick, in pick order \u2014 one hotfix cycle carries as many fixes as you name (#4411)").option("--json", "machine-readable output").action(async (o) => runHotfixSub("start", () => runHotfixStart(trainApplyDeps(), { from: o.from }), o.json, renderHotfixStart));
37492
37626
  hotfixCmd.command("release <version>").description("after the hotfix PR is merged + checks green: tag, GitHub Release, watch deploy/publish, verify distribution (idempotent)").option("--json", "machine-readable output").option("--announce-summary-file <path>", "agent-curated summary lines for the Hub Slack announcement (#883)").option("--carries <pr#|sha[,pr#|sha...]>", "declared fix target(s) this hotfix must carry; each must be proven present before tagging (#3056)").action(async (version, o) => runHotfixSub("release", () => runHotfixRelease(trainApplyDeps(), version, { announceSummaryFile: o.announceSummaryFile, carries: o.carries ? [o.carries] : [] }), o.json, renderHotfixRelease));
37493
37627
  hotfixCmd.command("status [version]").description("derive the full hotfix pipeline state from live git/gh reads and name the exact next subcommand").option("--json", "machine-readable output").action(async (version, o) => runHotfixSub("status", () => runHotfixStatus(trainApplyDeps(), version), o.json, renderHotfixStatus));
37494
37628
  var ci = program2.command("ci").description("org CI + merge-readiness audit and reconcile");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.105.6",
3
+ "version": "3.105.8",
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",