From 2ae581a732e7913111fd5224388395f581ae0686 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 22:48:36 +0000 Subject: [PATCH 1/3] fix: worktree support for git-guard, skill docs, and implementer agent Addresses #7 - three worktree support gaps: 1. git-guard.py: Parse -C from git commands so the commit-on-main check queries the correct worktree branch instead of always checking the main repo. Also anchor the commit regex to line start (^\s*git) to avoid false positives from heredoc content. 2. SKILL.md: Replace sibling directory pattern (../repo-feature) with internal .worktrees/BRANCH_NAME pattern that stays within Claude Code's permission scope. Use git -C instead of cd for worktree ops. 3. implementer.md: Replace "don't use git -C" guidance with "use git -C .worktrees/" for worktree operations. 4. .gitignore: Add .worktrees/ entry. Closes #7 https://claude.ai/code/session_0133wuXFfTNKJjnaTuc12Zco --- .gitignore | 3 + agents/implementer.md | 2 +- hooks/git-guard.py | 24 +++-- skills/using-git-worktrees/SKILL.md | 149 ++++++++++++---------------- 4 files changed, 85 insertions(+), 93 deletions(-) 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..cfe78f3 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 +- Use `git -C .worktrees/` when operating on a worktree from the main repo - 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..c91d50a 100755 --- a/hooks/git-guard.py +++ b/hooks/git-guard.py @@ -16,12 +16,21 @@ 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.""" + match = re.search(r'git\s+-C\s+(\S+)', command) + return match.group(1) if match else 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 +62,11 @@ def main(): sys.exit(2) # Block commits on main/master branch - if re.search(r'git\s+commit', command): - branch = get_current_branch() + # Only match "git commit" or "git -C commit" at the command level, + # not inside heredocs or string arguments + if re.search(r'^\s*git\s+(-C\s+\S+\s+)?commit', command, re.MULTILINE): + 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/using-git-worktrees/SKILL.md b/skills/using-git-worktrees/SKILL.md index 49f7a0c..57f9242 100644 --- a/skills/using-git-worktrees/SKILL.md +++ b/skills/using-git-worktrees/SKILL.md @@ -59,43 +59,37 @@ 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:** +**Verify worktree state (use `git -C` from the main repo):** ```bash -cd ../claude-code-workflows-feature-auth -``` - -**Verify worktree state:** - -```bash -# Confirm branch and clean state -git status +# Confirm branch and clean state in the worktree +git -C .worktrees/feature-auth status # Verify worktree list git worktree list ``` -**Checkpoint:** In worktree directory. Branch is correct. State is clean. +**Checkpoint:** Worktree exists. Branch is correct. State is clean. ### Phase 4: Chain to Implementation Planning @@ -114,23 +108,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 +145,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,29 +161,23 @@ 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 -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 +# Use git -C to run commands in worktree from the main repo +git -C .worktrees/branch-name status +git -C .worktrees/branch-name add . +git -C .worktrees/branch-name commit -m "feat: implement feature" +git -C .worktrees/branch-name push origin branch-name ``` ## Process Flow @@ -205,16 +187,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. **Verify worktree** - `git -C .worktrees/feature-name status`, `git worktree list` +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 +224,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 +247,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 +254,46 @@ $ 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 -$ git status +Phase 3 - VERIFY +$ git -C .worktrees/feature-auth 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 (use git -C .worktrees/feature-auth ...) → 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 +318,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 +328,19 @@ 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 -git add . -git commit -m "WIP: save work" +# Option 1: Commit or stash changes in the worktree +git -C .worktrees/branch-name add . +git -C .worktrees/branch-name 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 ``` From 82387f490faf5edc86602d45b7af4d50faab50d5 Mon Sep 17 00:00:00 2001 From: Tom Baker Date: Tue, 10 Feb 2026 01:06:27 -0500 Subject: [PATCH 2/3] fix: prefer cd into worktrees over git -C, update agent-team-development The original commit overcorrected toward git -C for all worktree operations. In practice, git -C triggers repeated permission approval prompts in Claude Code. Agents should cd into their worktree and work normally; git -C is reserved as a fallback. Changes: - implementer.md: "work from within worktree directly, fall back to git -C only when necessary" - using-git-worktrees SKILL.md: restore cd-based workflow for Phase 3, Working in Worktrees, example flow, and troubleshooting sections - agent-team-development SKILL.md: migrate from sibling directory pattern (../REPO-team-BRANCH-impl-N) to internal .worktrees/ pattern; add explicit cd-into-worktree instructions in implementer spawn prompt Co-Authored-By: Claude Opus 4.6 --- agents/implementer.md | 2 +- skills/agent-team-development/SKILL.md | 64 +++++++++++++++----------- skills/using-git-worktrees/SKILL.md | 41 ++++++++++------- 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/agents/implementer.md b/agents/implementer.md index cfe78f3..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 -- Use `git -C .worktrees/` when operating on a worktree from the main repo +- 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/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 57f9242..d20638d 100644 --- a/skills/using-git-worktrees/SKILL.md +++ b/skills/using-git-worktrees/SKILL.md @@ -79,17 +79,19 @@ git worktree add .worktrees/feature-auth feature-auth ### Phase 3: Verify Worktree -**Verify worktree state (use `git -C` from the main repo):** +**Navigate into worktree and verify state:** ```bash -# Confirm branch and clean state in the worktree -git -C .worktrees/feature-auth status +cd .worktrees/feature-auth -# Verify worktree list +# Confirm branch and clean state +git status + +# Verify worktree list (works from any worktree) git worktree list ``` -**Checkpoint:** Worktree exists. Branch is correct. State is clean. +**Checkpoint:** In worktree directory. Branch is correct. State is clean. ### Phase 4: Chain to Implementation Planning @@ -173,13 +175,16 @@ git worktree remove --force .worktrees/branch-name ### Working in Worktrees ```bash -# Use git -C to run commands in worktree from the main repo -git -C .worktrees/branch-name status -git -C .worktrees/branch-name add . -git -C .worktrees/branch-name commit -m "feat: implement feature" -git -C .worktrees/branch-name push origin branch-name +# 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 ``` +**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 @@ -188,7 +193,7 @@ git -C .worktrees/branch-name push origin branch-name 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 .worktrees/feature-name feature-name` -5. **Verify worktree** - `git -C .worktrees/feature-name status`, `git worktree list` +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 @@ -261,14 +266,15 @@ $ git worktree list → /home/user/project [main] → /home/user/project/.worktrees/feature-auth [feature-auth] -Phase 3 - VERIFY -$ git -C .worktrees/feature-auth status +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 (use git -C .worktrees/feature-auth ...) +→ Plan executes in isolated worktree directory → No conflicts with main workspace Phase 5 - CLEANUP (after merge) @@ -337,9 +343,10 @@ rm -rf .worktrees/branch-name ### "Cannot remove worktree with uncommitted changes" ```bash -# Option 1: Commit or stash changes in the worktree -git -C .worktrees/branch-name add . -git -C .worktrees/branch-name commit -m "WIP: save work" +# 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) git worktree remove --force .worktrees/branch-name From f4b32c2f38aeea6ee245c703cad6182e2d0f812f Mon Sep 17 00:00:00 2001 From: Tom Baker Date: Tue, 10 Feb 2026 01:13:16 -0500 Subject: [PATCH 3/3] fix: git-guard regex handles quoted paths and avoids heredoc false positives - parse_git_c_path: handle both quoted ("path with spaces") and unquoted paths in git -C arguments - commit detection: remove re.MULTILINE (which made ^ match every line, including heredoc content) and instead match git commit at string start or after shell operators (&&, ;, |) Co-Authored-By: Claude Opus 4.6 --- hooks/git-guard.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hooks/git-guard.py b/hooks/git-guard.py index c91d50a..7e1a8c1 100755 --- a/hooks/git-guard.py +++ b/hooks/git-guard.py @@ -18,8 +18,11 @@ def parse_git_c_path(command): """Extract the -C argument from a git command, if present.""" - match = re.search(r'git\s+-C\s+(\S+)', command) - return match.group(1) if match else None + # 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): @@ -62,9 +65,9 @@ def main(): sys.exit(2) # Block commits on main/master branch - # Only match "git commit" or "git -C commit" at the command level, - # not inside heredocs or string arguments - if re.search(r'^\s*git\s+(-C\s+\S+\s+)?commit', command, re.MULTILINE): + # 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"]: