Skip to content

Instantly share code, notes, and snippets.

@andynu
Last active February 4, 2026 19:25
Show Gist options
  • Select an option

  • Save andynu/13e362f7a5e69a9f083e7bca9f83f60a to your computer and use it in GitHub Desktop.

Select an option

Save andynu/13e362f7a5e69a9f083e7bca9f83f60a to your computer and use it in GitHub Desktop.
Multi-agent Claude Code workflow using tmux for parallel sessions

Multi-Agent Claude Code Workflow

A lightweight pattern for spawning secondary Claude Code sessions using tmux, enabling parallel work across projects without context collision.

Why Spawn Secondary Sessions?

When working from a "mission control" directory (like ~/work/src/ with 100+ projects), you often need to:

  • Dispatch deep work to a specific project while continuing meta-level coordination
  • Run parallel investigations that would pollute a single session's context
  • Isolate risky refactors where you might abandon the branch
  • Review someone's PR without disrupting your current work

The key insight: a single Claude session has one context. Spawning secondary sessions gives each task its own isolated context and working directory.

The Tools

tmx-claude — Quick Dispatch

Launch Claude in any directory without creating a new branch:

# From mission control, spawn a session in a specific project
tmx-claude -d ~/work/src/myapp "Fix the flaky test in user_spec.rb"

# Spawn and immediately attach to it
tmx-claude -a -d ~/work/src/billing "Review the invoice calculation"

# With custom window name
tmx-claude -n "auth-fix" -d ~/work/src/auth "Debug the OAuth callback"

What happens:

  1. Creates a new tmux window (or session if not in tmux)
  2. Changes to the specified directory
  3. Launches claude with your prompt
  4. Returns you to your original window (unless -a)

Good for:

  • Quick tasks in the main branch
  • Read-only investigations
  • Tasks that don't need branch isolation

tmx-worktree — Isolated Feature Work

Create a git worktree with a new branch and launch Claude in it:

# Create worktree + branch, launch Claude
tmx-worktree -b feature-auth "Implement OAuth login flow"

# Explicit project (when not in that directory)
tmx-worktree -p ~/work/src/myapp -b fix-123 "Fix the race condition in #123"

# Branch from non-main branch
tmx-worktree -b hotfix -f release-2.0 "Patch the security vuln"

What happens:

  1. Creates ~/work/src/<project>-<branch>/
  2. Creates a new git branch from main (or specified base)
  3. Spawns a tmux window in the worktree
  4. Launches Claude with your prompt

Good for:

  • Multi-session features
  • Risky refactors you might abandon
  • Parallel work on unrelated features
  • Reviewing/testing branches while keeping your work intact

worktree-clean — Safe Cleanup

Remove worktrees with safety checks:

# Remove a specific worktree (checks for uncommitted/unpushed work)
worktree-clean myapp-feature

# Also delete the branch
worktree-clean -d myapp-feature

# Preview what would be cleaned
worktree-clean --merged -n

# Clean all merged worktrees across projects
worktree-clean --merged

Safety checks:

  • Uncommitted changes (blocks)
  • Unpushed commits (blocks)
  • Unmerged branch (warns but doesn't block)

Workflow Patterns

Pattern 1: Dispatch and Forget

# You're triaging issues in ~/work/src/
# Found something that needs deep work in myapp

tmx-claude -d myapp "bd ready && bd show beads-abc123"

# Continue your triage - the child session handles myapp

Pattern 2: Parallel Investigations

# Need to understand how three apps handle auth differently

tmx-claude -d app-a "How does authentication work? Show me the flow."
tmx-claude -d app-b "How does authentication work? Show me the flow."
tmx-claude -d app-c "How does authentication work? Show me the flow."

# Three parallel sessions, each with full project context

Pattern 3: Isolated Feature Development

# Starting a feature that might take multiple sessions
tmx-worktree -p myapp -b feature-new-dashboard "Implement the analytics dashboard"

# Work happens across multiple days in ~/work/src/myapp-feature-new-dashboard/

# When merged, clean up
worktree-clean -d myapp-feature-new-dashboard

Pattern 4: PR Review Without Context Switch

# Someone asks you to review their PR
# You're mid-task in the main repo

# Option A: Quick review in main branch
tmx-claude -d myapp "Review PR #456 - git fetch && git diff origin/main..origin/feature-x"

# Option B: Full checkout for testing
git -C myapp fetch origin pull/456/head:pr-456
tmx-worktree -p myapp -b pr-456-review "Review and test PR #456"

Beads Issue Coordination

When dispatching work tracked in a parent directory's beads database, the child session won't find issues by ID alone (it looks in its own .beads/). Include the path:

# Instead of telling the child: "bd update beads-abc --status in_progress"
# Tell it to use the parent's database:

tmx-claude -d myapp "bd -C ~/work/src update src-abc --status in_progress && implement the feature"

Decision Guide

Situation Tool
Quick fix, same branch tmx-claude -d project "prompt"
Deep work, needs isolation tmx-worktree -b branch "prompt"
Read-only investigation tmx-claude -d project "prompt"
Might abandon the work tmx-worktree (easy cleanup)
Already in project's worktree Just use current session
Single-session task Just use current session

Implementation Notes

Both tools use the same underlying pattern:

  1. Create tmux window/session with -c DIR for working directory
  2. Use tmux send-keys to start claude with the prompt
  3. Return focus to original window (unless -a attach flag)

Worktrees live at ~/work/src/<project>-<branch>/ by convention, making them easy to find and clean up.

The tools gracefully handle both "inside tmux" (creates window) and "outside tmux" (creates session) scenarios.

#!/bin/bash
# tmx-claude - Launch Claude Code in a new tmux window with an initial prompt
#
# Usage:
# tmx-claude "Fix the login bug" # Current directory
# tmx-claude -d ~/work/src/myapp "Run the tests" # Specific directory
# cd ~/work/src/myapp && tmx-claude "Review code" # From that directory
#
# The window stays open for interactive follow-up. You remain in your
# original window to continue working.
set -e
usage() {
cat <<'EOF'
Usage: tmx-claude [-d DIR] [-n NAME] [-a] [-w SECS] PROMPT
Launch Claude Code in a new tmux window with an initial prompt.
The new window runs independently—you stay in your current window.
Options:
-d DIR Change to DIR before starting Claude (default: current directory)
-n NAME Window name (default: basename of directory)
-a Attach/switch to the new window immediately
-w SECS Seconds to wait for Claude to start (default: 2)
-h Show this help
Examples:
tmx-claude "Review the authentication code"
tmx-claude -d ~/work/src/myapp "Fix the login bug"
tmx-claude -n "auth-fix" "Update the auth flow"
Tip: Use with bd workflow:
tmx-claude -d ~/work/src/myapp "bd ready && bd show beads-xxx"
EOF
exit "${1:-0}"
}
# Check dependencies
if ! command -v tmux &>/dev/null; then
echo "Error: tmux is not installed" >&2
exit 1
fi
if ! command -v claude &>/dev/null; then
echo "Error: claude is not installed" >&2
exit 1
fi
# Defaults
DIR=""
WIN_NAME=""
ATTACH=false
WAIT_SECS=2
# Parse arguments
while getopts "d:n:aw:h" opt; do
case $opt in
d) DIR="$OPTARG" ;;
n) WIN_NAME="$OPTARG" ;;
a) ATTACH=true ;;
w) WAIT_SECS="$OPTARG" ;;
h) usage 0 ;;
*) usage 1 ;;
esac
done
shift $((OPTIND-1))
# Remaining args are the prompt
PROMPT="$*"
if [ -z "$PROMPT" ]; then
echo "Error: Prompt is required" >&2
usage 1
fi
# Use provided directory or current directory
DIR="${DIR:-$(pwd)}"
# Resolve to absolute path
DIR="$(cd "$DIR" 2>/dev/null && pwd)" || {
echo "Error: Directory does not exist: $DIR" >&2
exit 1
}
# Generate a window name from the directory if not provided
WIN_NAME="${WIN_NAME:-$(basename "$DIR")}"
# Truncate prompt for display (first 50 chars)
PROMPT_PREVIEW="${PROMPT:0:50}"
[ ${#PROMPT} -gt 50 ] && PROMPT_PREVIEW="${PROMPT_PREVIEW}..."
start_claude_in_window() {
local target="$1"
# Escape the prompt for shell embedding
local escaped_prompt
escaped_prompt=$(printf '%q' "$PROMPT")
# Start claude with the prompt as a positional argument (more reliable than send-keys)
tmux send-keys -t "$target" "claude $escaped_prompt" Enter
}
if [ -z "$TMUX" ]; then
# Not in tmux - create a new session (detached)
SESSION="claude-$$"
echo "Creating tmux session '$SESSION' in $DIR"
tmux new-session -d -s "$SESSION" -c "$DIR" -n "$WIN_NAME"
start_claude_in_window "$SESSION"
echo "✓ Started Claude in tmux session '$SESSION'"
echo " Window: $WIN_NAME"
echo " Dir: $DIR"
echo " Prompt: $PROMPT_PREVIEW"
echo ""
echo "Attach with: tmux attach -t $SESSION"
if $ATTACH; then
exec tmux attach -t "$SESSION"
fi
else
# In tmux - create a new window
CURRENT_WINDOW=$(tmux display-message -p '#I')
echo "Creating window '$WIN_NAME' in $DIR"
tmux new-window -n "$WIN_NAME" -c "$DIR"
start_claude_in_window "$WIN_NAME"
if ! $ATTACH; then
# Switch back to original window
tmux select-window -t "$CURRENT_WINDOW"
fi
echo "✓ Started Claude in window '$WIN_NAME'"
echo " Dir: $DIR"
echo " Prompt: $PROMPT_PREVIEW"
fi
#!/bin/bash
# tmx-worktree - Create a git worktree and launch Claude Code in it
#
# Usage:
# tmx-worktree -b feature-login "Implement login feature"
# tmx-worktree -p myapp -b fix-bug "Fix the authentication bug"
# tmx-worktree -b refactor -f develop "Refactor auth module"
#
# Creates a worktree at ~/work/src/<project>-<branch>/ and launches Claude
# in a new tmux window with the given prompt.
set -e
WORKTREE_BASE="$HOME/work/src"
usage() {
cat <<'EOF'
Usage: tmx-worktree [-p PROJECT] -b BRANCH [-f FROM] [-n NAME] [-a] PROMPT
Create a git worktree and launch Claude Code in a new tmux window.
Options:
-p PROJECT Source project directory (default: current directory)
-b BRANCH Branch name to create (required)
-f FROM Base branch to create from (default: auto-detect main/master)
-n NAME Tmux window name (default: project-branch)
-a Attach/switch to the new window immediately
-h Show this help
The worktree is created at ~/work/src/<project>-<branch>/
Examples:
tmx-worktree -b feature-auth "Implement OAuth login"
tmx-worktree -p myapp -b fix-123 "Fix issue #123"
tmx-worktree -b refactor -f develop "Refactor the data layer"
Cleanup:
Use worktree-clean to safely remove finished worktrees
EOF
exit "${1:-0}"
}
die() {
echo "Error: $1" >&2
exit 1
}
# Check dependencies
command -v git &>/dev/null || die "git is not installed"
command -v tmux &>/dev/null || die "tmux is not installed"
command -v claude &>/dev/null || die "claude is not installed"
# Defaults
PROJECT_DIR=""
BRANCH=""
FROM_BRANCH=""
WIN_NAME=""
ATTACH=false
# Parse arguments
while getopts "p:b:f:n:ah" opt; do
case $opt in
p) PROJECT_DIR="$OPTARG" ;;
b) BRANCH="$OPTARG" ;;
f) FROM_BRANCH="$OPTARG" ;;
n) WIN_NAME="$OPTARG" ;;
a) ATTACH=true ;;
h) usage 0 ;;
*) usage 1 ;;
esac
done
shift $((OPTIND-1))
# Remaining args are the prompt
PROMPT="$*"
# Validate required args
[ -z "$BRANCH" ] && die "Branch name is required (-b BRANCH)"
[ -z "$PROMPT" ] && die "Prompt is required"
# Resolve project directory
PROJECT_DIR="${PROJECT_DIR:-$(pwd)}"
PROJECT_DIR="$(cd "$PROJECT_DIR" 2>/dev/null && pwd)" || die "Directory does not exist: $PROJECT_DIR"
# Verify it's a git repository
git -C "$PROJECT_DIR" rev-parse --git-dir &>/dev/null || die "Not a git repository: $PROJECT_DIR"
# Get project name from directory
PROJECT_NAME="$(basename "$PROJECT_DIR")"
# Auto-detect main branch if not specified
if [ -z "$FROM_BRANCH" ]; then
# Try main first, then master
if git -C "$PROJECT_DIR" show-ref --verify --quiet refs/heads/main; then
FROM_BRANCH="main"
elif git -C "$PROJECT_DIR" show-ref --verify --quiet refs/heads/master; then
FROM_BRANCH="master"
else
die "Could not auto-detect main branch. Specify with -f"
fi
fi
# Verify base branch exists
git -C "$PROJECT_DIR" show-ref --verify --quiet "refs/heads/$FROM_BRANCH" || \
die "Base branch does not exist: $FROM_BRANCH"
# Build worktree path
WORKTREE_PATH="$WORKTREE_BASE/${PROJECT_NAME}-${BRANCH}"
# Check if worktree path already exists
if [ -e "$WORKTREE_PATH" ]; then
die "Worktree path already exists: $WORKTREE_PATH
To reuse it: tmx-claude -d '$WORKTREE_PATH' '$PROMPT'
To remove it: worktree-clean '$WORKTREE_PATH'"
fi
# Check if branch already exists
if git -C "$PROJECT_DIR" show-ref --verify --quiet "refs/heads/$BRANCH"; then
die "Branch already exists: $BRANCH
To use existing branch, create worktree manually:
cd $PROJECT_DIR && git worktree add '$WORKTREE_PATH' '$BRANCH'"
fi
# Set window name
WIN_NAME="${WIN_NAME:-${PROJECT_NAME}-${BRANCH}}"
echo "Creating worktree for $PROJECT_NAME..."
echo " Branch: $BRANCH (from $FROM_BRANCH)"
echo " Path: $WORKTREE_PATH"
echo ""
# Fetch latest to ensure we have up-to-date refs
echo "Fetching latest..."
git -C "$PROJECT_DIR" fetch --quiet origin "$FROM_BRANCH" 2>/dev/null || true
# Create the worktree with a new branch
# Use origin/$FROM_BRANCH if available, otherwise local $FROM_BRANCH
if git -C "$PROJECT_DIR" show-ref --verify --quiet "refs/remotes/origin/$FROM_BRANCH"; then
git -C "$PROJECT_DIR" worktree add -b "$BRANCH" "$WORKTREE_PATH" "origin/$FROM_BRANCH"
else
git -C "$PROJECT_DIR" worktree add -b "$BRANCH" "$WORKTREE_PATH" "$FROM_BRANCH"
fi
echo ""
echo "✓ Worktree created at $WORKTREE_PATH"
echo ""
# Now launch Claude in the worktree using tmx-claude's pattern
start_claude_in_window() {
local target="$1"
local escaped_prompt
escaped_prompt=$(printf '%q' "$PROMPT")
tmux send-keys -t "$target" "claude $escaped_prompt" Enter
}
# Truncate prompt for display
PROMPT_PREVIEW="${PROMPT:0:50}"
[ ${#PROMPT} -gt 50 ] && PROMPT_PREVIEW="${PROMPT_PREVIEW}..."
if [ -z "$TMUX" ]; then
# Not in tmux - create a new session
SESSION="worktree-$$"
echo "Creating tmux session '$SESSION'..."
tmux new-session -d -s "$SESSION" -c "$WORKTREE_PATH" -n "$WIN_NAME"
start_claude_in_window "$SESSION"
echo "✓ Started Claude in tmux session '$SESSION'"
echo " Window: $WIN_NAME"
echo " Dir: $WORKTREE_PATH"
echo " Prompt: $PROMPT_PREVIEW"
echo ""
echo "Attach with: tmux attach -t $SESSION"
if $ATTACH; then
exec tmux attach -t "$SESSION"
fi
else
# In tmux - create a new window
CURRENT_WINDOW=$(tmux display-message -p '#I')
echo "Creating window '$WIN_NAME'..."
tmux new-window -n "$WIN_NAME" -c "$WORKTREE_PATH"
start_claude_in_window "$WIN_NAME"
if ! $ATTACH; then
tmux select-window -t "$CURRENT_WINDOW"
fi
echo "✓ Started Claude in window '$WIN_NAME'"
echo " Dir: $WORKTREE_PATH"
echo " Prompt: $PROMPT_PREVIEW"
fi
echo ""
echo "When done, clean up with: worktree-clean $WORKTREE_PATH"
#!/bin/bash
# worktree-clean - Safely remove git worktrees
#
# Usage:
# worktree-clean ~/work/src/myapp-feature
# worktree-clean myapp-feature # Assumes ~/work/src/ prefix
# worktree-clean --merged # Clean all merged worktrees
#
# Safety checks:
# - Uncommitted changes
# - Unpushed commits
# - Unmerged branch (warning, not blocking)
set -e
WORKTREE_BASE="$HOME/work/src"
usage() {
cat <<'EOF'
Usage: worktree-clean [OPTIONS] PATH|NAME
worktree-clean --merged [-n]
Safely remove a git worktree created by tmx-worktree.
Arguments:
PATH Full path to worktree (e.g., ~/work/src/myapp-feature)
NAME Short name, assumes ~/work/src/ prefix (e.g., myapp-feature)
Options:
-f, --force Skip safety checks (uncommitted changes, unpushed commits)
-d, --delete Also delete the branch after removing worktree
-n, --dry-run Show what would be done without doing it
--merged Clean ALL worktrees whose branches are merged to main/master
-h, --help Show this help
Safety Checks:
- Uncommitted changes (blocks unless --force)
- Unpushed commits (blocks unless --force)
- Unmerged branch (warning only, does not block)
Examples:
worktree-clean myapp-feature # Remove specific worktree
worktree-clean -d myapp-feature # Remove worktree and delete branch
worktree-clean --merged # Clean all merged worktrees
worktree-clean --merged -n # Preview merged cleanup
EOF
exit "${1:-0}"
}
die() {
echo "Error: $1" >&2
exit 1
}
warn() {
echo "Warning: $1" >&2
}
info() {
echo "$1"
}
# Check dependencies
command -v git &>/dev/null || die "git is not installed"
# Defaults
FORCE=false
DELETE_BRANCH=false
DRY_RUN=false
CLEAN_MERGED=false
WORKTREE_PATH=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force) FORCE=true; shift ;;
-d|--delete) DELETE_BRANCH=true; shift ;;
-n|--dry-run) DRY_RUN=true; shift ;;
--merged) CLEAN_MERGED=true; shift ;;
-h|--help) usage 0 ;;
-*) die "Unknown option: $1" ;;
*)
if [ -z "$WORKTREE_PATH" ]; then
WORKTREE_PATH="$1"
else
die "Unexpected argument: $1"
fi
shift
;;
esac
done
# Get the main repo for a worktree
get_main_repo() {
local worktree="$1"
git -C "$worktree" rev-parse --path-format=absolute --git-common-dir 2>/dev/null | sed 's/\.git$//'
}
# Get branch name for a worktree
get_worktree_branch() {
local worktree="$1"
git -C "$worktree" rev-parse --abbrev-ref HEAD 2>/dev/null
}
# Detect main branch for a repo
get_main_branch() {
local repo="$1"
if git -C "$repo" show-ref --verify --quiet refs/heads/main; then
echo "main"
elif git -C "$repo" show-ref --verify --quiet refs/heads/master; then
echo "master"
else
echo ""
fi
}
# Check if branch is merged into main
is_branch_merged() {
local repo="$1"
local branch="$2"
local main_branch="$3"
# Check if branch is ancestor of main (i.e., merged)
git -C "$repo" merge-base --is-ancestor "$branch" "$main_branch" 2>/dev/null
}
# Check for uncommitted changes
has_uncommitted_changes() {
local worktree="$1"
! git -C "$worktree" diff --quiet 2>/dev/null || \
! git -C "$worktree" diff --cached --quiet 2>/dev/null
}
# Check for untracked files
has_untracked_files() {
local worktree="$1"
[ -n "$(git -C "$worktree" ls-files --others --exclude-standard 2>/dev/null)" ]
}
# Check for unpushed commits
has_unpushed_commits() {
local worktree="$1"
local branch
branch=$(get_worktree_branch "$worktree")
# Check if upstream exists
if git -C "$worktree" rev-parse --verify "@{upstream}" &>/dev/null; then
# Has upstream, check for unpushed
[ -n "$(git -C "$worktree" log '@{upstream}..HEAD' --oneline 2>/dev/null)" ]
else
# No upstream - if there are commits not in origin/main, consider unpushed
local main_branch
main_branch=$(get_main_branch "$worktree")
if [ -n "$main_branch" ]; then
local origin_main="origin/$main_branch"
if git -C "$worktree" show-ref --verify --quiet "refs/remotes/$origin_main"; then
[ -n "$(git -C "$worktree" log "$origin_main..HEAD" --oneline 2>/dev/null)" ]
else
# No remote, can't check
return 1
fi
else
return 1
fi
fi
}
# Clean a single worktree
clean_worktree() {
local worktree="$1"
local force="$2"
local delete_branch="$3"
local dry_run="$4"
# Resolve path
if [[ "$worktree" != /* ]]; then
worktree="$WORKTREE_BASE/$worktree"
fi
# Check if path exists
if [ ! -d "$worktree" ]; then
die "Worktree does not exist: $worktree"
fi
# Check if it's a git worktree
if ! git -C "$worktree" rev-parse --is-inside-work-tree &>/dev/null; then
die "Not a git repository: $worktree"
fi
local main_repo
main_repo=$(get_main_repo "$worktree")
# Check if it's actually a worktree (not the main repo)
local git_dir
git_dir=$(git -C "$worktree" rev-parse --git-dir)
if [[ "$git_dir" == ".git" ]]; then
die "This appears to be a main repository, not a worktree: $worktree"
fi
local branch
branch=$(get_worktree_branch "$worktree")
local main_branch
main_branch=$(get_main_branch "$main_repo")
info "Worktree: $worktree"
info "Branch: $branch"
info "Main repo: $main_repo"
echo ""
# Safety checks
local issues=0
if has_uncommitted_changes "$worktree"; then
if $force; then
warn "Uncommitted changes (--force specified, continuing)"
else
echo "✗ Uncommitted changes detected"
issues=$((issues + 1))
fi
else
echo "✓ No uncommitted changes"
fi
if has_untracked_files "$worktree"; then
if $force; then
warn "Untracked files (--force specified, continuing)"
else
echo "✗ Untracked files detected"
issues=$((issues + 1))
fi
else
echo "✓ No untracked files"
fi
if has_unpushed_commits "$worktree"; then
if $force; then
warn "Unpushed commits (--force specified, continuing)"
else
echo "✗ Unpushed commits detected"
issues=$((issues + 1))
fi
else
echo "✓ No unpushed commits"
fi
# Check merge status (warning only)
if [ -n "$main_branch" ] && [ "$branch" != "$main_branch" ]; then
if is_branch_merged "$main_repo" "$branch" "$main_branch"; then
echo "✓ Branch is merged into $main_branch"
else
warn "Branch is NOT merged into $main_branch"
fi
fi
echo ""
# Abort if issues found and not forcing
if [ $issues -gt 0 ] && ! $force; then
die "Safety checks failed. Use --force to override."
fi
# Perform cleanup
if $dry_run; then
echo "[DRY RUN] Would remove worktree: $worktree"
if $delete_branch; then
echo "[DRY RUN] Would delete branch: $branch"
fi
else
info "Removing worktree..."
git -C "$main_repo" worktree remove "$worktree"
echo "✓ Worktree removed"
if $delete_branch; then
info "Deleting branch..."
git -C "$main_repo" branch -D "$branch"
echo "✓ Branch '$branch' deleted"
fi
fi
echo ""
echo "Done!"
}
# Clean all merged worktrees
clean_merged_worktrees() {
local dry_run="$1"
info "Scanning for merged worktrees in $WORKTREE_BASE..."
echo ""
local cleaned=0
local skipped=0
# Find all directories that look like worktrees (contain .git file)
for dir in "$WORKTREE_BASE"/*; do
[ -d "$dir" ] || continue
# Check if it's a git worktree (has .git file, not directory)
if [ -f "$dir/.git" ]; then
local branch
branch=$(get_worktree_branch "$dir")
local main_repo
main_repo=$(get_main_repo "$dir")
local main_branch
main_branch=$(get_main_branch "$main_repo")
if [ -z "$main_branch" ]; then
warn "Skipping $dir: cannot determine main branch"
skipped=$((skipped + 1))
continue
fi
if [ "$branch" = "$main_branch" ]; then
continue # Skip main branch worktrees
fi
if is_branch_merged "$main_repo" "$branch" "$main_branch"; then
# Check for uncommitted/unpushed
if has_uncommitted_changes "$dir" || has_untracked_files "$dir"; then
warn "Skipping $dir: has uncommitted changes"
skipped=$((skipped + 1))
continue
fi
if $dry_run; then
echo "[DRY RUN] Would clean: $dir (branch: $branch)"
else
echo "Cleaning: $dir (branch: $branch)"
git -C "$main_repo" worktree remove "$dir"
git -C "$main_repo" branch -d "$branch" 2>/dev/null || true
fi
cleaned=$((cleaned + 1))
fi
fi
done
echo ""
if $dry_run; then
echo "Would clean $cleaned worktree(s), skipped $skipped"
else
echo "Cleaned $cleaned worktree(s), skipped $skipped"
fi
}
# Main logic
if $CLEAN_MERGED; then
clean_merged_worktrees "$DRY_RUN"
else
[ -z "$WORKTREE_PATH" ] && die "Worktree path is required (or use --merged)"
clean_worktree "$WORKTREE_PATH" "$FORCE" "$DELETE_BRANCH" "$DRY_RUN"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment