Permissions control which tools Claude can use. Hooks let you control how those tools are used, including blocking edits to specific files.
PreToolUse Guard Hook
A PreToolUse hook runs before Claude executes a tool. If it exits with code 2, the action is blocked.
{
"hooks": {
"PreToolUse": [{
"matcher": "Edit|Write",
"command": "bash -c 'FILE=$(echo $TOOL_INPUT | jq -r .file_path); case \"$FILE\" in *.env*|*lock.json|*.git/*) exit 2;; esac'"
}]
}
}
This blocks Claude from editing:
.env,.env.local,.env.productionpackage-lock.json,yarn.lock- Anything inside
.git/
Other Guard Patterns
Block generated files:
case "$FILE" in
*/generated/*|*/dist/*|*/__generated__/*) exit 2;;
esac
Block production configs:
case "$FILE" in
*/production.yml|*/prod.config.*|*Dockerfile.prod) exit 2;;
esac
Why Hooks Over Permissions
Permissions are coarse-grained (you can allow or deny Edit globally). Hooks give you file-level granularity. Claude can still edit code files freely while being blocked from touching sensitive paths.
Tip
Hook blocks are deterministic. They always run, unlike prompt instructions which Claude might occasionally ignore.