Configuring hooks-config.json vs hooks-config.local.json in Claude Code: Team Defaults vs Personal Overrides
Claude Code uses hooks-config.json for team-wide defaults committed to the repository, while hooks-config.local.json provides git-ignored personal overrides that take precedence when loaded by the hook runtime.
The shanraisshan/claude-code-best-practice repository implements a sophisticated hook system that balances team consistency with individual developer preferences. Understanding how to configure hooks-config.json versus hooks-config.local.json allows teams to share standardized auditory feedback and automation while letting developers silence specific hooks locally without polluting the shared codebase.
Understanding the Two Configuration Files
Team-Wide Defaults with hooks-config.json
The .claude/hooks/config/hooks-config.json file serves as the shared baseline for all repository collaborators. Because this file is committed to version control, it ensures every team member experiences consistent hook behavior—such as audio notifications on SessionStart or TaskCompleted events. The JSON structure contains Boolean flags for every hook event, including disableLogging, disablePreToolUseHook, disablePermissionRequestHook, and disableSessionStartHook.
Personal Overrides with hooks-config.local.json
The .claude/hooks/config/hooks-config.local.json file is git-ignored and resides only on individual workstations. Developers create this file to override specific flags from the team config without modifying the shared repository. According to the implementation in hooks.py, this local file loads after the team configuration, meaning any keys defined here take precedence while unspecified flags inherit the team defaults.
Configuration Precedence and Merge Logic
The hook runtime implemented in .claude/hooks/scripts/hooks.py follows a specific loading order to resolve configuration conflicts. First, the system loads hooks-config.json to establish the baseline. Then, if present, it loads hooks-config.local.json and merges the two dictionaries, with local values overwriting team values for any matching keys.
This merge strategy enables partial overrides. For example, if the team config enables SessionStart sounds but a developer wants to silence only that specific hook while keeping all other team defaults, they only need to specify "disableSessionStartHook": true in their local file.
Practical Configuration Examples
Team-wide configuration (committed to .claude/hooks/config/hooks-config.json):
{
"disableLogging": false,
"disablePreToolUseHook": false,
"disablePermissionRequestHook": false,
"disablePostToolUseHook": false,
"disablePostToolUseFailureHook": false,
"disableUserPromptSubmitHook": false,
"disableNotificationHook": false,
"disableStopHook": false,
"disableSubagentStartHook": false,
"disableSubagentStopHook": false,
"disablePreCompactHook": false,
"disableSessionStartHook": false,
"disableSessionEndHook": false,
"disableSetupHook": false,
"disableTeammateIdleHook": false,
"disableTaskCompletedHook": false,
"disableConfigChangeHook": false,
"disableWorktreeCreateHook": false,
"disableWorktreeRemoveHook": false,
"disableInstructionsLoadedHook": false
}
Personal override (created locally at .claude/hooks/config/hooks-config.local.json):
{
"disableLogging": true,
"disablePostToolUseHook": true,
"disableSessionStartHook": true
}
Global hook disable (in .claude/settings.local.json for complete silence):
{
"disableAllHooks": true
}
Key Files and Implementation Details
| File | Purpose | Location |
|---|---|---|
hooks-config.json |
Team-wide default configuration | .claude/hooks/config/hooks-config.json |
hooks-config.local.json |
Personal overrides (git-ignored) | .claude/hooks/config/hooks-config.local.json |
hooks.py |
Runtime implementation that merges configs and executes hooks | .claude/hooks/scripts/hooks.py |
settings.local.json |
Global disable switch for all hooks | .claude/settings.local.json |
CLAUDE.md |
Architecture documentation for the hooks system | CLAUDE.md (Hooks System section) |
HOOKS-README.md |
Detailed usage guide for hook configuration | .claude/hooks/HOOKS-README.md |
Summary
hooks-config.jsonprovides version-controlled team defaults for Claude Code hook behavior, ensuring consistent automation across all collaborators.hooks-config.local.jsonoffers git-ignored personal overrides that take precedence over team settings, allowing individual developers to customize their experience without modifying shared files.- The merge logic in
hooks.pyloads team configuration first, then applies local overrides, enabling partial customization while inheriting unspecified defaults. - For complete silence, use
.claude/settings.local.jsonwith"disableAllHooks": trueto bypass both configuration files entirely.
Frequently Asked Questions
What happens if I only specify some keys in hooks-config.local.json?
The hook runtime merges your local file with the team defaults. Any keys you define in hooks-config.local.json override the team values, while all unspecified flags inherit the settings from hooks-config.json. This partial override mechanism lets you silence specific hooks like SessionStart while keeping all other team-defined behaviors intact.
Can I completely disable all hooks without editing the config files?
Yes. Create or edit .claude/settings.local.json in your workspace root and add "disableAllHooks": true. This global flag supersedes both hooks-config.json and hooks-config.local.json, effectively preventing the hook runtime in hooks.py from executing any hook actions. This is particularly useful for CI environments or performance-critical sessions.
Why is hooks-config.local.json git-ignored while hooks-config.json is committed?
The repository intentionally tracks hooks-config.json to ensure every team member shares the same baseline hook behavior, creating a consistent development environment. Conversely, hooks-config.local.json is listed in .gitignore because personal preferences—such as disabling audio notifications for specific events—vary by developer and should not be forced upon the entire team or clutter the repository history.
Where does the actual hook execution logic reside?
The runtime implementation lives in .claude/hooks/scripts/hooks.py. This Python script reads and merges the JSON configurations from both hooks-config.json and hooks-config.local.json, then executes the appropriate actions—such as playing audio files—for each enabled hook event. The script handles the precedence logic where local settings override team defaults during the configuration merge phase.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →