Claude Code hooks are shell commands you set up to run automatically at fixed points in a session, such as before Claude edits a file or when it's waiting for you. Unlike an instruction in a prompt or a CLAUDE.md file, which Claude reads and usually follows, a hook is executed by Claude Code every single time, so it's how you make a rule that can't be skipped.
If someone's told you to "just add a hook", this page is for you. I'll keep it to what a hook does for a business, the few worth setting up first, how to check one works, and where hooks stop being the right tool.
What a hook is, in plain English
Anthropic's hooks guide describes hooks as user-defined shell commands that Claude Code runs at specific points in its lifecycle, which gives you deterministic control: the action always happens, rather than relying on the model to choose to run it. Think of a hook as a house rule written into the building, not a note on the fridge.
Hooks live in a hooks block inside a settings file. Where you put that file decides who the hook applies to:
- ~/.claude/settings.json: every project on your computer, just for you.
- .claude/settings.json in a project folder: that project only, and it can be committed so your whole team gets it.
- .claude/settings.local.json: that project only, kept off the shared copy.
- Managed policy settings: organisation-wide, controlled by an admin.
- Inside a plugin, skill or subagent: hooks can ship bundled with Claude plugins and run while that plugin is switched on.
Hook or instruction: which one holds
This is the distinction that matters. Anthropic's best practices page splits every instruction into two kinds. Your prompt, CLAUDE.md and skills are read by the model, which decides what to do with them. It usually follows them, but there's no guarantee. A hook, a deny rule or a managed setting is something Claude Code has to execute every time, no matter what the model decided.
The page's own tip is the best test I've seen: if you keep adding emphasis to a CLAUDE.md line and Claude still misses it, that line belongs in a hook. It works the other way round too. If you've got hooks for things that are only preferences, delete them and let Claude decide.
Your first hook: a desktop alert
The walkthrough in the hooks guide builds a Notification hook, which pops up a desktop alert when Claude is waiting for you. It's harmless, useful straight away, and teaches you the whole process.
- Open ~/.claude/settings.json in a text editor. If the file doesn't exist, create it.
- Add a hooks block with a Notification event whose command shows a desktop alert. The guide gives a ready block for macOS (using osascript), Linux (using notify-send) and Windows (using PowerShell). The Mac command is below. You can also ask Claude to write the hook for you by describing what you want.
- If your file already has a hooks key, add Notification next to the events already there. Each event name sits inside the one hooks object, so don't replace the whole thing.
- Type /hooks at the Claude Code prompt. The hooks browser opens, and your hook should be listed under Notification.
- Press Esc, then press Shift+Tab until the status bar says manual mode is on. Ask Claude to do something that needs your permission, then switch to another window. A desktop notification should appear.
The Mac alert command used in the guide
osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'
The catch on a Mac: osascript sends notifications through the built-in Script Editor app. If Script Editor doesn't have notification permission, the command fails silently and macOS won't prompt you. The fix is to run osascript -e 'display notification "test"' once in Terminal, then open System Settings, then Notifications, find Script Editor and turn on Allow Notifications.
Two timing details from the guide explain when the alert fires. A permission prompt triggers it after it's waited about six seconds, and an idle prompt triggers it when Claude finished responding about 60 seconds ago and you haven't typed since.
Hooks worth adding next
Each of these has a ready-made block in the hooks guide. Here's what each one does for you, minus the code:
| Hook | Event | What it does for you |
|---|---|---|
| Desktop alert | Notification | Tells you Claude's waiting, so you can work on something else |
| Protected files | PreToolUse | Blocks edits to files like .env or anything in .git before they happen |
| Auto-format | PostToolUse, matched to Edit or Write | Runs a formatter (the guide uses Prettier) on every file Claude edits |
| Context after compaction | SessionStart, with a compact matcher | Puts key reminders back after a long conversation is summarised |
| Settings audit | ConfigChange | Logs every change to settings or skills files, for compliance |
The protected-files hook shows how blocking works. A small script checks the file Claude wants to edit against a list of protected names and exits with code 2. Exit code 2 is the block. Whatever the script prints, such as a line starting Blocked:, goes back to Claude as feedback, so it can change its approach instead of trying again.
Want AI doing the heavy lifting in your marketing?
I build the systems that handle the boring 80 percent, so you get your week back. Done properly, with the human kept in.
How to check a hook is working
Start with /hooks. If your hook isn't listed under the event you expected, Claude Code can't see it. If it's listed but never fires, the guide's checklist is short: matchers are case-sensitive, so Edit and edit aren't the same, and make sure you picked the right event, because PreToolUse fires before a tool runs and PostToolUse fires after.
Then test it on purpose. For the protected-files hook, ask Claude to add a comment to your .env file and watch it get blocked. For a formatting hook, success shows nothing in the conversation, so open the edited file and check it was reformatted.
Limits to know before you rely on one
- A PostToolUse hook can't undo anything, because the tool has already run. Use PreToolUse if you need to stop something.
- A hook that returns allow can't override deny rules in your settings. A hook that returns deny holds in every permission mode, even bypass mode.
- Several hooks on the same event run in parallel. One hook's deny doesn't stop its siblings running, and the most restrictive answer wins, in the order deny, defer, ask, allow.
- Stop hooks fire whenever Claude finishes responding, not only when a task is complete.
- Most hooks run a shell command, but there are four other types: http, mcp_tool, prompt and agent. Prompt and agent hooks ask another Claude model to judge, so they aren't deterministic, and the guide marks agent hooks experimental.
The event list is long. When I checked, the guide's table showed more than thirty events, from SessionStart to SessionEnd. You won't need most of them. And hooks aren't the answer to everything: if you want work split up and handed to a helper, that's what subagents are for, and if you want a second pair of eyes on finished work, Claude Code's review step does a job no hook can.
One opinion from me, plainly: hooks run shell commands with your own permissions. Only add hooks you understand, and never paste one from a stranger without knowing what its command does.
Where I'd start
I'd start with the notification hook and the protected-files block. The first saves you watching a terminal, and the second turns your "never touch this" list into something Claude Code enforces. I'd skip prompt and agent hooks until those two basics are working and you understand why each one fired.
If you'd like help setting up guardrails so Claude Code can't touch what it shouldn't, that's the kind of AI enablement I help with. I run AI training and workshops for teams, and help businesses with AI enablement and workflow automation, so the busywork runs itself and people get time back. Most of that work is with SaaS marketing teams and small businesses. Here's how we can work together.
Frequently asked questions
What are Claude Code hooks?
They're shell commands you configure in a settings file that Claude Code runs automatically at set points, such as before a tool runs, after a file edit or when Claude needs your input.
What's the difference between a hook and a CLAUDE.md instruction?
Claude reads a CLAUDE.md instruction and usually follows it, but it's the model's call. A hook is executed by Claude Code every time, whatever the model decides.
Where do I put Claude Code hooks?
In a hooks block inside a settings file: ~/.claude/settings.json for all your projects, .claude/settings.json to share with a project team, or .claude/settings.local.json for a project just for you.
How do I block Claude from editing a file?
Use a PreToolUse hook that checks the file path and exits with code 2. The block happens before the edit, and the script's message goes back to Claude as feedback.
How do I see which hooks are active?
Type /hooks in a session. It opens the hooks browser and lists each hook under its event.


