@mutmutco/cli 3.105.7 → 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 +92 -17
  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
  }
@@ -31099,8 +31172,8 @@ var LOOP_PLAYBOOKS = {
31099
31172
  { label: "Read the next board item", command: "mmi-cli board read" },
31100
31173
  { label: "Claim it and create an isolated worktree", command: "mmi-cli worktree create <issue-number> --claim --from origin/development" },
31101
31174
  { label: "Build and test in the touched package", command: "npm test && npm run build" },
31102
- { label: "Publish the branch", command: "git push -u origin HEAD" },
31103
- { 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' },
31104
31177
  { label: "Wait for checks and land to development", command: "mmi-cli pr checks-wait <PR-number> && mmi-cli pr land <PR-number>" },
31105
31178
  { label: "Release only after the gated train is authorized", command: "mmi-cli release --apply" }
31106
31179
  ]
@@ -31117,8 +31190,8 @@ var LOOP_PLAYBOOKS = {
31117
31190
  "ship-pr": {
31118
31191
  title: "Ship PR",
31119
31192
  steps: [
31120
- { label: "Publish the branch", command: "git push -u origin HEAD" },
31121
- { 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' },
31122
31195
  { label: "Wait for CI checks", command: "mmi-cli pr checks-wait <PR-number>" },
31123
31196
  { label: "Land the PR (merge to development)", command: "mmi-cli pr land <PR-number>" }
31124
31197
  ]
@@ -31126,9 +31199,9 @@ var LOOP_PLAYBOOKS = {
31126
31199
  "hotfix": {
31127
31200
  title: "Hotfix",
31128
31201
  steps: [
31129
- { 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>...]" },
31130
31203
  { label: "Wait for the hotfix PR checks", command: "mmi-cli pr checks-wait <PR-number>" },
31131
- { 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>...]" }
31132
31205
  ]
31133
31206
  }
31134
31207
  };
@@ -36747,10 +36820,11 @@ withExamples(pr.command("create").description("create a PR and print {number,url
36747
36820
  console.log(JSON.stringify(created));
36748
36821
  }), [
36749
36822
  'mmi-cli pr create --title "Add the schema" --body "Closes #2680"',
36750
- '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'
36751
36824
  ], [
36752
36825
  "--head and --base default to the current branch and the repo default; only pass them to override.",
36753
- "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."
36754
36828
  ]);
36755
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) => {
36756
36830
  const n = Number(number);
@@ -37515,7 +37589,8 @@ function renderHotfixRelease(r) {
37515
37589
  ...r.runs.map((run) => ` - ${run.workflow}: ${run.conclusion}${run.url ? ` (${run.url})` : ""}`),
37516
37590
  ` - ${r.verifyNote}`,
37517
37591
  ...r.announceNote ? [` - announce: ${r.announceNote}`] : [],
37518
- ` - 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)`
37519
37594
  ].join("\n");
37520
37595
  }
37521
37596
  function renderHotfixStatus(r) {
@@ -37547,7 +37622,7 @@ var hotfixCmd = program2.command("hotfix").description("stepwise hotfix orchestr
37547
37622
  const steps = trainPlan("hotfix");
37548
37623
  console.log(o.json ? JSON.stringify({ command: "hotfix", steps }, null, 2) : renderSteps("mmi-cli hotfix: dry-run plan", steps));
37549
37624
  });
37550
- 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));
37551
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));
37552
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));
37553
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.7",
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",