
Worktree Manager
- 112 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
Use worktree-manager for development tasks
About
worktree-manager: A skill for development. This provides functionality for development workflows.
- worktree-manager
Worktree Manager by the numbers
- 112 all-time installs (skills.sh)
- Ranked #2,921 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill worktree-managerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 112 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
What it does
Use worktree-manager for development tasks
Files
Alpha-Forge Worktree Manager
<!-- ADR: /docs/adr/2025-12-14-alpha-forge-worktree-management.md -->
Create and manage git worktrees for the alpha-forge repository with automatic branch naming, consistent conventions, and lifecycle management.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
When to Use This Skill
Use this skill when:
- Creating a new worktree for alpha-forge development
- Setting up a worktree from a remote branch
- Using an existing local branch in a new worktree
- Managing multiple parallel feature branches
Operational Modes
This skill supports three distinct modes based on user input:
| Mode | User Input Example | Action |
|---|---|---|
| New Branch | "create worktree for sharpe validation" | Derive slug, create branch + worktree |
| Remote Track | "create worktree from origin/feat/existing" | Track remote branch in new worktree |
| Local Branch | "create worktree for feat/2025-12-15-my-feat" | Use existing branch in new worktree |
---
Mode 1: New Branch from Description (Primary)
This is the most common workflow. User provides a natural language description, Claude derives the slug.
Step 1: Parse Description and Derive Slug
Claude derives kebab-case slugs following these rules:
Word Economy Rule:
- Each word in slug MUST convey unique meaning
- Remove filler words: the, a, an, for, with, and, to, from, in, on, of, by
- Avoid redundancy (e.g., "database" after "ClickHouse")
- Limit to 3-5 words maximum
Conversion Steps:
1. Parse description from user input 2. Convert to lowercase 3. Apply word economy (remove filler words) 4. Replace spaces with hyphens
Examples:
| User Description | Derived Slug |
|---|---|
| "sharpe statistical validation" | sharpe-statistical-validation |
| "fix the memory leak in metrics" | memory-leak-metrics |
| "implement user authentication for API" | user-authentication-api |
| "add BigQuery data source support" | bigquery-data-source |
Step 2: Verify Main Worktree Status
CRITICAL: Before proceeding, check that main worktree is on main branch.
/usr/bin/env bash << 'GIT_EOF'
cd ~/eon/alpha-forge
CURRENT=$(git branch --show-current)
GIT_EOFIf NOT on main/master:
Use AskUserQuestion to warn user:
question: "Main worktree is on '$CURRENT', not main. Best practice is to keep main worktree clean. Continue anyway?"
header: "Warning"
options:
- label: "Continue anyway"
description: "Proceed with worktree creation"
- label: "Switch main to 'main' first"
description: "I'll switch the main worktree to main branch before creating"
multiSelect: falseIf user selects "Switch main to 'main' first":
cd ~/eon/alpha-forge
git checkout mainStep 3: Fetch Remote and Display Branches
cd ~/eon/alpha-forge
git fetch --all --prune
# Display available branches for user reference
echo "Available remote branches:"
git branch -r | grep -v HEAD | head -20Step 4: Prompt for Branch Type
Use AskUserQuestion:
question: "What type of branch is this?"
header: "Branch type"
options:
- label: "feat"
description: "New feature or capability"
- label: "fix"
description: "Bug fix or correction"
- label: "refactor"
description: "Code restructuring (no behavior change)"
- label: "chore"
description: "Maintenance, tooling, dependencies"
multiSelect: falseStep 5: Prompt for Base Branch
Use AskUserQuestion:
question: "Which branch should this be based on?"
header: "Base branch"
options:
- label: "main (Recommended)"
description: "Base from main branch"
- label: "develop"
description: "Base from develop branch"
multiSelect: falseIf user needs a different branch, they can select "Other" and provide the branch name.
Step 6: Construct Branch Name
/usr/bin/env bash << 'SKILL_SCRIPT_EOF'
TYPE="feat" # From Step 4
DATE=$(date +%Y-%m-%d)
SLUG="sharpe-statistical-validation" # From Step 1
BASE="main" # From Step 5
BRANCH="${TYPE}/${DATE}-${SLUG}"
# Result: feat/2025-12-15-sharpe-statistical-validation
SKILL_SCRIPT_EOFStep 7: Create Worktree (Atomic)
/usr/bin/env bash << 'GIT_EOF_2'
cd ~/eon/alpha-forge
WORKTREE_PATH="$HOME/eon/alpha-forge.worktree-${DATE}-${SLUG}"
# Atomic branch + worktree creation
git worktree add -b "${BRANCH}" "${WORKTREE_PATH}" "origin/${BASE}"
GIT_EOF_2Step 8: Generate Tab Name and Report
/usr/bin/env bash << 'SKILL_SCRIPT_EOF_2'
# Generate acronym from slug
ACRONYM=$(echo "$SLUG" | tr '-' '\n' | cut -c1 | tr -d '\n')
TAB_NAME="AF-${ACRONYM}"
SKILL_SCRIPT_EOF_2Report success:
✓ Worktree created successfully
Path: ~/eon/alpha-forge.worktree-2025-12-15-sharpe-statistical-validation
Branch: feat/2025-12-15-sharpe-statistical-validation
Tab: AF-ssv
Env: .envrc created (loads shared secrets)
iTerm2: Restart iTerm2 to see the new tab---
Mode 2: Remote Branch Tracking
When user specifies origin/branch-name, create a local tracking branch.
Detection: User input contains origin/ prefix.
Example: "create worktree from origin/feat/2025-12-10-existing-feature"
Workflow
/usr/bin/env bash << 'GIT_EOF_3'
cd ~/eon/alpha-forge
git fetch --all --prune
REMOTE_BRANCH="origin/feat/2025-12-10-existing-feature"
LOCAL_BRANCH="feat/2025-12-10-existing-feature"
# Extract date and slug for worktree naming
# Pattern: type/YYYY-MM-DD-slug
if [[ "$LOCAL_BRANCH" =~ ^(feat|fix|refactor|chore)/([0-9]{4}-[0-9]{2}-[0-9]{2})-(.+)$ ]]; then
DATE="${BASH_REMATCH[2]}"
SLUG="${BASH_REMATCH[3]}"
else
DATE=$(date +%Y-%m-%d)
SLUG="${LOCAL_BRANCH##*/}"
fi
WORKTREE_PATH="$HOME/eon/alpha-forge.worktree-${DATE}-${SLUG}"
# Create tracking branch + worktree
git worktree add -b "${LOCAL_BRANCH}" "${WORKTREE_PATH}" "${REMOTE_BRANCH}"
GIT_EOF_3---
Mode 3: Existing Local Branch
When user specifies a local branch name (without origin/), use it directly.
Detection: User input is a valid branch name format (e.g., feat/2025-12-15-slug).
Example: "create worktree for feat/2025-12-15-my-feature"
Workflow
/usr/bin/env bash << 'VALIDATE_EOF'
cd ~/eon/alpha-forge
BRANCH="feat/2025-12-15-my-feature"
# Verify branch exists
if ! git show-ref --verify "refs/heads/${BRANCH}" 2>/dev/null; then
echo "ERROR: Local branch '${BRANCH}' not found"
echo "Available local branches:"
git branch | head -20
exit 1
fi
# Extract date and slug
if [[ "$BRANCH" =~ ^(feat|fix|refactor|chore)/([0-9]{4}-[0-9]{2}-[0-9]{2})-(.+)$ ]]; then
DATE="${BASH_REMATCH[2]}"
SLUG="${BASH_REMATCH[3]}"
else
DATE=$(date +%Y-%m-%d)
SLUG="${BRANCH##*/}"
fi
WORKTREE_PATH="$HOME/eon/alpha-forge.worktree-${DATE}-${SLUG}"
# Create worktree for existing branch (no -b flag)
git worktree add "${WORKTREE_PATH}" "${BRANCH}"
VALIDATE_EOF---
Naming Conventions
Worktree Folder Naming (ADR-Style)
Format: alpha-forge.worktree-YYYY-MM-DD-slug
Location: ~/eon/
| Branch | Worktree Folder |
|---|---|
feat/2025-12-14-sharpe-statistical-validation | alpha-forge.worktree-2025-12-14-sharpe-statistical-validation |
feat/2025-12-13-feature-genesis-skills | alpha-forge.worktree-2025-12-13-feature-genesis-skills |
fix/quick-patch | alpha-forge.worktree-{TODAY}-quick-patch |
iTerm2 Tab Naming (Acronym-Based)
Format: AF-{acronym} where acronym = first character of each word in slug
| Worktree Slug | Tab Name |
|---|---|
sharpe-statistical-validation | AF-ssv |
feature-genesis-skills | AF-fgs |
eth-block-metrics-data-plugin | AF-ebmdp |
---
Stale Worktree Detection
Check for worktrees whose branches are already merged to main:
/usr/bin/env bash << 'PREFLIGHT_EOF'
cd ~/eon/alpha-forge
# Get branches merged to main
MERGED=$(git branch --merged main | grep -v '^\*' | grep -v 'main' | tr -d ' ')
# Check each worktree
git worktree list --porcelain | grep '^branch' | cut -d' ' -f2 | while read branch; do
branch_name="${branch##refs/heads/}"
if echo "$MERGED" | grep -q "^${branch_name}$"; then
path=$(git worktree list | grep "\[${branch_name}\]" | awk '{print $1}')
echo "STALE: $branch_name at $path"
fi
done
PREFLIGHT_EOFIf stale worktrees found, prompt user to cleanup using AskUserQuestion.
---
Cleanup Workflow
Remove Stale Worktree
# Remove worktree (keeps branch)
git worktree remove ~/eon/alpha-forge.worktree-{DATE}-{SLUG}
# Optionally delete merged branch
git branch -d {BRANCH}Prune Orphaned Entries
git worktree prune---
Error Handling
| Scenario | Action |
|---|---|
| Branch already exists | Suggest using Mode 3 (existing branch) or rename |
| Remote branch not found | List available remote branches |
| Main worktree on feature | Warn via AskUserQuestion, offer to switch |
| Empty description | Show usage examples |
| Network error on fetch | Allow offline mode with local branches only |
| Worktree path exists | Suggest cleanup or different slug |
Branch Not Found
✗ Branch 'feat/nonexistent' not found
Available branches:
- feat/2025-12-14-sharpe-statistical-validation
- main
To create from remote:
Specify: "create worktree from origin/branch-name"Worktree Already Exists
✗ Worktree already exists for this branch
Existing path: ~/eon/alpha-forge.worktree-2025-12-14-sharpe-statistical-validation
To use existing worktree:
cd ~/eon/alpha-forge.worktree-2025-12-14-sharpe-statistical-validation---
Integration
direnv Environment Setup
Worktrees automatically get a .envrc file that loads shared credentials from ~/eon/.env.alpha-forge.
What happens on worktree creation:
1. Script checks if ~/eon/.env.alpha-forge exists 2. Creates .envrc in the new worktree with dotenv directive 3. Runs direnv allow to approve the new .envrc
Shared secrets file (~/eon/.env.alpha-forge):
# ClickHouse credentials, API keys, etc.
CLICKHOUSE_HOST_READONLY="..."
CLICKHOUSE_USER_READONLY="..."
CLICKHOUSE_PASSWORD_READONLY="..."Generated `.envrc` (in each worktree):
# alpha-forge worktree direnv config
# Auto-generated by create-worktree.sh
# Load shared alpha-forge secrets
dotenv $HOME/eon/.env.alpha-forge
# Worktree-specific overrides can be added belowPrerequisites:
- direnv installed via mise (
mise use -g direnv@latest) - Shell hook configured (
eval "$(direnv hook zsh)"in~/.zshrc) - Shared secrets file at
~/eon/.env.alpha-forge
iTerm2 Dynamic Detection
The default-layout.py script auto-discovers worktrees:
1. Globs ~/eon/alpha-forge.worktree-* 2. Validates each against git worktree list 3. Generates AF-{acronym} tab names 4. Inserts tabs after main AF tab
---
References
- Naming Conventions
- ADR: Alpha-Forge Git Worktree Management
- Design Spec
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Branch already exists | Local branch with same name | Use Mode 3 (existing branch) or rename slug |
| Remote branch not found | Typo or not pushed yet | Run git fetch --all --prune and list branches |
| Worktree path exists | Previous worktree not cleaned | Remove old worktree or use different slug |
| direnv not loading | Shell hook not configured | Add eval "$(direnv hook zsh)" to ~/.zshrc |
| Shared secrets not found | ~/eon/.env.alpha-forge missing | Create shared secrets file with required vars |
| iTerm2 tab not appearing | Dynamic layout not refreshed | Restart iTerm2 to trigger layout regeneration |
| Main worktree not on main | Previous work not switched | Run git checkout main in main worktree first |
| Stale worktree detected | Branch merged but not cleaned | Run git worktree remove <path> to cleanup |
Post-Execution Reflection
After this skill completes, reflect before closing the task:
0. Locate yourself. — Find this SKILL.md's canonical path before editing. 1. What failed? — Fix the instruction that caused it. 2. What worked better than expected? — Promote to recommended practice. 3. What drifted? — Fix any script, reference, or dependency that no longer matches reality. 4. Log it. — Evolution-log entry with trigger, fix, and evidence.
Do NOT defer. The next invocation inherits whatever you leave behind.
Evolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
Alpha-Forge Worktree Naming Conventions
<!-- ADR: /docs/adr/2025-12-14-alpha-forge-worktree-management.md -->
Reference for worktree folder naming and iTerm2 tab naming conventions.
Worktree Folder Naming
Format
alpha-forge.worktree-YYYY-MM-DD-slugComponents
| Component | Description | Example |
|---|---|---|
alpha-forge | Repository identifier | Fixed prefix |
.worktree- | Worktree marker | Fixed delimiter |
YYYY-MM-DD | Date (from branch or today) | 2025-12-14 |
slug | Descriptive name from branch | sharpe-statistical-validation |
Date Extraction Rules
1. Branch has date: Extract from branch name
feat/2025-12-14-feature-name→2025-12-14
2. Branch without date: Use today's date
feat/quick-fix→{TODAY}
Slug Extraction Rules (From Existing Branches)
1. Standard branch: Remove prefix and date
feat/2025-12-14-sharpe-statistical-validation→sharpe-statistical-validation
2. Branch without date: Remove prefix only
fix/memory-leak→memory-leak
3. Preserve hyphens: Keep slug as-is for acronym generation
eth-block-metricsstays aseth-block-metrics
Slug Derivation Rules (From Descriptions)
When creating new branches from natural language descriptions, Claude derives slugs using these rules.
Word Economy Rule
Each word in the slug MUST convey unique meaning:
- Remove filler words: the, a, an, for, with, and, to, from, in, on, of, by
- Avoid redundancy: Don't repeat concepts (e.g., "database" after "ClickHouse")
- Limit length: 3-5 words maximum
Conversion Steps
1. Parse description from user input 2. Convert to lowercase 3. Apply word economy (remove filler words) 4. Replace spaces with hyphens 5. Validate: only [a-z0-9-] characters
Examples
| User Description | Derived Slug | Words Removed |
|---|---|---|
| "sharpe statistical validation" | sharpe-statistical-validation | (none) |
| "fix the memory leak in metrics" | memory-leak-metrics | fix, the, in |
| "implement user authentication for API" | user-authentication-api | implement, for |
| "add BigQuery data source support" | bigquery-data-source | add, support |
| "refactor the database connection pool" | database-connection-pool | refactor, the |
Why Word Economy Matters
- Tab names: Shorter slugs → shorter acronyms → easier to identify
- Paths: Shorter slugs → shorter filesystem paths
- Consistency: Same description should always produce same slug
- Readability: Essential words only → clearer purpose at a glance
iTerm2 Tab Naming
Format
AF-{acronym}Acronym Generation
Take first character of each hyphen-separated word in the slug:
| Slug | Words | Acronym |
|---|---|---|
sharpe-statistical-validation | sharpe, statistical, validation | ssv |
feature-genesis-skills | feature, genesis, skills | fgs |
eth-block-metrics-data-plugin | eth, block, metrics, data, plugin | ebmdp |
quick-fix | quick, fix | qf |
memory-leak | memory, leak | ml |
Algorithm
/usr/bin/env bash << 'NAMING_CONVENTIONS_SCRIPT_EOF'
slug="sharpe-statistical-validation"
acronym=$(echo "$slug" | tr '-' '\n' | cut -c1 | tr -d '\n')
# Result: ssv
NAMING_CONVENTIONS_SCRIPT_EOFUniqueness
Acronyms are deterministic - same slug always produces same acronym. Collisions indicate:
1. Duplicate branches (shouldn't happen) 2. Need for more descriptive slugs
Recommendation: Use 3+ word slugs for better acronym uniqueness.
Examples
Complete Mapping
| Branch Name | Worktree Folder | Tab Name |
|---|---|---|
feat/2025-12-14-sharpe-statistical-validation | alpha-forge.worktree-2025-12-14-sharpe-statistical-validation | AF-ssv |
feat/2025-12-13-feature-genesis-skills | alpha-forge.worktree-2025-12-13-feature-genesis-skills | AF-fgs |
fix/2025-12-10-memory-leak-fix | alpha-forge.worktree-2025-12-10-memory-leak-fix | AF-mlf |
refactor/code-cleanup | alpha-forge.worktree-{TODAY}-code-cleanup | AF-cc |
Edge Cases
| Scenario | Branch | Worktree Folder | Tab |
|---|---|---|---|
| Single word slug | feat/hotfix | alpha-forge.worktree-{TODAY}-hotfix | AF-h |
| Numbers in slug | feat/v2-migration | alpha-forge.worktree-{TODAY}-v2-migration | AF-vm |
| Long acronym (5+) | feat/a-b-c-d-e-f | alpha-forge.worktree-{TODAY}-a-b-c-d-e-f | AF-abcdef |
Validation
Valid Worktree Names
- Starts with
alpha-forge.worktree- - Contains valid date
YYYY-MM-DD - Slug contains only
[a-z0-9-]
Valid Tab Names
- Starts with
AF- - Acronym is lowercase
[a-z]+ - Minimum 1 character acronym
Related
- SKILL.md - Main skill documentation
- ADR - Architecture decision
#!/usr/bin/env bash
# Cleanup alpha-forge worktree and optionally delete merged branch
# ADR: /docs/adr/2025-12-14-alpha-forge-worktree-management.md
#
# Usage: cleanup-worktree.sh <worktree-path> [--delete-branch]
# Example: cleanup-worktree.sh ~/eon/alpha-forge.worktree-2025-12-14-feature-name
# Example: cleanup-worktree.sh ~/eon/alpha-forge.worktree-2025-12-14-feature-name --delete-branch
set -euo pipefail
WORKTREE_PATH="${1:-}"
DELETE_BRANCH="${2:-}"
# Dynamic worktree detection (ADR: 2025-12-29-ralph-constraint-scanning.md)
# Uses git rev-parse --git-common-dir instead of hardcoded path
detect_alpha_forge_root() {
local git_common_dir
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null || echo "")
if [[ -z "$git_common_dir" ]]; then
echo ""
return
fi
if [[ "$git_common_dir" == ".git" ]]; then
# Main worktree - we're in the repo root
pwd
else
# Linked worktree - git-common-dir points to main's .git
dirname "$git_common_dir"
fi
}
AF_ROOT="${AF_ROOT:-$(detect_alpha_forge_root)}"
# Fallback to legacy path if detection fails
if [[ -z "$AF_ROOT" ]]; then
AF_ROOT="$HOME/eon/alpha-forge"
fi
# Validate input
if [[ -z "$WORKTREE_PATH" ]]; then
echo "Usage: cleanup-worktree.sh <worktree-path> [--delete-branch]" >&2
echo "Example: cleanup-worktree.sh ~/eon/alpha-forge.worktree-2025-12-14-feature-name" >&2
exit 1
fi
# Expand path
WORKTREE_PATH=$(eval echo "$WORKTREE_PATH")
# Validate alpha-forge repo exists
if [[ ! -d "$AF_ROOT/.git" ]]; then
echo "Error: alpha-forge repo not found at $AF_ROOT" >&2
echo "" >&2
echo "Fix options:" >&2
echo " 1. Run from within an alpha-forge worktree (auto-detection)" >&2
echo " 2. Set AF_ROOT: export AF_ROOT=~/path/to/alpha-forge" >&2
exit 1
fi
cd "$AF_ROOT"
# Validate worktree exists
if ! git worktree list | grep -q "^${WORKTREE_PATH} "; then
echo "Error: '$WORKTREE_PATH' is not a valid worktree" >&2
echo "" >&2
echo "Current worktrees:" >&2
git worktree list >&2
exit 1
fi
# Get branch name for this worktree
BRANCH=$(git worktree list | grep "^${WORKTREE_PATH} " | sed 's/.*\[\(.*\)\].*/\1/' || true)
if [[ -z "$BRANCH" ]]; then
echo "Warning: Could not determine branch for worktree" >&2
BRANCH="(unknown)"
fi
# Extract slug for tab name
SLUG=$(basename "$WORKTREE_PATH" | sed 's/alpha-forge\.worktree-[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-//')
ACRONYM=$(echo "$SLUG" | tr '-' '\n' | cut -c1 | tr -d '\n' | tr '[:upper:]' '[:lower:]')
echo "Removing worktree..."
echo " Path: $WORKTREE_PATH"
echo " Branch: $BRANCH"
echo " Tab: AF-${ACRONYM}"
echo ""
# Remove worktree
git worktree remove "$WORKTREE_PATH"
echo "✓ Worktree removed"
# Optionally delete branch
if [[ "$DELETE_BRANCH" == "--delete-branch" && "$BRANCH" != "(unknown)" ]]; then
echo ""
echo "Deleting branch '$BRANCH'..."
# Check if branch is merged (safe delete with -d)
if git branch -d "$BRANCH" 2>/dev/null; then
echo "✓ Branch deleted (was merged)"
else
echo "Warning: Branch not fully merged. Use 'git branch -D $BRANCH' to force delete." >&2
fi
fi
# Prune any orphaned worktree entries
git worktree prune 2>/dev/null || true
echo ""
echo "Done. Restart iTerm2 to update tab layout."
#!/usr/bin/env bash
# Create alpha-forge worktree with ADR-style naming
# ADR: /docs/adr/2025-12-14-alpha-forge-worktree-management.md
#
# Modes:
# new - Create new branch + worktree (atomic)
# remote - Track remote branch in worktree
# local - Use existing local branch
#
# Usage:
# New: create-worktree.sh --mode new --slug <slug> --type <type> --base <base>
# Remote: create-worktree.sh --mode remote --branch <remote-branch>
# Local: create-worktree.sh --mode local --branch <branch>
#
# Examples:
# create-worktree.sh --mode new --slug sharpe-statistical-validation --type feat --base main
# create-worktree.sh --mode remote --branch origin/feat/2025-12-10-existing
# create-worktree.sh --mode local --branch feat/2025-12-15-my-feature
set -euo pipefail
# Dynamic worktree detection (ADR: 2025-12-29-ralph-constraint-scanning.md)
# Uses git rev-parse --git-common-dir instead of hardcoded path
detect_alpha_forge_root() {
local git_common_dir
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null || echo "")
if [[ -z "$git_common_dir" ]]; then
echo ""
return
fi
if [[ "$git_common_dir" == ".git" ]]; then
# Main worktree - we're in the repo root
pwd
else
# Linked worktree - git-common-dir points to main's .git
dirname "$git_common_dir"
fi
}
# Defaults
MODE=""
SLUG=""
TYPE=""
BASE=""
BRANCH=""
AF_ROOT="${AF_ROOT:-$(detect_alpha_forge_root)}"
WORKTREE_BASE="${WORKTREE_BASE:-$HOME/eon}"
# Fallback to legacy path if detection fails
if [[ -z "$AF_ROOT" ]]; then
AF_ROOT="$HOME/eon/alpha-forge"
fi
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--mode)
MODE="$2"
shift 2
;;
--slug)
SLUG="$2"
shift 2
;;
--type)
TYPE="$2"
shift 2
;;
--base)
BASE="$2"
shift 2
;;
--branch)
BRANCH="$2"
shift 2
;;
*)
# Legacy support: first positional arg is branch (local mode)
if [[ -z "$BRANCH" ]]; then
BRANCH="$1"
MODE="${MODE:-local}"
fi
shift
;;
esac
done
# Validate mode
if [[ -z "$MODE" ]]; then
echo "Error: --mode required (new|remote|local)" >&2
echo "" >&2
echo "Usage:" >&2
echo " New: create-worktree.sh --mode new --slug <slug> --type <type> --base <base>" >&2
echo " Remote: create-worktree.sh --mode remote --branch <remote-branch>" >&2
echo " Local: create-worktree.sh --mode local --branch <branch>" >&2
exit 1
fi
# Validate alpha-forge repo exists
if [[ ! -d "$AF_ROOT/.git" ]]; then
echo "Error: alpha-forge repo not found at $AF_ROOT" >&2
echo "" >&2
echo "Fix options:" >&2
echo " 1. Run from within an alpha-forge worktree (auto-detection)" >&2
echo " 2. Set AF_ROOT: export AF_ROOT=~/path/to/alpha-forge" >&2
exit 1
fi
cd "$AF_ROOT"
# Mode-specific validation and execution
case "$MODE" in
new)
# Mode 1: New branch from description (atomic creation)
if [[ -z "$SLUG" || -z "$TYPE" || -z "$BASE" ]]; then
echo "Error: --mode new requires --slug, --type, and --base" >&2
exit 1
fi
DATE=$(date +%Y-%m-%d)
BRANCH="${TYPE}/${DATE}-${SLUG}"
WORKTREE_NAME="alpha-forge.worktree-${DATE}-${SLUG}"
WORKTREE_PATH="${WORKTREE_BASE}/${WORKTREE_NAME}"
# Check if branch already exists
if git show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then
echo "Error: Branch '$BRANCH' already exists" >&2
echo "Use --mode local to create worktree for existing branch" >&2
exit 1
fi
# Check if worktree path already exists
if [[ -d "$WORKTREE_PATH" ]]; then
echo "Error: Worktree already exists at $WORKTREE_PATH" >&2
exit 1
fi
# Validate base branch exists on remote
if ! git show-ref --verify --quiet "refs/remotes/origin/$BASE" 2>/dev/null; then
echo "Error: Base branch 'origin/$BASE' not found" >&2
echo "" >&2
echo "Available remote branches:" >&2
git branch -r --format=' %(refname:short)' | head -20 >&2
exit 1
fi
# Atomic branch + worktree creation
echo "Creating new branch and worktree..."
echo " Branch: $BRANCH"
echo " Base: origin/$BASE"
echo " Path: $WORKTREE_PATH"
git worktree add -b "$BRANCH" "$WORKTREE_PATH" "origin/$BASE"
;;
remote)
# Mode 2: Track remote branch
if [[ -z "$BRANCH" ]]; then
echo "Error: --mode remote requires --branch <remote-branch>" >&2
exit 1
fi
# Strip origin/ prefix if present for local branch name
REMOTE_BRANCH="$BRANCH"
LOCAL_BRANCH="${BRANCH#origin/}"
# Extract date and slug from branch name
if [[ "$LOCAL_BRANCH" =~ ^(feat|fix|refactor|chore)/([0-9]{4}-[0-9]{2}-[0-9]{2})-(.+)$ ]]; then
DATE="${BASH_REMATCH[2]}"
SLUG="${BASH_REMATCH[3]}"
elif [[ "$LOCAL_BRANCH" =~ ^(feat|fix|refactor|chore)/(.+)$ ]]; then
DATE=$(date +%Y-%m-%d)
SLUG="${BASH_REMATCH[2]}"
else
DATE=$(date +%Y-%m-%d)
SLUG="${LOCAL_BRANCH##*/}"
fi
WORKTREE_NAME="alpha-forge.worktree-${DATE}-${SLUG}"
WORKTREE_PATH="${WORKTREE_BASE}/${WORKTREE_NAME}"
# Validate remote branch exists
if ! git show-ref --verify --quiet "refs/remotes/$REMOTE_BRANCH" 2>/dev/null; then
echo "Error: Remote branch '$REMOTE_BRANCH' not found" >&2
echo "" >&2
echo "Available remote branches:" >&2
git branch -r --format=' %(refname:short)' | head -20 >&2
exit 1
fi
# Check if worktree path already exists
if [[ -d "$WORKTREE_PATH" ]]; then
echo "Error: Worktree already exists at $WORKTREE_PATH" >&2
exit 1
fi
# Create tracking branch + worktree
echo "Creating tracking branch and worktree..."
echo " Remote: $REMOTE_BRANCH"
echo " Local: $LOCAL_BRANCH"
echo " Path: $WORKTREE_PATH"
git worktree add -b "$LOCAL_BRANCH" "$WORKTREE_PATH" "$REMOTE_BRANCH"
BRANCH="$LOCAL_BRANCH"
;;
local)
# Mode 3: Existing local branch
if [[ -z "$BRANCH" ]]; then
echo "Error: --mode local requires --branch <branch>" >&2
exit 1
fi
# Extract date and slug from branch name
if [[ "$BRANCH" =~ ^(feat|fix|refactor|chore)/([0-9]{4}-[0-9]{2}-[0-9]{2})-(.+)$ ]]; then
DATE="${BASH_REMATCH[2]}"
SLUG="${BASH_REMATCH[3]}"
elif [[ "$BRANCH" =~ ^(feat|fix|refactor|chore)/(.+)$ ]]; then
DATE=$(date +%Y-%m-%d)
SLUG="${BASH_REMATCH[2]}"
else
DATE=$(date +%Y-%m-%d)
SLUG="${BRANCH##*/}"
fi
WORKTREE_NAME="alpha-forge.worktree-${DATE}-${SLUG}"
WORKTREE_PATH="${WORKTREE_BASE}/${WORKTREE_NAME}"
# Validate local branch exists
if ! git show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then
echo "Error: Local branch '$BRANCH' not found" >&2
echo "" >&2
echo "Available local branches:" >&2
git branch --format=' %(refname:short)' | head -20 >&2
exit 1
fi
# Check if worktree path already exists
if [[ -d "$WORKTREE_PATH" ]]; then
echo "Error: Worktree already exists at $WORKTREE_PATH" >&2
exit 1
fi
# Check if branch already has a worktree
EXISTING_WT=$(git worktree list | grep "\[$BRANCH\]" | awk '{print $1}' || true)
if [[ -n "$EXISTING_WT" ]]; then
echo "Error: Branch '$BRANCH' already has a worktree at $EXISTING_WT" >&2
exit 1
fi
# Create worktree for existing branch
echo "Creating worktree for existing branch..."
echo " Branch: $BRANCH"
echo " Path: $WORKTREE_PATH"
git worktree add "$WORKTREE_PATH" "$BRANCH"
;;
*)
echo "Error: Unknown mode '$MODE'. Use: new|remote|local" >&2
exit 1
;;
esac
# Generate tab name from slug
ACRONYM=$(echo "$SLUG" | tr '-' '\n' | cut -c1 | tr -d '\n' | tr '[:upper:]' '[:lower:]')
TAB_NAME="AF-${ACRONYM}"
# Create .envrc for direnv (auto-load environment variables)
ENVRC_PATH="${WORKTREE_PATH}/.envrc"
ENV_SHARED="${WORKTREE_BASE}/.env.alpha-forge"
if [[ -f "$ENV_SHARED" ]]; then
cat > "$ENVRC_PATH" << EOF
# alpha-forge worktree direnv config
# Auto-generated by create-worktree.sh
# Load shared alpha-forge secrets (ClickHouse, API keys, etc.)
dotenv ${ENV_SHARED}
# Worktree-specific overrides can be added below
EOF
echo " Created .envrc (loads shared secrets)"
# Auto-allow direnv if available
if command -v direnv &> /dev/null; then
(cd "$WORKTREE_PATH" && direnv allow) 2>/dev/null || true
fi
else
echo " Note: No shared .env found at $ENV_SHARED"
echo " Create it to auto-load secrets in worktrees"
fi
echo ""
echo "✓ Worktree created successfully"
echo ""
echo " Path: $WORKTREE_PATH"
echo " Branch: $BRANCH"
echo " Tab: $TAB_NAME"
echo ""
echo " Note: Restart iTerm2 to see the new tab in your layout."
#!/usr/bin/env bash
# Detect stale (merged) worktrees in alpha-forge
# ADR: /docs/adr/2025-12-14-alpha-forge-worktree-management.md
#
# Usage: detect-stale.sh
# Output: List of stale worktrees (branch merged to main)
set -euo pipefail
# Dynamic worktree detection (ADR: 2025-12-29-ralph-constraint-scanning.md)
# Uses git rev-parse --git-common-dir instead of hardcoded path
detect_alpha_forge_root() {
local git_common_dir
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null || echo "")
if [[ -z "$git_common_dir" ]]; then
echo ""
return
fi
if [[ "$git_common_dir" == ".git" ]]; then
# Main worktree - we're in the repo root
pwd
else
# Linked worktree - git-common-dir points to main's .git
dirname "$git_common_dir"
fi
}
AF_ROOT="${AF_ROOT:-$(detect_alpha_forge_root)}"
# Fallback to legacy path if detection fails
if [[ -z "$AF_ROOT" ]]; then
AF_ROOT="$HOME/eon/alpha-forge"
fi
# Validate alpha-forge repo exists
if [[ ! -d "$AF_ROOT/.git" ]]; then
echo "Error: alpha-forge repo not found at $AF_ROOT" >&2
exit 1
fi
cd "$AF_ROOT"
# Get branches merged to main (excluding main itself and current branch)
MERGED=$(git branch --merged main 2>/dev/null | grep -v '^\*' | grep -v 'main' | tr -d ' ' || true)
if [[ -z "$MERGED" ]]; then
echo "No merged branches found."
exit 0
fi
# Get worktree information
WORKTREES=$(git worktree list --porcelain 2>/dev/null || true)
if [[ -z "$WORKTREES" ]]; then
echo "No worktrees found."
exit 0
fi
# Track if we found any stale worktrees
FOUND_STALE=0
# Check each worktree
while IFS= read -r line; do
if [[ "$line" =~ ^worktree\ (.+)$ ]]; then
CURRENT_PATH="${BASH_REMATCH[1]}"
elif [[ "$line" =~ ^branch\ refs/heads/(.+)$ ]]; then
BRANCH_NAME="${BASH_REMATCH[1]}"
# Skip if this is the main worktree
if [[ "$CURRENT_PATH" == "$AF_ROOT" ]]; then
continue
fi
# Check if branch is in merged list
if echo "$MERGED" | grep -q "^${BRANCH_NAME}$"; then
if [[ $FOUND_STALE -eq 0 ]]; then
echo "Stale worktrees (branch merged to main):"
echo ""
FOUND_STALE=1
fi
# Extract slug and generate tab name for context
SLUG=$(basename "$CURRENT_PATH" | sed 's/alpha-forge\.worktree-[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-//')
ACRONYM=$(echo "$SLUG" | tr '-' '\n' | cut -c1 | tr -d '\n' | tr '[:upper:]' '[:lower:]')
echo " STALE: $BRANCH_NAME"
echo " Path: $CURRENT_PATH"
echo " Tab: AF-${ACRONYM}"
echo ""
fi
fi
done <<< "$WORKTREES"
if [[ $FOUND_STALE -eq 0 ]]; then
echo "No stale worktrees found. All worktree branches are unmerged."
fi