@mutmutco/cli 2.5.0 → 2.5.1

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/index.cjs +40 -9
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -4255,6 +4255,26 @@ function parseWorktreePorcelain(stdout) {
4255
4255
  }
4256
4256
  return out;
4257
4257
  }
4258
+ function samePath(a, b) {
4259
+ return a.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase() === b.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
4260
+ }
4261
+ function selectPrMergeCleanupWorktree(branch, before, after, startingPath) {
4262
+ if (!branch) return void 0;
4263
+ const current = after.find((w) => w.branch === branch)?.path;
4264
+ if (current) return current;
4265
+ const previous = before.find((w) => w.branch === branch)?.path;
4266
+ if (previous) return previous;
4267
+ if (startingPath && before.some((w) => w.branch === branch && samePath(w.path, startingPath))) return startingPath;
4268
+ return void 0;
4269
+ }
4270
+ function selectSafeWorktreeCwd(worktrees, targetPath) {
4271
+ if (!targetPath) return void 0;
4272
+ return worktrees.find((w) => !samePath(w.path, targetPath))?.path;
4273
+ }
4274
+ function branchMissingFromList(branch, stdout) {
4275
+ const names = stdout.split(/\r?\n/).map((line) => line.replace(/^\*\s*/, "").trim()).filter(Boolean);
4276
+ return !names.includes(branch);
4277
+ }
4258
4278
  function formatGcPlan(plan2, apply) {
4259
4279
  const lines = [`mmi-cli gc: ${apply ? "apply" : "dry-run"}`];
4260
4280
  if (!plan2.branches.length && !plan2.trackingRefs.length) lines.push("nothing to clean");
@@ -6312,24 +6332,31 @@ async function applyGcPlan(plan2, remote) {
6312
6332
  await execFileP3("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS });
6313
6333
  }
6314
6334
  }
6315
- async function cleanupLocalBranch(branch) {
6335
+ async function cleanupLocalBranch(branch, before = {}) {
6316
6336
  const result = { branchDeleted: false };
6317
6337
  if (!branch) return result;
6318
6338
  const { stdout } = await execFileP3("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS });
6319
- const wt = parseWorktreePorcelain(stdout).find((w) => w.branch === branch);
6320
- if (wt) {
6321
- await execFileP3("git", ["worktree", "remove", "--force", wt.path], { timeout: GIT_TIMEOUT_MS }).catch(() => {
6339
+ const afterWorktrees = parseWorktreePorcelain(stdout);
6340
+ const wtPath = selectPrMergeCleanupWorktree(branch, before.worktrees ?? [], afterWorktrees, before.startingPath);
6341
+ const safeCwd = selectSafeWorktreeCwd([...afterWorktrees, ...before.worktrees ?? []], wtPath);
6342
+ const git = (args) => safeCwd ? execFileP3("git", ["-C", safeCwd, ...args], { timeout: GIT_TIMEOUT_MS }) : execFileP3("git", args, { timeout: GIT_TIMEOUT_MS });
6343
+ if (wtPath) {
6344
+ await git(["worktree", "remove", "--force", wtPath]).catch(() => {
6322
6345
  });
6323
- result.worktreeRemoved = wt.path;
6346
+ result.worktreeRemoved = wtPath;
6324
6347
  }
6325
- const current = await gitOut(["rev-parse", "--abbrev-ref", "HEAD"]) || "";
6348
+ const current = safeCwd ? ((await git(["rev-parse", "--abbrev-ref", "HEAD"]).catch(() => ({ stdout: "" }))).stdout || "").trim() : await gitOut(["rev-parse", "--abbrev-ref", "HEAD"]) || "";
6326
6349
  if (branch !== current) {
6327
- await execFileP3("git", ["branch", "-D", branch], { timeout: GIT_TIMEOUT_MS }).then(() => {
6350
+ await git(["branch", "-D", branch]).then(() => {
6328
6351
  result.branchDeleted = true;
6329
6352
  }).catch(() => {
6330
6353
  });
6354
+ if (!result.branchDeleted) {
6355
+ const remaining = await git(["branch", "--list", branch]).catch(() => ({ stdout: "" }));
6356
+ result.branchDeleted = branchMissingFromList(branch, remaining.stdout || "");
6357
+ }
6331
6358
  }
6332
- if (wt) await execFileP3("git", ["worktree", "prune"], { timeout: GIT_TIMEOUT_MS }).catch(() => {
6359
+ if (wtPath) await git(["worktree", "prune"]).catch(() => {
6333
6360
  });
6334
6361
  return result;
6335
6362
  }
@@ -7015,10 +7042,14 @@ pr.command("merge <number>").description("merge a PR (squash by default) and cle
7015
7042
  const method = o.rebase ? "--rebase" : o.merge ? "--merge" : "--squash";
7016
7043
  const repoArgs = o.repo ? ["--repo", o.repo] : [];
7017
7044
  const headRef = (await execFileP3("gh", ["pr", "view", number, ...repoArgs, "--json", "headRefName", "--jq", ".headRefName"], { timeout: GC_GH_TIMEOUT_MS })).stdout.trim();
7045
+ const startingPath = (await execFileP3("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout.trim();
7046
+ const beforeWorktrees = parseWorktreePorcelain(
7047
+ (await execFileP3("git", ["worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).catch(() => ({ stdout: "" }))).stdout
7048
+ );
7018
7049
  await execFileP3("gh", ["pr", "merge", number, ...repoArgs, method, "--delete-branch"], { timeout: GC_GH_TIMEOUT_MS }).catch((e) => {
7019
7050
  if (!/used by worktree|cannot delete branch|already been merged/i.test(String(e.message || ""))) throw e;
7020
7051
  });
7021
- const cleaned = repoArgs.length ? { branchDeleted: false } : await cleanupLocalBranch(headRef);
7052
+ const cleaned = repoArgs.length ? { branchDeleted: false } : await cleanupLocalBranch(headRef, { worktrees: beforeWorktrees, startingPath });
7022
7053
  console.log(JSON.stringify({ merged: number, branch: headRef, method: method.slice(2), ...cleaned }));
7023
7054
  });
7024
7055
  async function runBoardRead(o) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
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",