The building blocks for every Claude Code hook. Learn these patterns, combine them for any use case.
#!/bin/bash
COMMAND=$(cat | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE 'rm\s+-rf'; then
echo "BLOCKED: rm -rf" >&2
exit 2
fi
exit 0
#!/bin/bash
COMMAND=$(cat | jq -r '.tool_input.command // empty')
case "$(echo "$COMMAND" | awk '{print $1}')" in
cat|ls|grep|find)
echo '{"decision":"approve","reason":"Read-only"}'
exit 0 ;;
esac
exit 0
#!/bin/bash
COMMAND=$(cat | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE 'git\s+commit'; then
echo "REMINDER: Did you run tests?" >&2
fi
exit 0
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
echo "[$(date -Iseconds)] $TOOL: ${CMD:0:80}" >> ~/.claude/activity.log
exit 0
#!/bin/bash
FILE=$(cat | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0
case "$FILE" in
*.py) python3 -m py_compile "$FILE" 2>&1 || echo "SYNTAX ERROR: $FILE" >&2 ;;
*.json) python3 -m json.tool "$FILE" >/dev/null 2>&1 || echo "BAD JSON: $FILE" >&2 ;;
esac
exit 0
#!/bin/bash
FILE=$(cat | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
if [[ "$FILE" == *".env" ]]; then
jq -n '{"decision":"block","reason":"Protected file"}'
exit 0
fi
exit 0
#!/bin/bash
TRACKER="$HOME/.claude/agent-count"
COUNT=$(cat "$TRACKER" 2>/dev/null || echo 0)
if [ "$COUNT" -ge 5 ]; then
echo "BLOCKED: Too many agents" >&2; exit 2
fi
echo $((COUNT + 1)) > "$TRACKER"
exit 0
#!/bin/bash
FILE=$(cat | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0
mkdir -p .claude/checkpoints
cp "$FILE" ".claude/checkpoints/$(basename "$FILE").$(date +%s).bak"
exit 0
#!/bin/bash
COMMAND=$(cat | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE 'git\s+push.*(main|master)'; then
echo "RULE: CLAUDE.md prohibits pushing to main" >&2
fi
exit 0
#!/bin/bash
INPUT=$(cat)
REASON=$(echo "$INPUT" | jq -r '.stop_reason // empty')
if [ "$REASON" = "error" ]; then
# Send notification
echo "Session ended with error" >> ~/.claude/alerts.log
fi
exit 0