🤖 AI Tools
Claude Code Hooks Config Writer for Team Repos: settings.json PreToolUse and PostToolUse Matchers, Exit Code 2 Blocking, stdin JSON Parsing with jq, Auto Format After Edits, Stop Hook Loop Guard, and a Hook Test Plan
Write Claude Code hooks a team can commit: the right settings file scope, PreToolUse guards that block destructive shell commands and edits to protected paths with exit code 2 and a reason Claude can read, PostToolUse formatting on edited files, a Stop hook that checks work without looping forever, and a plan to test each hook before trusting it.
0Reviews
Prompt
Act as a developer productivity engineer who maintains Claude Code setups for a team, writes hooks as small shell or Python scripts under .claude/hooks, and treats every hook as code that runs with the developer's own permissions. Inputs: - Repo stack, package manager, formatter, linter, and test or typecheck commands: [RepoStack] - Commands or actions Claude must never run without asking: [BlockedActions] - Files or folders Claude must not edit: [ProtectedPaths] - What should happen automatically after edits or at the end of a task: [Automations] - Scope: personal only, shared with the team, or both: [Scope] - Output format: [Format] Generate: 1. Settings file choice from Scope: user settings in the home .claude folder, project settings in .claude/settings.json committed to git, or .claude/settings.local.json for personal overrides that stay out of git. 2. A hooks block in valid JSON: hook events as keys (PreToolUse, PostToolUse, Stop, and Notification or UserPromptSubmit only if Automations needs them), each with a matcher for tool names such as Bash or Edit|Write|MultiEdit, and command hooks pointing at scripts via the CLAUDE_PROJECT_DIR environment variable. 3. A PreToolUse guard for BlockedActions: read the JSON on stdin with jq, pull tool_input.command, match the dangerous patterns, print a reason to stderr, and exit 2 so the call is blocked and Claude sees why. Exit 0 lets the call through. 4. A path guard for ProtectedPaths on edit tools: read tool_input.file_path, block matches with exit 2 and a clear message. 5. PostToolUse automations from Automations and RepoStack: format only the edited file, keep output quiet, set a timeout, and never fail the session because a formatter is missing. 6. A Stop hook when Automations asks for a final check: run the typecheck or tests, exit 2 with the failures on stderr so Claude keeps working, and read stop_hook_active from stdin so it does not loop forever. 7. Each script in full with a shebang, set options, and a comment on what it reads and returns. Remind the team to make scripts executable. 8. A test plan: pipe sample JSON into each script by hand and check the exit code, then open the hooks menu in Claude Code to confirm they loaded, then try one blocked command in a scratch branch. 9. A security note: hooks run arbitrary commands with your account's access, so review hook changes in pull requests like any other code. Constraints: - Only use hook events, fields, and exit code behavior you are sure of; mark anything else "confirm in the current Claude Code hooks docs". - Quote paths that may contain spaces. No em dashes.
Instructions
Replace every [bracket] with your details before running. Works on ChatGPT, Claude, and Gemini.
Generated Output
This image was generated using the prompt above.

Examples
Example Input
RepoStack: TypeScript monorepo, pnpm, Prettier, ESLint, typecheck with pnpm -r typecheck BlockedActions: rm -rf on anything outside node_modules, git push --force, prisma migrate reset ProtectedPaths: .env files, prisma/migrations, pnpm-lock.yaml Automations: Prettier on every edited ts or tsx file; typecheck before Claude says it is done Scope: shared with the team Format: settings JSON, scripts, test plan
Example Output
1) FILE
Commit .claude/settings.json. Personal tweaks go in .claude/settings.local.json, which stays out of git.
2) SETTINGS
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-bash.sh" }] },
{ "matcher": "Edit|Write|MultiEdit",
"hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-paths.sh" }] }
],
"PostToolUse": [
{ "matcher": "Edit|Write|MultiEdit",
"hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format.sh", "timeout": 30 }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/typecheck.sh", "timeout": 180 }] }
]
}
}
3) guard-bash.sh
#!/usr/bin/env bash
# Reads PreToolUse JSON on stdin. Exit 2 blocks the command and shows stderr to Claude.
set -euo pipefail
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'git push (.* )?(--force|-f)( |$)|prisma migrate reset'; then
echo "Blocked by team hook: force push and migrate reset need a human. Ask the user." >&2
exit 2
fi
if echo "$cmd" | grep -Eq 'rm -rf' && ! echo "$cmd" | grep -Eq 'rm -rf (\./)?node_modules'; then
echo "Blocked by team hook: rm -rf is only allowed on node_modules." >&2
exit 2
fi
exit 0
4) guard-paths.sh
#!/usr/bin/env bash
set -euo pipefail
f=$(jq -r '.tool_input.file_path // empty')
case "$f" in
*.env|*.env.*|*/prisma/migrations/*|*pnpm-lock.yaml)
echo "Blocked by team hook: $f is protected. Explain the change to the user instead." >&2
exit 2 ;;
esac
exit 0
5) format.sh
#!/usr/bin/env bash
# PostToolUse: format only the edited file; never fail the session.
f=$(jq -r '.tool_input.file_path // empty')
case "$f" in
*.ts|*.tsx) pnpm exec prettier --write "$f" >/dev/null 2>&1 || true ;;
esac
exit 0
6) typecheck.sh
#!/usr/bin/env bash
# Stop: exit 2 sends errors back so Claude keeps working. stop_hook_active prevents a loop.
input=$(cat)
if [ "$(echo "$input" | jq -r '.stop_hook_active // false')" = "true" ]; then exit 0; fi
cd "$CLAUDE_PROJECT_DIR"
if ! out=$(pnpm -r typecheck 2>&1); then
echo "Typecheck failed. Fix these before finishing:" >&2
echo "$out" | tail -n 40 >&2
exit 2
fi
exit 0
Run: chmod +x .claude/hooks/*.sh
7) TEST PLAN
- echo '{"tool_input":{"command":"git push --force origin main"}}' | .claude/hooks/guard-bash.sh; echo $? -> 2
- echo '{"tool_input":{"command":"rm -rf node_modules"}}' | .claude/hooks/guard-bash.sh; echo $? -> 0
- echo '{"tool_input":{"file_path":"prisma/migrations/001/migration.sql"}}' | .claude/hooks/guard-paths.sh; echo $? -> 2
- Open the hooks menu in Claude Code and confirm four hooks are listed.
- In a scratch branch, ask Claude to force push and confirm it stops and explains.
8) SECURITY
Hooks run with your own access. Review .claude/hooks changes in pull requests like application code.