The 9 Best Claude Code Hooks to Set Up First
Everything else in your Claude Code setup is a request. CLAUDE.md asks, skills teach, commands prompt — and a model can decide, reasonably or not, to do something else. Hooks are the one layer that doesn't negotiate: shell commands the harness runs at fixed points in the loop, whose exit code can block a tool call outright. That makes them the right tool for a narrow, important set of jobs — and the wrong tool for most of what people try to use them for. Here are the nine worth having, ranked.
Our #1: format and lint on every edit
A PostToolUse hook on file edits that runs your formatter, then your linter, on the file that just changed. Set this up first, before anything else on the list.
It wins because it converts an instruction that gets ignored into a property that's always true. "Follow our formatting" in CLAUDE.md costs context on every request and still produces drift. A hook makes formatting non-negotiable, cleans up the diff noise that makes AI-written PRs annoying to review, and — the underrated part — feeds lint errors straight back to Claude, which then fixes them in the same turn instead of leaving them for CI. One line of config, immediate payoff, no downside.
2. Block dangerous commands before they run
A PreToolUse hook that inspects the pending command and refuses the ones you never want executed: recursive deletes outside the project, force-pushes to main, DROP against anything that isn't a local database, production deploy scripts. Exit code 2 blocks the call and tells Claude why, so it adapts rather than stalling. This is the hook that lets you loosen permissions everywhere else without anxiety.
3. Protect secrets from being read or written
A PreToolUse guard on file reads that denies .env, credential files, and key material, plus a PostToolUse scan that flags anything looking like a live key in a file Claude just wrote. Instructions are the wrong mechanism here — this is exactly the case where you want a guarantee rather than a strong preference.
4. Run the tests when the work stops
A Stop hook that runs your test suite, or the targeted subset, at the end of a session. It closes the most common gap between "Claude said it was done" and "it was done." Keep it fast — a suite that takes four minutes turns into a hook you disable by Thursday. Point it at the affected package, not the monorepo.
5. Type-check after edits
In a typed codebase, a PostToolUse type-check on changed files is the highest-signal feedback available, and it arrives while Claude still has full context on what it wrote. Cheap to add, catches a category of error that reads perfectly fine.
6. Tell you when Claude needs you
A Notification hook that pings your desktop, terminal bell, or phone when Claude is waiting on input. Sounds trivial; it's the hook that actually makes long agentic runs practical, because otherwise you either babysit the terminal or discover forty minutes later that it stopped to ask a yes/no question.
7. Load fresh context at session start
A SessionStart hook that injects the things that go stale: current branch and recent commits, open issues assigned to you, the current sprint, a fresh dependency list. Better than pasting the same orientation each morning, and better than putting facts in CLAUDE.md that will be wrong next week.
8. Enrich prompts automatically
A UserPromptSubmit hook can append context to what you type — the ticket referenced in your message, the current test failures, today's date. Use sparingly. Appending too much to every prompt is how a clever hook becomes a context tax you pay on every request.
9. Log what happened
A PostToolUse hook writing a JSONL line per tool call gives you a real record of what Claude did and when. Useful for teams that need an audit trail, and quietly useful for everyone else the first time you need to reconstruct a session that went sideways.
Which event to use
| Goal | Event | Can it block? |
|---|---|---|
| Stop an action happening | PreToolUse | Yes — exit 2 |
| React to a change | PostToolUse | No, but feeds output back |
| Add context to a request | UserPromptSubmit | Yes |
| Verify before finishing | Stop / SubagentStop | Yes — can force continuation |
| Get alerted | Notification | No |
| Set up a session | SessionStart | No |
Four rules that keep hooks from becoming the problem
- Fast or nothing. Hooks run inline. Anything over a couple of seconds on a frequent event makes the whole tool feel broken.
- Match narrowly. Scope matchers to the tools and file patterns you actually mean, or you'll run a Python formatter over a YAML file and spend an afternoon confused.
- Fail loudly, block rarely. Reserve blocking for genuine irreversibility. A hook that blocks on style will get switched off, taking your safety hooks with it.
- Remember they run with your permissions. A hook from someone else's repo is a shell command you agreed to run automatically. Read it.
Hooks live in settings.json — project-level for team-wide rules committed to git, user-level for personal ones. The JSON shape is fiddly enough that a bad matcher fails silently, which is why we built a free hooks builder that generates valid config for exactly these patterns.
Guarantees, plus the judgment to go with them. Hooks enforce; skills and agents decide. AgentsKit ships 103 skills, 89 agents, and 181 commands that encode how your review, testing, security, and release work should be done. See the full library →