Claude Code hooks for teams: four hooks worth committing to your repo
Most teams start shaping Claude Code with instructions: a CLAUDE.md file that says "run the formatter," "never edit the lockfile," "use our logger." That works most of the time. But an instruction is something the model chooses to follow. On a long session, or a busy day, it sometimes doesn't. Hooks close that gap. A hook is a shell command that Claude Code runs automatically at a fixed point, such as after every file edit or before every command. It runs every time, whether or not the model remembers the rule. For a team, that changes what you can promise. "The agent should format its code" becomes "the agent's code is always formatted." Where team hooks live Hooks go in the same settings files as permissions. For a team, the one that matters is .claude/settings.json in the repository, committed like any other config. Everyone who clones the repo gets the same hooks. Personal hooks belong in ~/.claude/settings.json or the uncommitted .claude/settings.local.json. Organizations can also set hooks centrally through managed settings. Every hook has the same shape: an event, an optional matcher that narrows which tools it applies to, and the command to run. 1. Format every file the agent edits The simplest hook, and the one that removes the most review noise. It runs your formatter after every edit, so formatting never shows up in a code review again. This is the example from the Claude Code documentation, using Prettier: { "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" } ] } ] } } PostToolUse fires after a tool runs, and the Edit|Write matcher limits it to file edits. The hook receives details of the tool call as JSON on standard input, which is where jq reads the file path. Swap Prettier for whatever your team uses: ruff format, gofmt, rustfmt. 2. Protect files nobody should hand-edit Lockfiles, generated code, .env files and anything in .git/ should never be edited by an agent. A PreToolUse hook runs before the tool does, and if the script exits with code 2, Claude Code blocks the action. The message the script writes to standard error goes back to Claude, so it can adjust its approach instead of just failing. Save this as .claude/hooks/protect-files.sh and make it executable: #!/bin/bash INPUT=$(cat) FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/") for pattern in "${PROTECTED_PATTERNS[@]}"; do if [[ "$FILE_PATH" == *"$pattern"* ]]; then echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2 exit 2 fi done exit 0 Then register it: { "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" } ] } ] } } Because the script is committed in .claude/hooks/, the list of protected files is reviewed in pull requests like any other code. 3. Check risky shell commands with your own logic Permission rules in settings can deny commands by pattern, but they match the command text, and the same command can be written many ways. A PreToolUse hook on the Bash tool lets you inspect the full command with real logic before it runs: block rm -rf outside the build folder, stop pushes to the main branch, refuse anything that touches production config. The mechanics are the same as the file guard. Match on Bash, read .tool_input.command from the JSON input, and exit with code 2 and a reason to block it. Exit 0 means the hook has no objection; the normal permission rules still decide whether the command runs. One caution: a hook that inspects command text is a strong guardrail, not a security boundary. For limits that must hold no matter how a command is written, pair it with Claude Code's sandboxing and with access control (an agent can't touch production if the machine has no production credentials). 4. Restore key context after compaction When a session's context fills up, Claude Code compacts it: it summarizes the conversation to free space. Details can get lost in that summary, including the rule you stated an hour ago. A SessionStart hook with the compact matcher runs right after compaction, and whatever the command prints is added to Claude's context. The documentation's example: { "hooks": { "SessionStart": [ { "matcher": "compact", "hooks": [ { "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'" } ] } ] } } Replace the echo with a command that prints something dynamic, such as git log --oneline -5 for recent commits. Keep the always-true rules in your CLAUDE.md file; use this hook for the few reminders that matter most after a long session. A hook to be careful with: Stop It is tempting to add a Stop hook that runs the test suite and makes Claude keep working until it passes. Know two things first. Stop fires whenever Claude finishes responding, not only when a task is done, so a slow test suite will run constantly. And Claude Code overrides a Stop hook that blocks eight times in a row, so the script should read the stop_hook_active field from its input and exit early when it is true. For most teams, running tests in CI and in the pull request checks is simpler. Rolling hooks out to a team Start with the formatter hook. It is low risk and the benefit is visible on the first pull request. Add the file guard next, with a short list. Grow it when someone finds a file the agent shouldn't have touched. Commit scripts to .claude/hooks/ and register them in .claude/settings.json, so changes go through review. Make sure everyone has the tools the hooks call (jq, the formatter). For most events, a hook that can't find its command fails as a non-blocking error, so the rule silently stops applying. Use claude --debug when a hook doesn't seem to run; it logs each hook's execution. Instructions and hooks together Hooks are for the rules that must always hold. Everything else, such as how your codebase is organized, which patterns to follow and who owns what, belongs in the instructions the agent reads at the start of each session. That is what we built AI Workspace Builder for. You upload the docs your team already has, and it generates the CLAUDE.md, a profile for each role, and an install script in 15 to 30 seconds, using your own Anthropic API key. There's one free trial workspace, no card required. Our free templates include a shared settings file you can add these hooks to.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to