[MODEL] Should not encourage shell command substitution `$(...)` in system prompt — causes permission approval dialog spam
Preflight Checklist
- [x] I have searched existing issues for similar behavior reports
- [x] This report does NOT contain sensitive information (API keys, passwords, etc.)
Type of Behavior Issue
Claude's behavior changed between sessions
What You Asked Claude to Do
Recent versions of Claude Code increasingly use shell command substitution ($(...))
in generated commands — apparently encouraged by the system prompt itself.
However, any command containing $(...) always triggers a manual permission
approval dialog, regardless of allow rules defined in settings.json.
This results in approval dialog spam for otherwise-allowed commands.
Background
Claude Code's permission system correctly treats shell command substitution as
requiring explicit approval (reasonable from a security perspective).
However, the system prompt appears to encourage patterns like:
git commit --message "$(cat << 'EOF'
...
EOF
)"
This means even commands covered by allow rules (e.g. Bash(git commit:*))
always prompt for manual approval, defeating the purpose of the allowlist entirely.
What Claude Actually Did
I have added Bash(git commit:*) to the allow list in ~/.claude/settings.json
- Let Claude Code to perform a git commit
- Claude generates:
git commit -m "$(cat << 'EOF' ...)" - Permission approval dialog appears every time, despite the allowlist entry
Expected Behavior
Claude Code should use allowlist-compatible command forms, e.g.:
git commit -m "1st line" -m "2nd line"
This would respect the Bash(git commit:*) allow rule and avoid unnecessary prompts.
Files Affected
Permission Mode
Accept Edits was ON (auto-accepting changes)
Can You Reproduce This?
Yes, every time with the same prompt
Steps to Reproduce
As written in the What Claude Actually Did section
Claude Model
Opus
Relevant Conversation
Impact
Low - Minor inconvenience
Claude Code Version
v2.1.70
Platform
Anthropic API
Additional Context
When asked to stop using this pattern, Claude Code itself acknowledged that shell command substitution ($(...)) is directed by its system prompt.
Adding an explicit instruction to CLAUDE.md to avoid shell command substitution
partially mitigates the issue, but it requires strong phrasing and is not reliable —
the system prompt appears to override user instructions in CLAUDE.md.
The proper fix should be in the system prompt itself.
6 Comments
This is very annoying! Would be super neat with a bundled
Gittool to make this smoother.Agreed. It's basically impossible to let it go off and explore a repository nowadays without having to babysit all the shell commands it wants to run.
Agreed. This is so annoying that I am actively searching how to get Claude code (Opus) to stop using
$()This happens because there are two separate enforcement layers, and they don't talk to each other:
Bash(git commit:*)works here.$(), backticks, newlines in compound commands). These fire after the allowlist check passes, and there's no setting to suppress them.The system prompt explicitly tells the model to use the heredoc
$(cat << 'EOF'...)pattern for commit messages. So the model is doing exactly what it's told — and then the safety heuristic blocks it. The model's own instructions conflict with the safety layer.Workaround that actually works: Add this to your project's
CLAUDE.md:This isn't 100% reliable (CLAUDE.md is guidance, not enforcement), but it reduces heredoc commits significantly. If you need it enforced, a PreToolUse:Bash hook can intercept and reject commands containing
$(before they hit the safety heuristic:Exit code 2 = hard block. The model will retry with a simpler command form.
Running 1,000+ hours of autonomous Claude Code sessions, I've found that any rule you need reliably enforced should be in a hook, not just in CLAUDE.md or settings. The permission/heuristic system is pattern-matching text; hooks are executing code.
This is very annoying!
I keep hitting Yes all day long!!!
<img width="346" height="168" alt="Image" src="https://github.com/user-attachments/assets/0600e97b-1491-43d6-b23a-ede3cf863032" />
This can also happen when backticks are used for formatting messages rather than actual command substitutions:
<img width="1904" height="752" alt="Image" src="https://github.com/user-attachments/assets/9d7acc1c-f88f-4b43-b074-bc8d9ecda1b8" />