#!/usr/bin/env bash # protect-paths.sh · .acme/hooks/protect-paths.sh # QRefAI AI Coding Field Guide, Part 2 (Q2.5) # # A PreToolUse hook handler. This is the ONE place an exit code matters: # exit 2 → BLOCK the tool call (the edit is denied) # exit 0 → allow # Keep it FAST (well under ~200ms) — it runs before every Edit/Write. # # Claude Code passes the tool call as JSON on stdin. We extract the target path # and deny edits to protected trees (infra, compliance, etc.). Tune PROTECTED. set -euo pipefail # Protected path fragments. An edit whose target matches any of these is denied. PROTECTED_REGEX='(^|/)(infra|compliance|\.github/workflows|managed-settings)(/|$)' # Read the hook payload from stdin. payload="$(cat)" # Pull the file path the tool wants to touch. Prefer jq; fall back to grep. target="" if command -v jq >/dev/null 2>&1; then target="$(printf '%s' "$payload" | jq -r '.tool_input.file_path // .tool_input.path // empty')" else target="$(printf '%s' "$payload" | grep -oE '"(file_path|path)"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | sed -E 's/.*:"([^"]+)"/\1/')" fi # No path resolved → allow (nothing to protect against here). [ -z "$target" ] && exit 0 if printf '%s' "$target" | grep -Eq "$PROTECTED_REGEX"; then echo "BLOCKED: '$target' is in a protected path. Edits here require a human change via PR." >&2 exit 2 # <-- deny the tool call fi exit 0