--settings flag merges instead of overriding hooks configuration
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When using the --settings flag with a custom settings JSON file, the hooks configuration merges with the existing ~/.claude/settings.json instead of overriding it. This makes it impossible to disable specific hooks (like Stop hooks) on a per-invocation basis.
Actual behavior:
- Hooks from
~/.claude/settings.jsonare still executed even when providing--settings custom.jsonwith empty or modified hooks arrays - The custom settings appear to merge/append rather than replace the original hooks
Impact:
This prevents use cases like:
- Temporarily disabling notification/sound hooks for batch processing
- Running Claude programmatically without triggering user-facing hooks
- Testing different hook configurations without modifying the global settings file
What Should Happen?
The --settings flag should either:
Option A (Preferred): Completely override the default ~/.claude/settings.json for the current invocation
- Custom settings file becomes the sole source of configuration
- No merging with default settings
- Clear documentation that
--settingsis a full override
Option B (Alternative): Provide a --no-hooks or --skip-hooks flag
- Explicit way to disable hooks for specific invocations
- Simpler than managing custom settings files
- Useful for programmatic/batch usage
Error Messages/Logs
No error messages - the behavior is silent. The hooks from the default settings file simply execute despite the custom settings specifying empty hooks.
Steps to Reproduce
- Create a settings file at
~/.claude/settings.jsonwith a Stop hook:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "/path/to/some-notification-hook.sh"
}
]
}
]
}
}
- Create a custom settings file at
/tmp/custom-settings.jsonwith empty Stop hooks:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": []
}
]
}
}
- Run a command with the custom settings:
claude --settings /tmp/custom-settings.json --print "Say hello"
- Expected: The notification hook should NOT execute (empty hooks array in custom settings)
- Actual: The notification hook DOES execute (hooks merged from both files)
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.0.37 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
Workaround
The only current workaround is to temporarily backup and modify ~/.claude/settings.json:
import shutil
from pathlib import Path
settings_path = Path.home() / ".claude" / "settings.json"
backup_path = Path.home() / ".claude" / "settings.json.backup"
# Backup original
shutil.copy2(settings_path, backup_path)
# Modify settings
# ... (remove unwanted hooks)
# Run claude command
subprocess.run(["claude", "--print", "prompt"])
# Restore original
shutil.copy2(backup_path, settings_path)
backup_path.unlink()
This is error-prone and requires careful cleanup even on exceptions/timeouts.
Documentation Gap
The current documentation for --settings doesn't clarify:
- Whether settings merge or override
- How nested structures (like hooks arrays) are handled
- The precedence/priority of different configuration sources
Suggested Fix
If the merge behavior is intentional, consider adding:
--settings-overrideflag for full replacement behavior--no-hooksflag for disabling all hooks--skip-hook <pattern>flag for selective hook disabling- Clear documentation about merge vs override semantics
Use Case
This issue arose when building a YouTube transcript summarization tool that calls Claude programmatically. The tool needs to avoid triggering sound/notification hooks during batch processing, while preserving those hooks for interactive use.
---
Related: This may affect other nested configuration structures (MCP servers, tools, etc.) but hooks are the most critical for programmatic usage.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗