|
#!/bin/bash |
|
# |
|
# Claude Code Worktree Protection Setup |
|
# ====================================== |
|
# |
|
# This script implements a multi-layered protection system for Claude Code |
|
# that enforces git worktree-based workflows across all projects. |
|
# |
|
# What it does: |
|
# - Installs global rules in ~/.claude/CLAUDE.md |
|
# - Creates enforcement hooks in ~/.claude/hooks/ |
|
# - Configures global settings in ~/.claude/settings.json |
|
# - Provides helper functions for project-level setup |
|
# |
|
# Why: |
|
# Multiple AI agents (or AI + human) working concurrently need isolation |
|
# to prevent accidentally overwriting each other's uncommitted work. |
|
# Git worktrees provide physical separation and safe concurrent development. |
|
# |
|
# Usage: |
|
# bash setup-claude-worktree-protection.sh |
|
# |
|
# Author: Generated for Claude Code multi-agent workflows |
|
# License: MIT |
|
|
|
set -e # Exit on error |
|
|
|
# Colors for output |
|
RED='\033[0;31m' |
|
GREEN='\033[0;32m' |
|
YELLOW='\033[1;33m' |
|
BLUE='\033[0;34m' |
|
NC='\033[0m' # No Color |
|
|
|
# Configuration |
|
CLAUDE_HOME="$HOME/.claude" |
|
BACKUP_DIR="$CLAUDE_HOME/backups/$(date +%Y%m%d_%H%M%S)" |
|
|
|
# Helper functions |
|
log_info() { |
|
echo -e "${BLUE}βΉ${NC} $1" |
|
} |
|
|
|
log_success() { |
|
echo -e "${GREEN}β${NC} $1" |
|
} |
|
|
|
log_warning() { |
|
echo -e "${YELLOW}β ${NC} $1" |
|
} |
|
|
|
log_error() { |
|
echo -e "${RED}β${NC} $1" |
|
} |
|
|
|
separator() { |
|
echo "βββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
} |
|
|
|
backup_if_exists() { |
|
local file="$1" |
|
if [ -f "$file" ]; then |
|
mkdir -p "$BACKUP_DIR" |
|
cp "$file" "$BACKUP_DIR/" |
|
log_info "Backed up existing $(basename "$file") to $BACKUP_DIR" |
|
return 0 |
|
fi |
|
return 1 |
|
} |
|
|
|
# Main installation |
|
main() { |
|
separator |
|
echo "Claude Code Worktree Protection Setup" |
|
separator |
|
echo "" |
|
|
|
log_info "This will install global protection rules for all Claude Code sessions" |
|
log_info "Installation location: $CLAUDE_HOME" |
|
echo "" |
|
|
|
read -p "Continue? (y/N) " -n 1 -r |
|
echo |
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|
log_info "Installation cancelled" |
|
exit 0 |
|
fi |
|
|
|
echo "" |
|
separator |
|
echo "Step 1: Creating directory structure" |
|
separator |
|
|
|
mkdir -p "$CLAUDE_HOME/hooks" |
|
mkdir -p "$CLAUDE_HOME/commands" |
|
log_success "Created $CLAUDE_HOME/hooks/" |
|
log_success "Created $CLAUDE_HOME/commands/" |
|
|
|
echo "" |
|
separator |
|
echo "Step 2: Installing global memory rules" |
|
separator |
|
|
|
backup_if_exists "$CLAUDE_HOME/CLAUDE.md" |
|
|
|
cat > "$CLAUDE_HOME/CLAUDE.md" << 'EOF' |
|
# Global Git Safety Rules - ALL PROJECTS |
|
|
|
This file is automatically loaded by Claude Code for every session across all projects. |
|
These rules enforce safe concurrent multi-agent development workflows using Git worktrees. |
|
|
|
## Multi-Agent Worktree Architecture |
|
|
|
**MANDATORY**: All development work MUST happen in Git linked worktrees to prevent conflicts |
|
between concurrent AI agents or AI + human workflows. |
|
|
|
### Why Worktrees? |
|
- **Physical isolation**: Each agent works in a separate directory |
|
- **No conflicts**: Changes in one worktree don't affect others |
|
- **Safe experimentation**: Reset, rebase, force operations are safe within your worktree |
|
- **Clean history**: Each agent's work is on a clear branch |
|
- **Easy recovery**: Worktrees can be removed without data loss |
|
- **Standard Git**: No special tooling required |
|
|
|
### Important: Worktrees Live Outside the Repository |
|
Git worktrees must be created outside the main repository directory. This is a Git requirement. |
|
We create them in a sibling `worktrees/` directory for organization. |
|
|
|
## Quick Start - Copy This to Start Any Session |
|
|
|
Run these commands from the main repository directory: |
|
|
|
```bash |
|
# 1) Name your task (free text; will be sanitized automatically) |
|
TASK_NAME="brief-task-description" |
|
|
|
# 2) Derive safe names and paths |
|
TASK_SLUG=$(echo "$TASK_NAME" \ |
|
| tr '[:upper:]' '[:lower:]' \ |
|
| tr -cs 'a-z0-9._-' '-') |
|
BRANCH_NAME="agent/$(date +%s)-$TASK_SLUG" |
|
|
|
REPO_ROOT="$(git rev-parse --show-toplevel)" |
|
WORKTREES_ROOT="$(cd "$REPO_ROOT/.." && pwd)/worktrees" |
|
mkdir -p "$WORKTREES_ROOT" |
|
WORKTREE_PATH="$WORKTREES_ROOT/$(basename "$BRANCH_NAME")" |
|
|
|
# 3) Detect default branch and fetch |
|
DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p') |
|
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main} |
|
git fetch origin |
|
|
|
# 4) Create and enter your isolated worktree from the default branch |
|
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "origin/$DEFAULT_BRANCH" |
|
cd "$WORKTREE_PATH" |
|
|
|
# 5) Verify context (should be a linked worktree on agent/*) |
|
git branch --show-current |
|
pwd |
|
if [ "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)" ]; then |
|
echo "WARNING: This is the main working tree; do not develop here." |
|
else |
|
echo "β Linked worktree OK." |
|
fi |
|
|
|
# 6) Log this worktree in the shared git directory (works from any worktree) |
|
echo "$(date -Iseconds) | $BRANCH_NAME | $WORKTREE_PATH | $TASK_NAME" \ |
|
>> "$(git rev-parse --git-common-dir)/worktrees.log" |
|
|
|
# Optional: set upstream now so future pushes don't need -u |
|
# git push -u origin "$BRANCH_NAME" |
|
``` |
|
|
|
### Working in Your Worktree |
|
|
|
You are isolated from other agentsβwork freely in this linked worktree. |
|
|
|
β
**SAFE operations** (no user consent needed): |
|
- `git reset --hard` - Only affects your linked worktree |
|
- `git clean -fd` - Only affects your linked worktree |
|
- `git rebase` / `git commit --amend` - Your branch, your rules |
|
- Experimental changes - Isolated from others |
|
- Commit frequently with descriptive messages |
|
|
|
β οΈ **REQUIRES USER CONSENT**: |
|
- `git push --force` to shared branches (default branch, release branches, etc.) |
|
- Deleting branches outside agent/* namespace |
|
- Modifying branches used by others |
|
|
|
β **NEVER DO**: |
|
- Work directly in the main working tree (always use a linked worktree) |
|
- Force push to the default branch |
|
- Delete other agents' agent/* branches without approval |
|
|
|
### Syncing with Upstream |
|
|
|
To incorporate upstream changes into your branch: |
|
|
|
```bash |
|
git fetch origin |
|
DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p') |
|
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main} |
|
|
|
# Rebase (cleaner history) or merge (history-preserving) β team preference |
|
git rebase "origin/$DEFAULT_BRANCH" # or: git merge "origin/$DEFAULT_BRANCH" |
|
``` |
|
|
|
### Completing Your Work |
|
|
|
1) Ensure all changes are committed and the tree is clean: |
|
|
|
```bash |
|
git status # Should show a clean working directory |
|
``` |
|
|
|
2) Push your branch (use `-u` the first time to set upstream): |
|
|
|
```bash |
|
git push -u origin "$BRANCH_NAME" # first push |
|
# Afterwards: git push |
|
``` |
|
|
|
3) Ask the user: "I've completed the work on branch `$BRANCH_NAME`. Would you like me to: |
|
- Create a PR for review? |
|
- Merge directly to the default branch? |
|
- Keep the worktree for further work?" |
|
|
|
4) After merge approval, clean up from the main repo directory: |
|
|
|
```bash |
|
cd "$REPO_ROOT" |
|
git worktree remove "$WORKTREE_PATH" |
|
git branch -d "$BRANCH_NAME" # local cleanup |
|
|
|
# Optional remote cleanup after merge |
|
# git push origin --delete "$BRANCH_NAME" |
|
|
|
# Optional maintenance |
|
# git worktree prune |
|
``` |
|
|
|
### Resuming an Existing Session |
|
|
|
If you already have a branch and want a worktree for it: |
|
|
|
```bash |
|
BRANCH_NAME="agent/<timestamp>-<task-slug>" |
|
REPO_ROOT="$(git rev-parse --show-toplevel)" |
|
WORKTREES_ROOT="$(cd "$REPO_ROOT/.." && pwd)/worktrees" |
|
WORKTREE_PATH="$WORKTREES_ROOT/$(basename "$BRANCH_NAME")" |
|
|
|
git fetch origin |
|
|
|
# If the local branch exists already |
|
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then |
|
git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" |
|
else |
|
# Branch only on remote (or fresh): create local branch tracking remote |
|
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "origin/$BRANCH_NAME" |
|
fi |
|
|
|
cd "$WORKTREE_PATH" |
|
echo "$(date -Iseconds) | $BRANCH_NAME | $WORKTREE_PATH | RESUME" \ |
|
>> "$(git rev-parse --git-common-dir)/worktrees.log" |
|
``` |
|
|
|
## Destructive Operations - Global Policy |
|
|
|
**Core principle**: If reverting individual files, manually undo changes from memory |
|
rather than using git reset. This is safer and more precise. |
|
|
|
**Rationale**: Other agents may have uncommitted work. Blanket resets risk data loss. |
|
|
|
**Enforcement**: A pre-bash hook will block destructive operations outside worktrees. |
|
|
|
## Directory Structure Convention |
|
|
|
``` |
|
my-project/ # Main repository |
|
βββ .git/ |
|
β βββ worktrees/ # Git's worktree metadata |
|
βββ README.md |
|
βββ src/ |
|
βββ ... |
|
|
|
worktrees/ # Agent worktrees (parallel directory) |
|
βββ agent-1234567890-feature-x/ |
|
βββ agent-1234567891-bugfix-y/ |
|
βββ agent-1234567892-refactor-z/ |
|
``` |
|
|
|
## Troubleshooting |
|
|
|
**"fatal: 'worktrees/...' already exists"** |
|
β That path is in use. Use a different task name or clean up old worktrees. |
|
|
|
**"Logging fails with `.git/worktrees.log`"** |
|
β In a linked worktree, `.git` is a file, not a directory. Always log to: |
|
```bash |
|
$(git rev-parse --git-common-dir)/worktrees.log |
|
``` |
|
|
|
**"I'm not sure which worktree I'm in"** |
|
β Run: `git worktree list` and `pwd` to orient yourself. |
|
β Verify you're in a linked worktree: |
|
```bash |
|
test "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" \ |
|
&& echo "Linked worktree β" || echo "Main working tree β οΈ" |
|
``` |
|
|
|
**"Can I work on multiple tasks simultaneously?"** |
|
β Yes! Create multiple worktrees, one per task. Each is isolated. |
|
|
|
**"How do I see what other agents are working on?"** |
|
β Run: `git branch -a | grep agent/` to see active agent branches. |
|
|
|
**"Default branch isn't `main`"** |
|
β The quick start auto-detects via `git remote show origin`. If detection fails, set `DEFAULT_BRANCH` explicitly. |
|
|
|
**"Hook blocked me but I'm in a worktree"** |
|
β Verify you're on an `agent/*` branch: `git branch --show-current` |
|
β The hook requires BOTH: (1) linked worktree AND (2) agent/* branch. |
|
|
|
## Benefits Summary |
|
|
|
This workflow transforms the original safety concern into a solved problem: |
|
- Agents can't accidentally interfere (physical separation) |
|
- All git operations are safe within your worktree |
|
- Clean, reviewable history per feature/fix |
|
- Easy parallel development |
|
- No special tooling required (standard git) |
|
|
|
## Advanced: Hooks and Automation |
|
|
|
Projects can add additional checks in `.claude/hooks/` at the project level. |
|
These layer on top of global rules for defense-in-depth. |
|
|
|
See: https://docs.claude.com/en/docs/claude-code/settings for details. |
|
|
|
--- |
|
|
|
**Remember**: This is enforced automatically via hooks. If blocked, you're being |
|
protected from a potentially destructive operation. Follow the guidance to create |
|
a worktree and work safely. |
|
EOF |
|
|
|
log_success "Created $CLAUDE_HOME/CLAUDE.md" |
|
|
|
echo "" |
|
separator |
|
echo "Step 3: Installing enforcement hook" |
|
separator |
|
|
|
backup_if_exists "$CLAUDE_HOME/hooks/pre-bash.sh" |
|
|
|
cat > "$CLAUDE_HOME/hooks/pre-bash.sh" << 'EOF' |
|
#!/bin/bash |
|
# |
|
# Global Pre-Bash Hook for Claude Code |
|
# ===================================== |
|
# |
|
# This hook enforces the worktree-based workflow for all git operations |
|
# across all projects on this workstation. |
|
# |
|
# Location: ~/.claude/hooks/pre-bash.sh |
|
# Triggered: Before every bash command Claude Code attempts to run |
|
# Scope: Global (all projects) |
|
|
|
# Read the tool invocation data from Claude Code |
|
read -r input |
|
|
|
# Extract the bash command that Claude wants to run |
|
command=$(echo "$input" | jq -r '.command // ""') |
|
|
|
# List of destructive git operations that require worktree isolation |
|
DESTRUCTIVE_PATTERN="git (reset --hard|clean -f.*d|push.*--force|checkout -- \.)" |
|
|
|
# Check if this command matches our destructive pattern |
|
if echo "$command" | grep -qE "$DESTRUCTIVE_PATTERN"; then |
|
|
|
# First, check if we're even in a git repository |
|
if ! git rev-parse --git-dir > /dev/null 2>&1; then |
|
# Not in a git repo - this might be a different context (npm install, etc.) |
|
# Allow it to proceed |
|
exit 0 |
|
fi |
|
|
|
# Gather context information |
|
current_branch=$(git branch --show-current 2>/dev/null) |
|
current_dir=$(pwd) |
|
|
|
# Check if we're in a linked worktree (not the main working tree) |
|
# In linked worktrees: git-dir != git-common-dir |
|
# In main working tree: git-dir == git-common-dir |
|
git_dir=$(git rev-parse --git-dir 2>/dev/null) |
|
git_common_dir=$(git rev-parse --git-common-dir 2>/dev/null) |
|
|
|
if [ "$git_dir" = "$git_common_dir" ]; then |
|
# We're in the main working tree - ALWAYS unsafe |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "β GLOBAL SAFETY BLOCK: Working in main repository detected" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "" |
|
echo "Blocked command: $command" |
|
echo "" |
|
echo "Current context:" |
|
echo " Directory: $current_dir" |
|
echo " Branch: $current_branch" |
|
echo " Status: Main working tree (NOT a linked worktree)" |
|
echo "" |
|
echo "π‘οΈ WHY THIS IS BLOCKED:" |
|
echo " You are in the main repository directory, not a linked worktree." |
|
echo " Multiple agents may work here concurrently. Destructive operations" |
|
echo " in the main working tree risk overwriting uncommitted work." |
|
echo "" |
|
echo "β
TO PROCEED SAFELY - Create an isolated worktree:" |
|
echo "" |
|
echo " TASK='your-task-description'" |
|
echo " TASK_SLUG=\$(echo \"\$TASK\" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9._-' '-')" |
|
echo " BRANCH=\"agent/\$(date +%s)-\$TASK_SLUG\"" |
|
echo " REPO_ROOT=\"\$(git rev-parse --show-toplevel)\"" |
|
echo " WORKTREES_ROOT=\"\$(cd \"\$REPO_ROOT/..\" && pwd)/worktrees\"" |
|
echo " WORKTREE=\"\$WORKTREES_ROOT/\$(basename \$BRANCH)\"" |
|
echo "" |
|
echo " mkdir -p \"\$WORKTREES_ROOT\"" |
|
echo " DEFAULT_BRANCH=\$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p')" |
|
echo " DEFAULT_BRANCH=\${DEFAULT_BRANCH:-main}" |
|
echo " git worktree add -b \"\$BRANCH\" \"\$WORKTREE\" \"origin/\$DEFAULT_BRANCH\"" |
|
echo " cd \"\$WORKTREE\"" |
|
echo "" |
|
echo " # Now retry your operation - it will be safe!" |
|
echo "" |
|
echo "π Learn more: cat ~/.claude/CLAUDE.md" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
exit 1 |
|
fi |
|
|
|
# We're in a linked worktree - now check if it's an agent/* branch |
|
if [[ "$current_branch" == agent/* ]]; then |
|
# Safe context: Linked worktree AND agent/* branch - allow the operation |
|
exit 0 |
|
fi |
|
|
|
# We're in a linked worktree but NOT on an agent/* branch - provide guidance |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "β GLOBAL SAFETY BLOCK: Wrong branch for destructive operation" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "" |
|
echo "Blocked command: $command" |
|
echo "" |
|
echo "Current context:" |
|
echo " Branch: $current_branch" |
|
echo " Directory: $current_dir" |
|
echo " Status: Linked worktree (correct) but not on agent/* branch" |
|
echo "" |
|
echo "π‘οΈ WHY THIS IS BLOCKED:" |
|
echo " You're in a linked worktree, but not on an agent/* branch." |
|
echo " Destructive operations should only happen on agent/* branches" |
|
echo " to avoid affecting shared branches used by multiple developers." |
|
echo "" |
|
echo "β
TO PROCEED SAFELY:" |
|
echo "" |
|
echo " Option 1: Create a new agent/* branch from here:" |
|
echo " TASK='your-task-description'" |
|
echo " TASK_SLUG=\$(echo \"\$TASK\" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9._-' '-')" |
|
echo " git checkout -b \"agent/\$(date +%s)-\$TASK_SLUG\"" |
|
echo "" |
|
echo " Option 2: Create a fresh worktree with agent/* branch:" |
|
echo " (See ~/.claude/CLAUDE.md for full quick-start commands)" |
|
echo "" |
|
echo "π Learn more: cat ~/.claude/CLAUDE.md" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "" |
|
echo " # Now retry your operation - it will be safe!" |
|
echo "" |
|
echo "π Learn more: cat ~/.claude/CLAUDE.md" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
|
|
# Exit with error code to block the operation |
|
exit 1 |
|
fi |
|
|
|
# Additional safety: Block obviously dangerous commands |
|
if echo "$command" | grep -qE "rm -rf /|sudo rm -rf|:\(\)|fork bomb"; then |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "β BLOCKED: Extremely dangerous command pattern detected" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
echo "" |
|
echo "Command: $command" |
|
echo "" |
|
echo "This command pattern is blocked by global safety rules." |
|
echo "If you genuinely need to run this, please request explicit user approval." |
|
echo "" |
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ" |
|
exit 1 |
|
fi |
|
|
|
# All other commands pass through |
|
exit 0 |
|
EOF |
|
|
|
chmod +x "$CLAUDE_HOME/hooks/pre-bash.sh" |
|
log_success "Created $CLAUDE_HOME/hooks/pre-bash.sh" |
|
log_success "Made hook executable" |
|
|
|
echo "" |
|
separator |
|
echo "Step 4: Configuring global settings" |
|
separator |
|
|
|
if [ -f "$CLAUDE_HOME/settings.json" ]; then |
|
backup_if_exists "$CLAUDE_HOME/settings.json" |
|
log_warning "Existing settings.json found - creating merge template" |
|
|
|
cat > "$CLAUDE_HOME/settings.merge-required.json" << 'EOF' |
|
{ |
|
"hooks": { |
|
"PreToolUse": [ |
|
{ |
|
"matcher": "Bash", |
|
"command": "~/.claude/hooks/pre-bash.sh" |
|
} |
|
] |
|
}, |
|
"permissions": { |
|
"Bash": { |
|
"mode": "ask", |
|
"allowedCommands": [ |
|
"git status", |
|
"git branch", |
|
"git log", |
|
"git diff", |
|
"git worktree list", |
|
"git worktree add", |
|
"git fetch", |
|
"git push", |
|
"ls", |
|
"pwd", |
|
"cat", |
|
"grep", |
|
"find" |
|
] |
|
} |
|
} |
|
} |
|
EOF |
|
log_warning "Please manually merge settings.merge-required.json into settings.json" |
|
log_info "Add the 'hooks' section to register the pre-bash.sh hook" |
|
else |
|
cat > "$CLAUDE_HOME/settings.json" << 'EOF' |
|
{ |
|
"hooks": { |
|
"PreToolUse": [ |
|
{ |
|
"matcher": "Bash", |
|
"command": "~/.claude/hooks/pre-bash.sh" |
|
} |
|
] |
|
}, |
|
"permissions": { |
|
"Bash": { |
|
"mode": "ask", |
|
"allowedCommands": [ |
|
"git status", |
|
"git branch", |
|
"git log", |
|
"git diff", |
|
"git worktree list", |
|
"git worktree add", |
|
"git fetch", |
|
"git push", |
|
"ls", |
|
"pwd", |
|
"cat", |
|
"grep", |
|
"find" |
|
] |
|
} |
|
} |
|
} |
|
EOF |
|
log_success "Created $CLAUDE_HOME/settings.json" |
|
fi |
|
|
|
echo "" |
|
separator |
|
echo "Step 5: Creating helper commands" |
|
separator |
|
|
|
cat > "$CLAUDE_HOME/commands/create-worktree.md" << 'EOF' |
|
# Create Worktree Command |
|
|
|
Create a new git worktree for safe isolated development. |
|
|
|
## Usage |
|
/user:create-worktree [task-description] |
|
|
|
## What this does |
|
1. Prompts for a task description if not provided |
|
2. Creates a unique branch: agent/<timestamp>-<task> |
|
3. Creates worktree in ../worktrees/ directory |
|
4. Changes to the new worktree |
|
5. Confirms you're in a safe environment |
|
|
|
## Example |
|
/user:create-worktree fix-login-bug |
|
|
|
## Implementation |
|
```bash |
|
# Get task name (prompt if not provided) |
|
if [ -z "$1" ]; then |
|
read -p 'Task description: ' TASK_NAME |
|
else |
|
TASK_NAME="$1" |
|
fi |
|
|
|
# Sanitize task name into a slug |
|
TASK_SLUG=$(echo "$TASK_NAME" \ |
|
| tr '[:upper:]' '[:lower:]' \ |
|
| tr -cs 'a-z0-9._-' '-') |
|
|
|
# Create unique branch with timestamp |
|
BRANCH_NAME="agent/$(date +%s)-$TASK_SLUG" |
|
|
|
# Determine paths |
|
REPO_ROOT="$(git rev-parse --show-toplevel)" |
|
WORKTREES_ROOT="$(cd "$REPO_ROOT/.." && pwd)/worktrees" |
|
mkdir -p "$WORKTREES_ROOT" |
|
WORKTREE_PATH="$WORKTREES_ROOT/$(basename "$BRANCH_NAME")" |
|
|
|
# Detect default branch |
|
DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p') |
|
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main} |
|
|
|
# Fetch and create worktree |
|
git fetch origin |
|
git worktree add -b "$BRANCH_NAME" "$WORKTREE_PATH" "origin/$DEFAULT_BRANCH" |
|
|
|
# Move into worktree |
|
cd "$WORKTREE_PATH" |
|
|
|
# Log creation |
|
echo "$(date -Iseconds) | $BRANCH_NAME | $WORKTREE_PATH | $TASK_NAME" \ |
|
>> "$(git rev-parse --git-common-dir)/worktrees.log" |
|
|
|
# Confirm |
|
echo "β Created worktree: $WORKTREE_PATH" |
|
echo "β On branch: $BRANCH_NAME" |
|
git branch --show-current |
|
|
|
# Verify it's a linked worktree |
|
if [ "$(git rev-parse --git-dir)" = "$(git rev-parse --git-common-dir)" ]; then |
|
echo "β οΈ WARNING: This appears to be the main working tree, not a linked worktree" |
|
else |
|
echo "β Linked worktree verified" |
|
fi |
|
|
|
git worktree list |
|
``` |
|
EOF |
|
|
|
log_success "Created /user:create-worktree command" |
|
|
|
cat > "$CLAUDE_HOME/commands/cleanup-worktrees.md" << 'EOF' |
|
# Cleanup Worktrees Command |
|
|
|
List and optionally remove old/merged worktrees. |
|
|
|
## Usage |
|
/user:cleanup-worktrees |
|
|
|
## What this does |
|
1. Lists all worktrees |
|
2. Checks which agent/* branches are merged |
|
3. Offers to remove merged worktrees |
|
4. Cleans up stale worktree references |
|
|
|
## Implementation |
|
```bash |
|
echo "Current worktrees:" |
|
git worktree list |
|
|
|
echo "" |
|
|
|
# Detect default branch |
|
DEFAULT_BRANCH=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p') |
|
DEFAULT_BRANCH=${DEFAULT_BRANCH:-main} |
|
|
|
echo "Checking for merged agent branches (against $DEFAULT_BRANCH):" |
|
git branch --merged "$DEFAULT_BRANCH" | grep agent/ || echo " (none found)" |
|
|
|
echo "" |
|
echo "Worktree log entries:" |
|
if [ -f "$(git rev-parse --git-common-dir)/worktrees.log" ]; then |
|
tail -10 "$(git rev-parse --git-common-dir)/worktrees.log" |
|
else |
|
echo " (no log file found)" |
|
fi |
|
|
|
echo "" |
|
echo "Commands:" |
|
echo " - Prune stale references: git worktree prune" |
|
echo " - Remove specific worktree: git worktree remove <path>" |
|
echo " - List remote agent branches: git branch -r | grep agent/" |
|
``` |
|
EOF |
|
|
|
log_success "Created /user:cleanup-worktrees command" |
|
|
|
echo "" |
|
separator |
|
echo "Installation Complete! π" |
|
separator |
|
echo "" |
|
|
|
log_success "Global protection is now active for all Claude Code sessions" |
|
echo "" |
|
echo "What was installed:" |
|
echo " π $CLAUDE_HOME/CLAUDE.md" |
|
echo " - Worktree workflow documentation" |
|
echo " - Safety rules and rationale" |
|
echo "" |
|
echo " π $CLAUDE_HOME/hooks/pre-bash.sh" |
|
echo " - Enforcement layer" |
|
echo " - Blocks destructive ops outside worktrees" |
|
echo "" |
|
echo " βοΈ $CLAUDE_HOME/settings.json" |
|
echo " - Hook registration" |
|
echo " - Bash permissions configuration" |
|
echo "" |
|
echo " π οΈ $CLAUDE_HOME/commands/" |
|
echo " - /user:create-worktree helper" |
|
echo " - /user:cleanup-worktrees helper" |
|
echo "" |
|
|
|
if [ -d "$BACKUP_DIR" ]; then |
|
echo " πΎ Backups saved to:" |
|
echo " $BACKUP_DIR" |
|
echo "" |
|
fi |
|
|
|
separator |
|
echo "Testing Your Installation" |
|
separator |
|
echo "" |
|
echo "Test the protection layer:" |
|
echo "" |
|
echo " 1. cd to any git repository" |
|
echo " 2. Try: git reset --hard" |
|
echo " 3. Should be BLOCKED with safety message" |
|
echo "" |
|
echo " 4. Create a worktree:" |
|
echo " git worktree add -b agent/\$(date +%s)-test ../worktrees/test" |
|
echo "" |
|
echo " 5. cd ../worktrees/test" |
|
echo " 6. Try: git reset --hard" |
|
echo " 7. Should be ALLOWED (you're in a safe worktree)" |
|
echo "" |
|
|
|
separator |
|
echo "Next Steps" |
|
separator |
|
echo "" |
|
echo "β’ Start any Claude Code session - rules are automatically loaded" |
|
echo "β’ Use /user:create-worktree to quickly set up isolated environments" |
|
echo "β’ Read the docs: cat ~/.claude/CLAUDE.md" |
|
echo "β’ Check loaded config in Claude: /debug" |
|
echo "" |
|
|
|
log_info "For project-specific rules, add .claude/CLAUDE.md to each repo" |
|
log_info "Global rules will always apply + project rules layer on top" |
|
echo "" |
|
|
|
separator |
|
} |
|
|
|
# Run main installation |
|
main |