Description
When running Claude Code in non-interactive prompt mode (-p), project-level hooks defined in .claude/settings.json (PreToolUse, PostToolUse) do not fire. This appears to affect all hook types, not just PermissionRequest (which is documented as not firing in -p mode).
Steps to Reproduce
- Create a project with
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(*)",
"hooks": [
{
"type": "command",
"command": "echo HOOK_FIRED >> .hook-breadcrumb.log",
"timeout": 5
}
]
}
]
}
}
- Run Claude in prompt mode:
CLAUDECODE= claude -p "Run 'echo hello' using Bash." \
--model haiku \
--dangerously-skip-permissions \
--output-format stream-json
- Check for
.hook-breadcrumb.log — it does not exist. The hook never fired.
Expected Behavior
PreToolUse and PostToolUse hooks should fire in -p mode, just as they fire in interactive mode. The documentation states PermissionRequest hooks don't fire in non-interactive mode, but does not mention this limitation for PreToolUse/PostToolUse.
Actual Behavior
No hooks fire at all in -p mode. Tested with:
--dangerously-skip-permissions and without
- Relative and absolute paths in hook commands
- Both
PreToolUse and PostToolUse matchers
Environment
- Claude Code: v2.1.41
- OS: Windows 11 (Git Bash / MSYS2)
- Node.js: v25.6.1
Impact
We use -p mode for automated behavioral testing (SBTF — Sovereign Behavioral Testing Framework). Hooks are the primary enforcement mechanism for governance gates (e.g., requiring verify.sh to pass before commits). Without hooks in -p mode, we had to fall back to git pre-commit hooks as a workaround.
Workaround
Git pre-commit hooks (.git/hooks/pre-commit) fire at the git level regardless of Claude Code mode. We use these for enforcement in automated test runs.
Description
When running Claude Code in non-interactive prompt mode (
-p), project-level hooks defined in.claude/settings.json(PreToolUse, PostToolUse) do not fire. This appears to affect all hook types, not just PermissionRequest (which is documented as not firing in-pmode).Steps to Reproduce
.claude/settings.json:{ "hooks": { "PreToolUse": [ { "matcher": "Bash(*)", "hooks": [ { "type": "command", "command": "echo HOOK_FIRED >> .hook-breadcrumb.log", "timeout": 5 } ] } ] } }CLAUDECODE= claude -p "Run 'echo hello' using Bash." \ --model haiku \ --dangerously-skip-permissions \ --output-format stream-json.hook-breadcrumb.log— it does not exist. The hook never fired.Expected Behavior
PreToolUse and PostToolUse hooks should fire in
-pmode, just as they fire in interactive mode. The documentation states PermissionRequest hooks don't fire in non-interactive mode, but does not mention this limitation for PreToolUse/PostToolUse.Actual Behavior
No hooks fire at all in
-pmode. Tested with:--dangerously-skip-permissionsand withoutPreToolUseandPostToolUsematchersEnvironment
Impact
We use
-pmode for automated behavioral testing (SBTF — Sovereign Behavioral Testing Framework). Hooks are the primary enforcement mechanism for governance gates (e.g., requiringverify.shto pass before commits). Without hooks in-pmode, we had to fall back to git pre-commit hooks as a workaround.Workaround
Git pre-commit hooks (
.git/hooks/pre-commit) fire at the git level regardless of Claude Code mode. We use these for enforcement in automated test runs.