Where do Claude Code hooks go? File locations and scope

Claude Code hooks go under the hooks key in a settings file: ~/.claude/settings.json for all your projects, .claude/settings.json for one project and your team, or .claude/settings.local.json for one project and only you. Plugins, skills and subagents can define hooks too. A hook in a standalone file doesn't load.

Every place a hook can live

LocationScopeShareable
~/.claude/settings.jsonAll your projectsNo, local to your machine
.claude/settings.jsonOne projectYes, commit it to the repo
.claude/settings.local.jsonOne projectNo, gitignored when Claude Code saves a setting to it
Managed policy settingsYour organizationYes, controlled by an admin
Plugin hooks/hooks.jsonWhile the plugin is enabledYes, bundled with the plugin
Skill frontmatterThe rest of the session once the skill is invokedYes, in the skill file
Subagent frontmatterWhile that subagent runsYes, in the subagent file

What a hook looks like in settings.json

Add a hooks key at the top level. Each event name is a key inside that one object, so if the file already has a hooks key, add your event beside the existing ones rather than replacing the block. This one shows a Mac notification when Claude needs you and formats files after edits:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Where the hook script goes

The JSON has to be in a settings file, but the script it runs can be anywhere. The docs' examples keep project scripts in .claude/hooks/ and point at them with ${CLAUDE_PROJECT_DIR}, the project root where the session started:

"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh"

Use that form or an absolute path to avoid a command not found error, and make the script executable with chmod +x.

How the files combine

  • Hooks add up. Hook entries merge across settings levels. User, project and local settings add their own without removing anyone else's, including managed ones.
  • disableAllHooks follows normal precedence. A false in a project's .claude/settings.json overrides a true in your user settings. Only managed settings can turn off managed hooks.
  • Trust comes first. In an interactive session, Claude Code holds back hooks from every settings file, your own included, until you accept the workspace trust dialog for the folder. A -p or SDK session never shows the dialog and treats the folder as trusted, so committed hooks run. Review a repo's .claude/ settings before scripting claude -p over code you didn't write.
  • Cloud sessions don't read your local ~/.claude/settings.json.

Other settings resolve differently from hooks. See which settings.json wins for the rest.

Check that the hook loaded

  1. Run /hooks. It lists every hook registered for the session, grouped by event.
  2. If yours is missing, check that it's under the hooks key, that the JSON has no trailing commas or comments, and that matcher is a single string. An array there marks the entry invalid in claude doctor.
  3. If it's listed but doesn't fire, check the matcher. Use | between tool names, as in "Edit|Write". Matchers are case-sensitive, and a misspelled tool name fails silently.
  4. Start with claude --debug to see each event, the matchers checked, and the hook's exit code and output.

A hook defined in a skill's frontmatter doesn't show in /hooks until that skill has been invoked in the session.

When to use a hook

Use a hook for anything that must happen every time. CLAUDE.md instructions are context that Claude tries to follow; a hook runs as a command at a fixed point whatever Claude decides. See why Claude Code ignores CLAUDE.md for how to tell which one a rule needs.

Rulebook, the Mac app we are building, lists the hook events and matcher counts in each settings file and has a switch for disableAllHooks. Editing the hooks themselves happens in its raw JSON view.

Questions

Can I put Claude Code hooks in their own file?
Not on their own. A hook in a standalone file doesn't load; hooks go under the hooks key in a settings file. The script a hook runs can live anywhere, and .claude/hooks/ is the path the docs use in their examples. Plugins are the exception, with hooks/hooks.json.
Should a hook go in settings.json or settings.local.json?
Use .claude/settings.json for a hook the whole team should run, since you commit it with the repo. Use .claude/settings.local.json for one only you need in that project, and ~/.claude/settings.json for one you want in every project.
Do hooks in different settings files override each other?
No. Hook entries merge across settings levels. User, project and local settings each add their own hooks. To turn them off, set disableAllHooks to true, which can't turn off managed hooks unless it's set in managed settings.
Why isn't my Claude Code hook running?
Run /hooks. If the hook isn't listed, check that it's under the hooks key, that the JSON is valid, and that matcher is a single string rather than an array. If it's listed but doesn't fire, the matcher is the usual cause: matchers are case-sensitive.
Do I need to restart Claude Code after adding a hook?
Normally no. Claude Code watches settings files and applies hook changes to the running session. If the hook doesn't appear in /hooks after a few seconds, restart the session.

Rulebook for Mac

Rulebook is a native Mac app we are building for these files. It shows every CLAUDE.md and settings.json level side by side, lets you drag a rule from one file to another, and edits permissions without hand-written JSON. See the features.

Rulebook is in development and not available yet.

Email us about the launch

Related guides