A two-part approach to get a prompt-free Claude Code experience while keeping safety guardrails.
Claude Code's --dangerously-skip-permissions flag disables ALL safety checks. But even with a granular allow list in settings.json, Claude's internal security heuristics (newline + # in Python scripts, string interpolation patterns, etc.) fire after the allow list and still prompt you on false positives.
Define exactly which commands are permitted. This is your security boundary.
{
"permissions": {
"defaultMode": "bypassPermissions",
"allow": [
"Read", "Write", "Edit", "Glob", "Grep",
"WebFetch", "WebSearch",
"Bash(git:*)",
"Bash(bundle:*)",
"Bash(bin/rspec:*)",
"Bash(ruby:*)",
"Bash(gem:*)",
"Bash(rails:*)",
"Bash(rspec:*)",
"Bash(srb:*)",
"Bash(brakeman:*)",
"Bash(packwerk:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(cat:*)",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(mkdir:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(rm:*)",
"Bash(touch:*)",
"Bash(ln:*)",
"Bash(chmod:*)",
"Bash(grep:*)",
"Bash(rg:*)",
"Bash(sed:*)",
"Bash(awk:*)",
"Bash(diff:*)",
"Bash(sort:*)",
"Bash(uniq:*)",
"Bash(wc:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(jq:*)",
"Bash(xargs:*)",
"Bash(echo:*)",
"Bash(pwd)",
"Bash(which:*)",
"Bash(test:*)",
"Bash(true)",
"Bash(false)",
"Bash(curl:*)",
"Bash(python3:*)",
"Bash(pip:*)",
"Bash(pip3:*)",
"Bash(/tmp:*)",
"Bash(timeout:*)",
"Bash(gtimeout:*)",
"Bash(open:*)",
"Bash(brew:*)",
"Bash(SKIP_COVERAGE=*:*)",
"Bash(SKIP_DB_INIT=*:*)",
"Bash(RAILS_ENV=*:*)",
"Bash(NODE_ENV=*:*)",
"Bash(gh:*)"
]
}
}Register both hooks on the Bash matcher:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/safe-rm.sh"
},
{
"type": "command",
"command": ".claude/hooks/safe-python.sh"
}
]
}
]
}
}safe-rm.sh— Interceptsrmcommands. Plainrmis rewritten to move files to~/.claude/trash/<date>/(auto-approved).rm -rfescalates to a prompt so you still approve destructive deletes.safe-python.sh— The catch-all auto-approver. Any Bash command that already cleared your allow list gets approved, bypassing Claude's internal security heuristics that would otherwise false-positive on things like Python comments or shell string interpolation.
{
"skipDangerousModePermissionPrompt": true
}Suppresses the startup warning about bypass mode.
Command invoked
→ Allow list check (settings.json)
→ BLOCKED if not in list (never reaches hooks)
→ PASSED → PreToolUse hooks fire:
→ safe-rm.sh: rewrites rm → trash, blocks rm -rf
→ safe-python.sh: auto-approves everything else
→ No prompt. Command runs.
The key insight: the allow list alone isn't enough because Claude's built-in security heuristics fire after the allow list. The safe-python.sh hook acts as a blanket auto-approve for anything that already cleared the whitelist — giving you a clean no-prompt experience without actually running --dangerously-skip-permissions.