@mutmutco/cli 3.111.0 → 3.112.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 +51 -14
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -7686,14 +7686,27 @@ function validateDeadWorktreeDirCleanup(planned, inspected) {
7686
7686
  }
7687
7687
  return { ok: true };
7688
7688
  }
7689
- function validateDeadWorktreeDirContent(planned, inspected) {
7689
+ function describeLeftoverEntries(entries) {
7690
+ const shown = entries.slice(0, 8);
7691
+ const more = entries.length > shown.length ? `, +${entries.length - shown.length} more` : "";
7692
+ return `${shown.join(", ")}${more}`;
7693
+ }
7694
+ function validateDeadWorktreeDirContent(planned, inspected, options = {}) {
7690
7695
  if (!inspected.entries) return { ok: false, reason: inspected.error ?? "unable to inspect directory contents" };
7691
7696
  const entries = inspected.entries.filter(Boolean);
7692
7697
  if (planned.reason === "orphaned-folder") {
7693
- return entries.length === 0 ? { ok: true } : { ok: false, reason: "orphaned worktree directory still contains files" };
7698
+ if (entries.length === 0 || options.force) return { ok: true };
7699
+ return {
7700
+ ok: false,
7701
+ reason: `orphaned worktree directory still contains files: ${describeLeftoverEntries(entries)} \u2014 re-run with --force to remove anyway`
7702
+ };
7694
7703
  }
7695
7704
  if (planned.reason === "dangling-gitdir") {
7696
- return entries.length === 1 && entries[0] === ".git" ? { ok: true } : { ok: false, reason: "dangling worktree directory still contains files" };
7705
+ if (entries.length === 1 && entries[0] === ".git" || options.force) return { ok: true };
7706
+ return {
7707
+ ok: false,
7708
+ reason: `dangling worktree directory (git registration already severed) still contains files: ${describeLeftoverEntries(entries)} \u2014 re-run with --force to remove anyway`
7709
+ };
7697
7710
  }
7698
7711
  return { ok: false, reason: "unknown dead worktree cleanup kind" };
7699
7712
  }
@@ -13252,7 +13265,10 @@ async function fetchTrainAuthority(repo, deps) {
13252
13265
  headers: { Authorization: `Bearer ${token}` }
13253
13266
  });
13254
13267
  if (res.status === 426) return { ok: false, error: upgradeRequiredError(res, await res.json().catch(() => null)) };
13255
- if (!res.ok) return { ok: false, error: `train-authority HTTP ${res.status}` };
13268
+ if (!res.ok) {
13269
+ const detail = (await res.json().catch(() => null))?.error;
13270
+ return { ok: false, error: detail ? `train-authority HTTP ${res.status}: ${detail}` : `train-authority HTTP ${res.status}` };
13271
+ }
13256
13272
  const body = await res.json();
13257
13273
  if (typeof body?.train !== "boolean" || !body.role) return { ok: false, error: "malformed train-authority response" };
13258
13274
  return { ok: true, authority: body };
@@ -15618,10 +15634,10 @@ var rollout_plan_default = {
15618
15634
  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)."
15619
15635
  },
15620
15636
  baseline: {
15621
- version: "3.111.0",
15622
- tag: "v3.111.0",
15623
- commit: "ef48be2432be",
15624
- npm: "@mutmutco/cli@3.111.0"
15637
+ version: "3.112.0",
15638
+ tag: "v3.112.0",
15639
+ commit: "e6eaa0103b25",
15640
+ npm: "@mutmutco/cli@3.112.0"
15625
15641
  },
15626
15642
  exitCriterion: "fleet-n-of-n",
15627
15643
  hubOnlyShortcut: "forbidden",
@@ -15638,14 +15654,14 @@ var rollout_plan_default = {
15638
15654
  repo: "mutmutco/mmi-hub",
15639
15655
  role: "canary",
15640
15656
  schedule: "train",
15641
- v3Target: "v3.111.0"
15657
+ v3Target: "v3.112.0"
15642
15658
  }
15643
15659
  ],
15644
15660
  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.",
15645
15661
  rollback: {
15646
15662
  independent: true,
15647
- mechanism: "npm dist-tag latest -> 3.111.0 and redeploy the Hub Lambda from tag v3.111.0 (ef48be2432be); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15648
- v3Target: "v3.111.0 (@mutmutco/cli@3.111.0, tag commit ef48be2432be \u2014 the preserved latest-v3 distribution, D6b)"
15663
+ mechanism: "npm dist-tag latest -> 3.112.0 and redeploy the Hub Lambda from tag v3.112.0 (e6eaa0103b25); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15664
+ v3Target: "v3.112.0 (@mutmutco/cli@3.112.0, tag commit e6eaa0103b25 \u2014 the preserved latest-v3 distribution, D6b)"
15649
15665
  }
15650
15666
  },
15651
15667
  {
@@ -32802,7 +32818,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
32802
32818
  result.failed.push(`${wt.path}: skipped dead-dir removal (${stillValid.reason})`);
32803
32819
  continue;
32804
32820
  }
32805
- const contentSafe = validateDeadWorktreeDirContent(wt, inspectDeadWorktreeDirContent(wt.path));
32821
+ const contentSafe = validateDeadWorktreeDirContent(wt, inspectDeadWorktreeDirContent(wt.path), { force: opts.force });
32806
32822
  if (!contentSafe.ok) {
32807
32823
  result.failed.push(`${wt.path}: skipped dead-dir removal (${contentSafe.reason})`);
32808
32824
  continue;
@@ -33780,6 +33796,17 @@ function landSteps(branch, hasWorktree, hasStage) {
33780
33796
  steps.push("prune worktree metadata");
33781
33797
  return steps;
33782
33798
  }
33799
+ function describeLandUnresolvableWorktree(inspection) {
33800
+ if (inspection.gitType === "missing") {
33801
+ return "this directory has no .git at all \u2014 its worktree registration is gone";
33802
+ }
33803
+ if (inspection.gitType === "file" && inspection.gitDirExists === false) {
33804
+ const match = /^gitdir:\s*(.+)\s*$/im.exec(inspection.gitFileContent ?? "");
33805
+ const gitdir = match?.[1]?.trim();
33806
+ return `this directory's .git points at ${gitdir ? `'${gitdir}'` : "its admin dir"}, which no longer exists \u2014 its worktree registration was severed`;
33807
+ }
33808
+ return void 0;
33809
+ }
33783
33810
  function orphanLandSteps(lostBranch, hasStage) {
33784
33811
  const steps = [];
33785
33812
  if (hasStage) steps.push("stop spawned dev stage");
@@ -34012,7 +34039,17 @@ function registerWorktreeCommands(program3) {
34012
34039
  if (o.apply && o.dryRun) return fail("worktree land: choose either --dry-run or --apply");
34013
34040
  const apply = Boolean(o.apply);
34014
34041
  try {
34015
- const wtPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
34042
+ let wtPath;
34043
+ try {
34044
+ wtPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
34045
+ } catch (e) {
34046
+ const reason = describeLandUnresolvableWorktree(inspectSiblingWorktreeDir(process.cwd(), ""));
34047
+ if (!reason) throw e;
34048
+ return fail(
34049
+ `worktree land: ${reason}. It cannot be landed from inside itself \u2014 cd to the primary checkout and run \`mmi-cli worktree gc --apply\` (add --force if this directory still holds leftover files) to clear it.`,
34050
+ { code: ERROR_CODES.ERR_BAD_ENUM }
34051
+ );
34052
+ }
34016
34053
  const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
34017
34054
  const symbolicBranch = (await execFileP2(
34018
34055
  "git",
@@ -39759,7 +39796,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
39759
39796
  process.exit(process.exitCode ?? 0);
39760
39797
  });
39761
39798
  });
39762
- gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH ? an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
39799
+ gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH ? an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
39763
39800
  if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
39764
39801
  if (o.scratch) {
39765
39802
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.111.0",
3
+ "version": "3.112.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",