Skip to content

Commit bb1b27a

Browse files
jwaldripclaude
andcommitted
feat(cleanup): detect and remove merged worktrees
Enhances /cleanup to identify active worktrees whose branch has already been merged into the default branch, and offers to remove them. Previously only orphaned worktrees (stale git entries) were detected — merged-but-lingering worktrees were invisible to cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c0349b7 commit bb1b27a

File tree

1 file changed

+64
-13
lines changed

1 file changed

+64
-13
lines changed

plugin/skills/cleanup/SKILL.md

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
---
2-
description: Remove orphaned AI-DLC worktrees
2+
description: Remove orphaned and merged AI-DLC worktrees
33
disable-model-invocation: true
44
---
55

66
## Name
77

8-
`ai-dlc:cleanup` - Remove orphaned AI-DLC worktrees.
8+
`ai-dlc:cleanup` - Remove orphaned and merged AI-DLC worktrees.
99

1010
## Synopsis
1111

@@ -17,12 +17,12 @@ disable-model-invocation: true
1717

1818
**User-facing command** - Run this to clean up stale worktrees left behind by interrupted sessions.
1919

20-
Scans `.ai-dlc/worktrees/` for worktree directories and removes any that are orphaned (the backing git worktree entry is stale or the directory is left over from a crashed session).
20+
Scans `.ai-dlc/worktrees/` for worktree directories and removes any that are orphaned (the backing git worktree entry is stale or the directory is left over from a crashed session) or merged (the worktree's branch has already been merged into the default branch).
2121

2222
This does not:
2323
- Clear AI-DLC state (use `/reset` for that)
24-
- Delete branches or commits
25-
- Affect active worktrees with running sessions
24+
- Delete unmerged branches or commits
25+
- Affect active worktrees whose branches have not been merged
2626

2727
## Implementation
2828

@@ -57,20 +57,48 @@ if [ -z "$DIRS" ]; then
5757
fi
5858
```
5959

60-
### Step 2: Identify Orphaned Worktrees
60+
### Step 2: Identify Orphaned, Merged, and Active Worktrees
6161

6262
```bash
6363
# Get list of valid worktree paths from git
6464
VALID_WORKTREES=$(git worktree list --porcelain | grep '^worktree ' | sed 's/^worktree //')
6565

66+
# Determine the default branch
67+
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
68+
if [ -z "$DEFAULT_BRANCH" ]; then
69+
# Fallback: try common names
70+
for candidate in main master; do
71+
if git rev-parse --verify "refs/heads/$candidate" &>/dev/null; then
72+
DEFAULT_BRANCH="$candidate"
73+
break
74+
fi
75+
done
76+
fi
77+
78+
# Get the list of branches already merged into the default branch
79+
MERGED_BRANCHES=$(git branch --merged "$DEFAULT_BRANCH" 2>/dev/null | sed 's/^[* ]*//')
80+
6681
ORPHANED=()
82+
MERGED=()
83+
MERGED_BRANCHES_MAP=() # parallel array: branch name for each merged entry
6784
ACTIVE=()
6885

6986
for dir in $DIRS; do
87+
slug=$(basename "$dir")
7088
if echo "$VALID_WORKTREES" | grep -qF "$dir"; then
71-
ACTIVE+=("$(basename "$dir")")
89+
# Active worktree — check if its branch is merged
90+
BRANCH=$(git worktree list --porcelain | awk -v path="$dir" '
91+
/^worktree / { wt=$0; sub(/^worktree /,"",wt) }
92+
/^branch / { if (wt == path) { sub(/^branch refs\/heads\//,""); print; exit } }
93+
')
94+
if [ -n "$BRANCH" ] && echo "$MERGED_BRANCHES" | grep -qxF "$BRANCH"; then
95+
MERGED+=("$slug")
96+
MERGED_BRANCHES_MAP+=("$BRANCH")
97+
else
98+
ACTIVE+=("$slug")
99+
fi
72100
else
73-
ORPHANED+=("$(basename "$dir")")
101+
ORPHANED+=("$slug")
74102
fi
75103
done
76104
```
@@ -82,21 +110,26 @@ Show the user what was found:
82110
```
83111
## AI-DLC Worktree Cleanup
84112
85-
**Active worktrees:** {count}
86-
{list of active worktree names, if any}
87-
88113
**Orphaned worktrees:** {count}
89114
{list of orphaned worktree names, if any}
115+
116+
**Merged worktrees (safe to remove):** {count}
117+
{list of merged worktree names with branch names, e.g. "slug (ai-dlc/slug/main)"}
118+
119+
**Active worktrees:** {count}
120+
{list of active worktree names, if any}
90121
```
91122

92123
If there are orphaned worktrees, ask the user to confirm removal using `AskUserQuestion`.
93124

94-
If there are no orphaned entries but there are active worktrees, ask whether to force-remove all worktrees (with a warning that this will interrupt any running sessions).
125+
If there are merged worktrees, ask the user (separately from orphaned confirmation) whether to remove them using `AskUserQuestion`. Explain that their branches are already merged into the default branch so removal is safe.
126+
127+
If there are no orphaned or merged entries but there are active worktrees, ask whether to force-remove all worktrees (with a warning that this will interrupt any running sessions).
95128

96129
If there is nothing to clean up, output:
97130

98131
```
99-
No orphaned worktrees found. Everything is clean.
132+
No orphaned or merged worktrees found. Everything is clean.
100133
```
101134

102135
### Step 4: Remove
@@ -109,6 +142,23 @@ for name in "${ORPHANED[@]}"; do
109142
rm -rf "${REPO_ROOT}/.ai-dlc/worktrees/${name}"
110143
done
111144

145+
# Remove merged worktrees (if user confirmed)
146+
for i in "${!MERGED[@]}"; do
147+
name="${MERGED[$i]}"
148+
branch="${MERGED_BRANCHES_MAP[$i]}"
149+
# Remove the worktree (no --force needed since branch is merged)
150+
git worktree remove "${REPO_ROOT}/.ai-dlc/worktrees/${name}" 2>/dev/null
151+
# Delete the merged branch (safe — git -d refuses if not merged)
152+
git branch -d "$branch" 2>/dev/null
153+
# Clean up the intent spec directory if it exists
154+
if [ -d "${REPO_ROOT}/.ai-dlc/${name}" ]; then
155+
# Ask user before removing spec directory
156+
# Use AskUserQuestion: "Remove spec directory .ai-dlc/${name}/?"
157+
# If confirmed:
158+
rm -rf "${REPO_ROOT}/.ai-dlc/${name}"
159+
fi
160+
done
161+
112162
# If user chose to force-remove active worktrees too:
113163
for name in "${FORCE_REMOVE[@]}"; do
114164
git worktree remove --force "${REPO_ROOT}/.ai-dlc/worktrees/${name}" 2>/dev/null
@@ -125,4 +175,5 @@ Output:
125175
Cleanup complete.
126176
127177
Removed {count} orphaned worktree(s).
178+
Removed {count} merged worktree(s) and their branches.
128179
```

0 commit comments

Comments
 (0)