Compound cd && git commands in worktrees should not require approval

Status Open
Maintainer reply None cached
Activity 8 comments · opened Mar 2, 2026

Summary

When working in git worktrees, Claude Code flags cd <worktree-path> && git <command> as requiring user approval with the message:

Compound commands with cd and git require approval to prevent bare repository attacks

This is overly broad when the target directory is a known worktree of the current repository (e.g., under .claude/worktrees/).

The inconsistency

git -C <path> <command> does exactly the same thing as cd <path> && git <command> — both run git commands in a different directory. However, only the cd && form triggers the security approval. This means:

  • git -C /some/untrusted/path status — no approval required
  • cd /some/untrusted/path && git status — approval required

Both can target unexpected repositories equally, so the security check is inconsistent.

The worktree problem

When Claude Code spawns agents in worktrees (via isolation: "worktree" or EnterWorktree), the agent needs to run git commands in the worktree directory. Since the worktree is a subdirectory of the original repo (.claude/worktrees/<name>), it's not an "unexpected repository" — it's the same repo, just a different working copy.

Currently, every git command in a worktree triggers an approval prompt, which defeats the purpose of autonomous worktree-based agents.

Suggested fix

The security check should recognize worktree paths as safe. Specifically:

  1. If the target of cd is a git worktree of the current repository (verifiable via git worktree list), it should be treated the same as running git commands in the main working directory.
  2. Alternatively, if git -C is not going to be checked, cd && git shouldn't be checked either — the current inconsistency provides a false sense of security.

Reproduction

  1. Create a worktree: git worktree add .claude/worktrees/test-branch
  2. Have Claude Code run: cd .claude/worktrees/test-branch && git status
  3. Observe the approval prompt about bare repository attacks
  4. Compare with: git -C .claude/worktrees/test-branch status (no prompt)

Environment

  • Claude Code CLI
  • Any git repository using worktrees

View original on GitHub ↗

8 Comments

evoskamp · 6 months ago

Clarification: The cd && git pattern should really only be needed once when an agent first enters a worktree. After that initial cd, the working directory persists across Bash tool calls, so subsequent git commands don't need the cd prefix at all.

So the scope of this issue is narrower than originally described — it's specifically about that single initial directory change when entering a worktree. But the inconsistency between git -C and cd && git still stands.

github-actions[bot] · 6 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/28240
  2. https://github.com/anthropics/claude-code/issues/16561
  3. https://github.com/anthropics/claude-code/issues/29491

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

grokys · 6 months ago

Because of this issue, Claude Code requires confirmation for every git operation. Needless to say that's rather annoying.

mathiaswillburger · 5 months ago

Slows down the development process on Windows extremely as Claude asks for permission on almost every operation while researching, planning and reviewing

rcocks-hl · 5 months ago

Even without explicit extra worktrees, this is a significant problem when working in windows at a folder lower than the repo root.

If you work claude out of c:\repos\repo-root\product

When it wants to look at git history, it tries to operate at the pwd, and fails.

Then every command is cd c:\repos\repo-root && git ...

This requires permission prompting and makes researching and planning impossible to leave unattended.

jonnyom · 5 months ago

Can't agree more. This is a real frustration.

To get around it, I've built a hook that detects compound commands and redirects Claude to first cd into the directory, and then run the relevant commands.

It works surprisingly well, but frustrating that I had to do it at all.

The hook I've been using

{
	"type": "command",
	"command": "jq -r '.tool_input.command' | { IFS= read -r -d '' CMD || true; FIRST=$(echo \"$CMD\" | head -1); if echo \"$FIRST\" | grep -qE '^\\s*cd\\s+\\S+.*(&&|;|\\|\\|)'; then printf '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Do not combine cd with other commands using &&, ;, or ||. Run cd <dir> as a separate Bash command first, then run your actual command in a second Bash call.\"}}'; elif echo \"$FIRST\" | grep -qE '^\\s*cd\\s+' && [ \"$(printf '%s' \"$CMD\" | wc -l | tr -d ' ')\" -gt 1 ]; then printf '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"Do not combine cd with other commands on separate lines. Run cd <dir> as a separate Bash command first, then run your actual command in a second Bash call.\"}}'; fi; }",
	"timeout": 5,
	"statusMessage": "Checking for compound cd commands..."
}

I'd be willing to bet someone has a better solution for this 😄

nicolassenechal · 5 months ago

This is especially painful with isolation: "worktree" on subagents. The Agent tool creates a worktree at /tmp/wt-* specifically so the agent can work autonomously, but then every single operation in that worktree — reads, writes, cd && git status — requires manual approval because the path is outside the project directory.

This completely defeats the purpose of worktree isolation. The user ends up babysitting a stream of approval prompts for an agent that's supposed to be autonomous.

Worktree paths created by Claude Code itself should automatically inherit the parent project's permissions.

yurukusa · 5 months ago

Root cause diagnosis: Claude Code's permission matcher evaluates each rule against the entire command string. Bash(git *) matches git status but not cd /path && git status because the string doesn't start with git. This is a literal string match limitation, not a security decision — git -C /path status bypasses it entirely, as OP noted.
Hook workaround (PermissionRequest):
A PermissionRequest hook can parse compound commands, verify each part is safe, and auto-approve:

{
  "hooks": {
    "PermissionRequest": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/hooks/auto-approve-compound-git.sh"
      }]
    }]
  }
}

The hook splits on &&, ;, ||, checks each component against a safe list (cd, git status/log/diff/add/commit/...), and only approves if all parts are recognized:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
SAFE=true
while IFS= read -r part; do
  part=$(echo "$part" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
  [ -z "$part" ] && continue
  if echo "$part" | grep -qE '^(cd |git (status|log|diff|show|branch|tag|stash|add|commit|fetch|pull|checkout|switch|restore|merge|remote) )'; then
    continue
  fi
  if echo "$part" | grep -qE '^git (status|log|diff|show|branch|tag|stash|fetch|pull)$'; then
    continue
  fi
  SAFE=false; break
done < <(echo "$COMMAND" | tr '&' '\n' | tr ';' '\n' | tr '|' '\n')
if [ "$SAFE" = "true" ]; then
  jq -n '{hookSpecificOutput:{hookEventName:"PermissionRequest",permissionDecision:"allow",permissionDecisionReason:"Compound git: all parts are safe ops"}}'
fi

Key design choice: This is approve-based, not deny-based. If any component is unrecognized, the hook stays silent and falls through to the normal prompt — so it can't approve something dangerous by mistake.
For worktree users specifically: Since the initial cd <worktree-path> is always followed by git commands, this hook eliminates the approval prompt for the exact pattern described in this issue.
Full tested version with 13 tests: auto-approve-compound-git.sh