diff --git a/.gitignore b/.gitignore index 8652b42..2c82157 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ node_modules/ # Python __pycache__/ *.pyc + +# Git worktrees +.worktrees/ diff --git a/agents/implementer.md b/agents/implementer.md index 61ceb17..1de5a52 100644 --- a/agents/implementer.md +++ b/agents/implementer.md @@ -15,7 +15,7 @@ Check for existing project conventions first. If none: - Never source venv activate scripts - use `uv run` or `poetry run` ### Git -- Don't use `git -C /path` when already in that directory +- When assigned a worktree, work from within it directly (cd into it). Fall back to `git -C` only when necessary - Create feature branches for changes (never commit to main) - Conventional commits: `feat: description` or `fix: description` diff --git a/hooks/git-guard.py b/hooks/git-guard.py index 4c92168..7e1a8c1 100755 --- a/hooks/git-guard.py +++ b/hooks/git-guard.py @@ -16,12 +16,24 @@ import subprocess -def get_current_branch(): - """Get current git branch name.""" +def parse_git_c_path(command): + """Extract the -C argument from a git command, if present.""" + # Handle both quoted and unquoted paths: git -C "path with spaces" or git -C path + match = re.search(r'git\s+-C\s+(?:"([^"]+)"|(\S+))', command) + if match: + return match.group(1) if match.group(1) else match.group(2) + return None + + +def get_current_branch(cwd=None): + """Get current git branch name, optionally for a specific directory.""" try: + cmd = ["git"] + if cwd: + cmd += ["-C", cwd] + cmd += ["rev-parse", "--abbrev-ref", "HEAD"] result = subprocess.run( - ["git", "rev-parse", "--abbrev-ref", "HEAD"], - capture_output=True, text=True, timeout=5 + cmd, capture_output=True, text=True, timeout=5 ) return result.stdout.strip() if result.returncode == 0 else None except Exception: @@ -53,8 +65,11 @@ def main(): sys.exit(2) # Block commits on main/master branch - if re.search(r'git\s+commit', command): - branch = get_current_branch() + # Match git commit at the start of the command or after shell operators (&&, ;, |) + # but NOT inside heredocs or string arguments (no re.MULTILINE — ^ only matches string start) + if re.search(r'(?:^|&&|;|\|)\s*git\s+(-C\s+(?:"[^"]+"|(\S+))\s+)?commit', command): + worktree_path = parse_git_c_path(command) + branch = get_current_branch(cwd=worktree_path) if branch in ["main", "master"]: print("❌ BLOCKED: Cannot commit directly to main/master branch", file=sys.stderr) print("", file=sys.stderr) diff --git a/skills/agent-team-development/SKILL.md b/skills/agent-team-development/SKILL.md index 00462c9..2fb27f1 100644 --- a/skills/agent-team-development/SKILL.md +++ b/skills/agent-team-development/SKILL.md @@ -76,18 +76,20 @@ SETUP WORKTREE SETUP ====================================================================== 5. Create git worktrees for each implementer: - Pattern: ../REPO-team-BRANCH-impl-N + Pattern: .worktrees/team-BRANCH-impl-N - Example for repo "my-app" on branch "feature/auth": - ../my-app-team-feature-auth-impl-1 - ../my-app-team-feature-auth-impl-2 - ../my-app-team-feature-auth-impl-3 + Example for branch "feature/auth": + .worktrees/team-feature-auth-impl-1 + .worktrees/team-feature-auth-impl-2 + .worktrees/team-feature-auth-impl-3 Commands: - git worktree add ../REPO-team-BRANCH-impl-1 HEAD - git worktree add ../REPO-team-BRANCH-impl-2 HEAD + git worktree add .worktrees/team-feature-auth-impl-1 HEAD + git worktree add .worktrees/team-feature-auth-impl-2 HEAD ... + Ensure `.worktrees/` is in `.gitignore`. + DELEGATE MODE ====================================================================== 6. Enter delegate mode (Shift+Tab) @@ -151,9 +153,10 @@ FINAL ====================================================================== 17. Lead performs final staff-code-reviewer (opus) on entire implementation 18. Clean up worktrees: - git worktree remove ../REPO-team-BRANCH-impl-1 - git worktree remove ../REPO-team-BRANCH-impl-2 + git worktree remove .worktrees/team-BRANCH-impl-1 + git worktree remove .worktrees/team-BRANCH-impl-2 ... + git worktree prune 19. Create PR (follow repo template if exists) 20. Invoke /pr-merge workflow (or user invokes manually) ``` @@ -170,20 +173,26 @@ from the shared task list, implement them, and commit your work. Working directory: [WORKTREE_PATH] Feature branch: [BRANCH_NAME] +IMPORTANT: cd into your worktree directory and work from there. +All git and file operations should happen from within the worktree. +Only fall back to `git -C` if you cannot cd into the worktree. + ## Workflow -1. Check the shared task list for unclaimed tasks -2. Claim a task by marking it in_progress -3. Before starting: git pull to get latest changes -4. Implement the task following its acceptance criteria -5. Run tests and verify your changes -6. Commit with conventional message: feat|fix|refactor: description -7. Push to remote: git push -8. Mark the task as ready for review (add "READY FOR REVIEW" to task) -9. Wait for reviewer feedback via mailbox -10. If reviewer requests fixes: fix, commit, push, notify reviewer -11. Once approved: move to next task +1. cd into your worktree: cd [WORKTREE_PATH] +2. Check the shared task list for unclaimed tasks +3. Claim a task by marking it in_progress +4. Before starting: git pull to get latest changes +5. Implement the task following its acceptance criteria +6. Run tests and verify your changes +7. Commit with conventional message: feat|fix|refactor: description +8. Push to remote: git push +9. Mark the task as ready for review (add "READY FOR REVIEW" to task) +10. Wait for reviewer feedback via mailbox +11. If reviewer requests fixes: fix, commit, push, notify reviewer +12. Once approved: move to next task ## Guidelines +- Work from within your worktree directory (cd into it first) - Follow project conventions from CLAUDE.md - Write tests as specified in the task's testing approach - Keep commits focused on the task @@ -285,21 +294,22 @@ Options: ### Creation (during setup) ```bash -# Get repo name and branch for naming -REPO_NAME=$(basename "$(git rev-parse --show-toplevel)") +# Get branch name for worktree naming BRANCH_NAME=$(git branch --show-current | tr '/' '-') -# Create worktrees -git worktree add "../${REPO_NAME}-team-${BRANCH_NAME}-impl-1" HEAD -git worktree add "../${REPO_NAME}-team-${BRANCH_NAME}-impl-2" HEAD +# Create worktrees inside .worktrees/ +git worktree add ".worktrees/team-${BRANCH_NAME}-impl-1" HEAD +git worktree add ".worktrees/team-${BRANCH_NAME}-impl-2" HEAD # Add impl-3 if 5+ tasks + +# Ensure .worktrees/ is in .gitignore ``` ### Cleanup (during final) ```bash -git worktree remove "../${REPO_NAME}-team-${BRANCH_NAME}-impl-1" -git worktree remove "../${REPO_NAME}-team-${BRANCH_NAME}-impl-2" +git worktree remove ".worktrees/team-${BRANCH_NAME}-impl-1" +git worktree remove ".worktrees/team-${BRANCH_NAME}-impl-2" # Remove impl-3 if it exists git worktree prune ``` diff --git a/skills/using-git-worktrees/SKILL.md b/skills/using-git-worktrees/SKILL.md index 49f7a0c..d20638d 100644 --- a/skills/using-git-worktrees/SKILL.md +++ b/skills/using-git-worktrees/SKILL.md @@ -59,39 +59,35 @@ git branch -a | grep feature-name git branch feature-name ``` -**Create worktree with smart directory naming:** +**Create worktree inside the repo's `.worktrees/` directory:** ```bash -# Sibling directory pattern: ../repo-feature-name -# Example: claude-code-workflows → ../claude-code-workflows-feature-auth +# Internal directory pattern: .worktrees/BRANCH_NAME +# Example: .worktrees/feature-auth -cd /home/tbaker/workspace/claude-code-workflows -git worktree add ../claude-code-workflows-feature-auth feature-auth +git worktree add .worktrees/feature-auth feature-auth ``` **Directory naming convention:** -- Pattern: `../REPO_NAME-BRANCH_NAME` -- Example: `../claude-code-workflows-context-recovery` -- Keeps worktrees organized as siblings to main repo -- Easy to identify and clean up later +- Pattern: `.worktrees/BRANCH_NAME` +- Example: `.worktrees/feature-auth` +- Stays inside the repo, respecting Claude Code's permission scoping +- No additional user approval needed for file operations +- Ensure `.worktrees/` is in `.gitignore` **Checkpoint:** Worktree created. Directory exists. -### Phase 3: Navigate and Verify +### Phase 3: Verify Worktree -**Navigate to worktree:** +**Navigate into worktree and verify state:** ```bash -cd ../claude-code-workflows-feature-auth -``` - -**Verify worktree state:** +cd .worktrees/feature-auth -```bash # Confirm branch and clean state git status -# Verify worktree list +# Verify worktree list (works from any worktree) git worktree list ``` @@ -114,23 +110,17 @@ The implementation plan will execute in this isolated worktree, preventing any c **When work is merged or PR is created:** -1. **Return to main repo:** - -```bash -cd /home/tbaker/workspace/claude-code-workflows -``` - -2. **Remove worktree:** +1. **Remove worktree:** ```bash # Remove worktree directory and unregister it -git worktree remove ../claude-code-workflows-feature-auth +git worktree remove .worktrees/feature-auth # Or if directory was manually deleted: git worktree prune ``` -3. **Delete branch (if merged):** +2. **Delete branch (if merged):** ```bash # Delete local branch @@ -157,13 +147,13 @@ git worktree list ```bash # Create worktree for new branch -git worktree add ../repo-branch-name branch-name +git worktree add .worktrees/branch-name branch-name # Create worktree for existing branch -git worktree add ../repo-existing-branch existing-branch +git worktree add .worktrees/existing-branch existing-branch # Create worktree and new branch from specific commit -git worktree add -b new-branch ../repo-new-branch abc123 +git worktree add -b new-branch .worktrees/new-branch abc123 ``` ### Managing Worktrees @@ -173,31 +163,28 @@ git worktree add -b new-branch ../repo-new-branch abc123 git worktree list # Remove specific worktree -git worktree remove ../repo-branch-name +git worktree remove .worktrees/branch-name # Remove worktree (if already deleted manually) git worktree prune # Remove worktree with uncommitted changes (force) -git worktree remove --force ../repo-branch-name +git worktree remove --force .worktrees/branch-name ``` ### Working in Worktrees ```bash -# Navigate to worktree -cd ../repo-branch-name - -# All git commands work normally +# Work from inside the worktree directory +cd .worktrees/branch-name git status git add . git commit -m "feat: implement feature" git push origin branch-name - -# Return to main repo -cd /home/tbaker/workspace/claude-code-workflows ``` +**Note:** Prefer working from within the worktree. Use `git -C .worktrees/branch-name` only as a fallback (e.g., from the lead orchestrator when agents are unavailable). + ## Process Flow ### Starting Feature Work with Worktree @@ -205,16 +192,15 @@ cd /home/tbaker/workspace/claude-code-workflows 1. **Verify main repo state** - `git status` shows clean 2. **Check branch availability** - `git branch -a | grep feature-name` is empty 3. **Create feature branch** - `git branch feature-name` -4. **Create worktree** - `git worktree add ../repo-feature-name feature-name` -5. **Navigate to worktree** - `cd ../repo-feature-name` -6. **Verify worktree** - `git status`, `git worktree list` -7. **Chain to writing-plans** - Ready for implementation planning +4. **Create worktree** - `git worktree add .worktrees/feature-name feature-name` +5. **Navigate to worktree** - `cd .worktrees/feature-name`, verify with `git status` +6. **Chain to writing-plans** - Ready for implementation planning ### Verification at Each Phase - After SAFETY CHECKS: Main clean, branch name available - After CREATE: Worktree exists, branch created -- After NAVIGATE: In worktree directory, correct branch +- After VERIFY: Worktree on correct branch, state is clean - After CHAIN: Implementation plan can execute in isolation ## Integration with Other Skills @@ -243,18 +229,19 @@ cd /home/tbaker/workspace/claude-code-workflows ❌ Creating worktree when main repo is dirty ❌ Using branch name that already exists -❌ Creating worktree in subdirectory of main repo -❌ Forgetting to navigate to worktree after creation +❌ Creating worktrees as sibling directories outside the repo (breaks permission scoping) +❌ Forgetting to verify worktree state after creation ❌ Leaving worktrees around after feature is merged ❌ Using worktree for quick branch switches (just use `git switch`) ❌ Creating nested worktrees +❌ Forgetting to add `.worktrees/` to `.gitignore` ## Success Criteria ✅ Main repo state is clean before worktree creation ✅ Branch name is unique (doesn't exist locally or remotely) -✅ Worktree created in sibling directory with clear naming -✅ Successfully navigated to worktree directory +✅ Worktree created inside `.worktrees/` directory +✅ `.worktrees/` is in `.gitignore` ✅ Worktree list shows both main and new worktree ✅ Implementation plan chains after worktree setup ✅ Cleanup performed after feature merge/completion @@ -265,7 +252,6 @@ cd /home/tbaker/workspace/claude-code-workflows SCENARIO: Implementing new authentication feature with parallel agents Phase 1 - SAFETY CHECKS -$ cd /home/tbaker/workspace/claude-code-workflows $ git status → On branch main, working tree clean ✓ $ git branch -a | grep feature-auth @@ -273,48 +259,47 @@ $ git branch -a | grep feature-auth Phase 2 - CREATE $ git branch feature-auth -$ git worktree add ../claude-code-workflows-feature-auth feature-auth +$ git worktree add .worktrees/feature-auth feature-auth → Preparing worktree (new branch 'feature-auth') → Checking out files: 100% done $ git worktree list - → /home/tbaker/workspace/claude-code-workflows [main] - → /home/tbaker/workspace/claude-code-workflows-feature-auth [feature-auth] + → /home/user/project [main] + → /home/user/project/.worktrees/feature-auth [feature-auth] -Phase 3 - NAVIGATE -$ cd ../claude-code-workflows-feature-auth +Phase 3 - NAVIGATE & VERIFY +$ cd .worktrees/feature-auth $ git status → On branch feature-auth, nothing to commit, working tree clean ✓ Phase 4 - CHAIN TO PLANNING → Invoke dev-workflow:writing-plans → Create implementation plan for authentication feature -→ Plan executes in isolated worktree +→ Plan executes in isolated worktree directory → No conflicts with main workspace Phase 5 - CLEANUP (after merge) -$ cd /home/tbaker/workspace/claude-code-workflows -$ git worktree remove ../claude-code-workflows-feature-auth +$ git worktree remove .worktrees/feature-auth $ git branch -d feature-auth $ git worktree list - → /home/tbaker/workspace/claude-code-workflows [main] + → /home/user/project [main] → Worktree cleaned up ✓ ``` ## Directory Naming Examples ``` -Main repo: /home/tbaker/workspace/my-app -Worktrees: - ../my-app-feature-auth → Authentication feature - ../my-app-fix-login-bug → Bug fix branch - ../my-app-refactor-api → Refactoring work - ../my-app-add-tests → Test additions - -Main repo: /home/tbaker/workspace/claude-code-workflows -Worktrees: - ../claude-code-workflows-context-recovery - ../claude-code-workflows-pr-merge-improvements - ../claude-code-workflows-new-skill +Main repo: /home/user/workspace/my-app +Worktrees (inside .worktrees/): + .worktrees/feature-auth → Authentication feature + .worktrees/fix-login-bug → Bug fix branch + .worktrees/refactor-api → Refactoring work + .worktrees/add-tests → Test additions + +Main repo: /home/user/workspace/claude-code-workflows +Worktrees (inside .worktrees/): + .worktrees/context-recovery + .worktrees/pr-merge-improvements + .worktrees/new-skill ``` ## Troubleshooting @@ -339,7 +324,7 @@ git fetch origin git worktree list # Remove if stale -git worktree remove ../repo-branch-name +git worktree remove .worktrees/branch-name # Or prune all stale worktrees git worktree prune @@ -349,21 +334,20 @@ git worktree prune ```bash # Check if directory is a worktree -git worktree list | grep directory-name +git worktree list | grep branch-name # If not a worktree, remove or use different name -rm -rf ../directory-name +rm -rf .worktrees/branch-name ``` ### "Cannot remove worktree with uncommitted changes" ```bash -# Option 1: Commit or stash changes -cd ../repo-branch-name +# Option 1: Commit or stash changes from within the worktree +cd .worktrees/branch-name git add . git commit -m "WIP: save work" # Option 2: Force remove (loses changes) -cd /home/tbaker/workspace/main-repo -git worktree remove --force ../repo-branch-name +git worktree remove --force .worktrees/branch-name ```