@biffo/cli 0.301.2 → 0.301.5

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.js +36 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1115,6 +1115,25 @@ var GitAdapter = class {
1115
1115
  const { stdout } = await execa("git", ["status", "--porcelain"], { cwd });
1116
1116
  return stdout.trim().length > 0;
1117
1117
  }
1118
+ /**
1119
+ * True if `worktreePath` is currently held by biffo-fleet's own
1120
+ * `.fleet-worktree-claim` lock (#1833, replaces #1825) — a live session
1121
+ * may hold a worktree open for follow-up work after its PR has already
1122
+ * merged, which is invisible to every other check `doctor --fix` applies
1123
+ * (clean, attached, merged, HEAD contained in what shipped all pass while
1124
+ * this is true).
1125
+ *
1126
+ * Deliberately a plain filesystem existence check, not a shell-out to
1127
+ * `bin/fleet.sh worktree-claim --check` — that verb is not read-only (it
1128
+ * `mkdir`s the lock and only reports "held" on failure), so calling it from
1129
+ * here to merely ask would risk *creating* a claim on a free worktree
1130
+ * rather than answering the question. The lock directory's existence is a
1131
+ * filesystem fact this repo is allowed to read without touching
1132
+ * biffo-fleet's own source (#1833's own routing note).
1133
+ */
1134
+ async hasFleetWorktreeClaim(worktreePath) {
1135
+ return existsSync6(join6(worktreePath, ".fleet-worktree-claim"));
1136
+ }
1118
1137
  /** Best-effort fetch of the tracking remote, so the ahead/behind check below
1119
1138
  * compares against reality rather than a stale local ref (#394). Never throws:
1120
1139
  * offline or a missing remote must not block an upgrade on its own — the
@@ -14992,6 +15011,7 @@ function runDoctorChecks(facts) {
14992
15011
 
14993
15012
  // src/lib/doctor-reaper.ts
14994
15013
  function classifyReapCandidate(facts) {
15014
+ if (facts.hasFleetClaim) return { action: "keep", reason: "fleet-worktree-claimed" };
14995
15015
  if (facts.isDetached) return { action: "keep", reason: "detached-head" };
14996
15016
  if (facts.isDirty) return { action: "keep", reason: "uncommitted-changes" };
14997
15017
  switch (facts.prVerdict) {
@@ -15017,12 +15037,13 @@ function findReapCandidates(branches, worktrees) {
15017
15037
  }
15018
15038
  async function reapCandidate(cwd, candidate, deps) {
15019
15039
  const { git, github } = deps;
15020
- const [current, isDirty] = await Promise.all([
15040
+ const [current, isDirty, hasFleetClaim] = await Promise.all([
15021
15041
  git.currentBranch(candidate.worktreePath),
15022
- git.hasUncommittedChanges(candidate.worktreePath)
15042
+ git.hasUncommittedChanges(candidate.worktreePath),
15043
+ git.hasFleetWorktreeClaim(candidate.worktreePath)
15023
15044
  ]);
15024
15045
  const isDetached = current === "HEAD" || current === "";
15025
- const prVerdict = isDetached || isDirty ? "unknown" : await github.prVerdictForBranch(cwd, candidate.branch);
15046
+ const prVerdict = isDetached || isDirty || hasFleetClaim ? "unknown" : await github.prVerdictForBranch(cwd, candidate.branch);
15026
15047
  let mergeContainsHead = true;
15027
15048
  if (prVerdict === "merged") {
15028
15049
  const [headSha, mergedHeadSha] = await Promise.all([
@@ -15031,7 +15052,13 @@ async function reapCandidate(cwd, candidate, deps) {
15031
15052
  ]);
15032
15053
  mergeContainsHead = headSha === null || mergedHeadSha === null ? null : await git.isAncestor(cwd, headSha, mergedHeadSha);
15033
15054
  }
15034
- const verdict = classifyReapCandidate({ isDetached, isDirty, prVerdict, mergeContainsHead });
15055
+ const verdict = classifyReapCandidate({
15056
+ isDetached,
15057
+ isDirty,
15058
+ hasFleetClaim,
15059
+ prVerdict,
15060
+ mergeContainsHead
15061
+ });
15035
15062
  if (verdict.action === "keep") {
15036
15063
  return { candidate, verdict, worktreeRemoved: null };
15037
15064
  }
@@ -15066,6 +15093,10 @@ async function reapBareBranch(cwd, candidate, deps) {
15066
15093
  const verdict = classifyReapCandidate({
15067
15094
  isDetached: false,
15068
15095
  isDirty: false,
15096
+ // A bare branch has no worktree, so there is no `.fleet-worktree-claim`
15097
+ // directory that could exist — trivially false, same reasoning as the
15098
+ // two fields above (#1833).
15099
+ hasFleetClaim: false,
15069
15100
  prVerdict,
15070
15101
  mergeContainsHead
15071
15102
  });
@@ -15216,6 +15247,7 @@ function printFindings(findings) {
15216
15247
  var KEEP_REASON_TEXT = {
15217
15248
  "detached-head": "worktree HEAD is detached",
15218
15249
  "uncommitted-changes": "worktree has uncommitted changes",
15250
+ "fleet-worktree-claimed": "worktree is held by a live biffo-fleet worktree-claim lock",
15219
15251
  "pr-open": "PR is still open",
15220
15252
  "pr-closed": "PR closed unmerged",
15221
15253
  "no-pr": "no PR was ever opened from this branch",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.301.2",
3
+ "version": "0.301.5",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",