Denies rm -rf on the root or home folder, force-pushes and hard resets before Bash runs them.
WHEN TO USE IT
When you let Claude run shell commands and want a floor under the worst ones, whatever permission mode you are in.
Formats change. If a field stops working, check it against Anthropic's documentation.
Read it before you install it. A hook runs with your user account's permissions, on its own, every time it is triggered. That is what makes it useful, and why a stranger's should never be installed unread — this one included.
Matcher Bash: this runs before every shell command Claude Code issues.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
The second documented way to block: exit 0 and print a permissionDecision of deny as JSON. Same effect as exit 2, but the reason is structured.
#!/usr/bin/env bash
# Denies a short list of commands that are hard to undo.
command -v jq >/dev/null || { echo "block-dangerous: install jq" >&2; exit 2; }
cmd=$(jq -r '.tool_input.command // empty')
deny() {
jq -n --arg reason "$1" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: $reason
}
}'
exit 0
}
[[ "$cmd" =~ rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*f?[[:space:]]+(/|~|\$HOME)([[:space:]]|$) ]] \
&& deny "Refusing to delete the root or home directory."
[[ "$cmd" =~ git[[:space:]]+push.*(--force|-f)([[:space:]]|$) ]] \
&& deny "Force-push is blocked. Ask the user to run it themselves."
[[ "$cmd" =~ git[[:space:]]+reset[[:space:]]+--hard ]] \
&& deny "git reset --hard discards work. Ask the user first."
exit 0
A pattern list is a floor, not a sandbox — a determined command can be spelled another way. Keep permission prompts on for anything that matters.
mkdir -p .claude/hooks
mv block-dangerous.sh .claude/hooks/
chmod +x .claude/hooks/block-dangerous.sh
MORE HOOKS
The authority
Anthropic's documentation for Hooks
This template is a starting point. Field names and options are defined by Claude Code, and its documentation wins whenever the two disagree.
https://code.claude.com/docs/en/hooks