Skip to content

Instantly share code, notes, and snippets.

@Dowwie
Created October 28, 2025 09:10
Show Gist options
  • Save Dowwie/86814ff865e867c797d06f182e6a94ba to your computer and use it in GitHub Desktop.
Save Dowwie/86814ff865e867c797d06f182e6a94ba to your computer and use it in GitHub Desktop.
Git Enforcement Strategy for Agents
#!/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

Git Enforcement Strategy - Summary

The Problem

Multiple AI agents (or AI + humans) working concurrently in the same repository risk overwriting each other's uncommitted work through destructive git operations like reset --hard or clean -fd.

The Solution: Worktree-Based Isolation

Every agent works in a physically separate directory (Git worktree) on its own agent/* branch.

Multi-Layer Enforcement Approach

Layer 1: Documentation (Guidance)

File: ~/.claude/CLAUDE.md

  • Loaded automatically for every Claude Code session
  • Explains the worktree workflow
  • Provides copy-paste commands
  • Details the "why" behind the rules

Layer 2: Active Enforcement (Prevention)

File: ~/.claude/hooks/pre-bash.sh

  • Intercepts every bash command before execution
  • Blocks destructive git operations if:
    • You're in the main working tree (not a linked worktree), OR
    • You're in a worktree but not on an agent/* branch
  • Provides context-specific error messages with exact remediation steps

Layer 3: Configuration (Integration)

File: ~/.claude/settings.json

  • Registers the pre-bash hook with Claude Code
  • Sets permission modes for bash commands
  • Ensures enforcement runs automatically

Layer 4: Automation (Convenience)

Commands:

  • /user:create-worktree - Quick worktree setup
  • /user:cleanup-worktrees - Manage old work

The Workflow

# 1. Start session - Create isolated environment
TASK="fix-login-bug"
git worktree add -b agent/$(date +%s)-$TASK ../worktrees/$TASK

# 2. Work freely - You're isolated, all operations safe
cd ../worktrees/$TASK
git reset --hard  # βœ… ALLOWED (in your worktree)

# 3. Complete - Push and cleanup
git push origin $(git branch --show-current)
# Ask user about PR/merge

Key Technical Patterns

  1. Slugification - Sanitize task names: "Fix Login" β†’ fix-login
  2. Default branch detection - Works with main, master, develop
  3. Git-common-dir - Proper logging path that works from any worktree
  4. Worktree verification - Compare git-dir vs git-common-dir (reliable)

Protection Scope

Global - Applies to:

  • βœ… All Claude Code sessions
  • βœ… All repositories on your workstation
  • βœ… All projects, everywhere

What Gets Blocked

❌ git reset --hard (in main repo or non-agent branch)
❌ git clean -fd (in main repo or non-agent branch)
❌ git push --force (requires consent)
❌ git checkout -- . (blanket operations)

What's Always Safe

βœ… All operations within your agent/* worktree
βœ… Read-only commands (status, log, diff)
βœ… Creating new worktrees
βœ… Committing, pushing (non-force)

Installation

One command:

bash setup-claude-worktree-protection.sh

Result: Every future Claude Code session automatically enforces worktree isolation.

Why This Works

  • Physical separation - Can't accidentally touch other agents' files
  • Active enforcement - Can't bypass even if you forget
  • Clear feedback - Errors tell you exactly what to do
  • Standard Git - No special tooling required
  • Defense-in-depth - Multiple layers catch violations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment