You'll finish with: A working hook that blocks writes to secrets.
Time to build one. This blocks any attempt to write to files holding secrets — passwords, keys, API tokens.
How a hook answers
The dot in .claude makes the folder hidden. Mac: press Cmd + Shift + . in Finder to see it. Windows: tick "Hidden items" in the View menu.
Claude sends the hook a description of what it's about to do. The script looks at the filename and exits with a number: 0 means allow, 2 means block and tell Claude why.
JSON is a way of writing settings that programs can read. It's fussy about punctuation: every { needs its }, and a single missing comma or bracket makes the whole file unreadable — at which point your hook silently never runs. If something isn't working, paste the file into a free "JSON validator" in your browser; it will point at the exact character.
chmod +x means "let this file be run as a program". Mac and Linux need it before a script will do anything. On Windows you don't need it and it won't work — Windows decides differently. If you're on Windows and using Git Bash or WSL, run it there; in plain PowerShell, skip it.
.claude/hooks/protect-paths.sh inside it.chmod +x .claude/hooks/*.sh.claude/settings.json#!/bin/bash
# Blocks writes to secrets and your offers file. FAILS CLOSED.
INPUT=$(cat)
VALUE=""
PARSED=0
for PY in python3 python py; do
command -v "$PY" >/dev/null 2>&1 || continue
OUT=$(printf '%s' "$INPUT" | "$PY" -c "
import sys,json
try: print(json.load(sys.stdin).get('tool_input',{}).get('file_path','') or '')
except Exception: sys.exit(9)" 2>/dev/null) || continue
VALUE="$OUT"
PARSED=1
break
done
if [ "$PARSED" -ne 1 ]; then
echo "Blocked: this hook could not read its input, so it cannot tell whether" >&2
echo "the action is safe. It needs a working python3 (or python) on PATH." >&2
echo "Install Python, then try again. Blocking is deliberate — see the Gates course." >&2
exit 2
fi
FILE="$VALUE"
[ -z "$FILE" ] && exit 0
case "$FILE" in
*.env|*.env.*|*/secrets/*|*credentials*|*.pem|*.key)
echo "Blocked: $FILE holds secrets." >&2; exit 2 ;;
*/business/offers.md|business/offers.md)
echo "Blocked: pricing is owner-edit only." >&2; exit 2 ;;
esac
exit 0
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-paths.sh"
}]
}]
}
}
Ask Claude to edit a file called .env. It should refuse and say the hook blocked it.
If the edit goes through: check settings.json is inside .claude/, and that you ran the chmod line.