Description
When approving Bash commands to add to the permission allow-list, Claude Code captures the entire literal command instead of extracting a sensible command pattern. This results in:
- Malformed/bloated settings files
- Permission rules that will never match future commands
- Multi-line commands getting split into nonsensical individual entries
Reproduction Steps
Case 1: Simple command with complex arguments
- Run a command like
gh issue create --title "..." --body "$(cat <<'EOF'...EOF)"
- Approve it for the allow-list
- Check
.claude/settings.local.json
Expected: Bash(gh issue create:*) or similar pattern
Actual: The entire literal command including the full heredoc body is stored verbatim (~2KB of text in one permission rule)
Case 2: Multi-line bash constructs
- Run a for-loop or similar multi-line bash construct
- Approve it
Actual result in settings:
"Bash(for i in {1..15})",
"Bash(do)",
"Bash(if tail -5 /tmp/claude/ng-serve.log)",
"Bash(then)",
"Bash(break)",
"Bash(fi)",
"Bash(done)"
These are fragments of a for-loop, not valid standalone commands.
Environment
- Claude Code Version: 2.1.25
- OS: macOS Darwin 25.2.0
Description
When approving Bash commands to add to the permission allow-list, Claude Code captures the entire literal command instead of extracting a sensible command pattern. This results in:
Reproduction Steps
Case 1: Simple command with complex arguments
gh issue create --title "..." --body "$(cat <<'EOF'...EOF)".claude/settings.local.jsonExpected:
Bash(gh issue create:*)or similar patternActual: The entire literal command including the full heredoc body is stored verbatim (~2KB of text in one permission rule)
Case 2: Multi-line bash constructs
Actual result in settings:
These are fragments of a for-loop, not valid standalone commands.
Environment