PreToolUse/PostToolUse hooks don't fire in non-interactive -p mode
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-permissionsand without- Relative and absolute paths in hook commands
- Both
PreToolUseandPostToolUsematchers
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.
4 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Your matcher syntax is invalid:
Bash(*)isn't a valid pattern. The(*)makes it an invalid regex (quantifier with nothing to repeat), so it silently matches nothing. Instead use:See https://code.claude.com/docs/en/hooks#matcher-patterns
Thanks @dicksontsai — good catch on the matcher syntax, that was definitely wrong in my repro.
That said, the underlying question still stands: do PreToolUse/PostToolUse hooks fire in non-interactive
-pmode? Issue #20063 reports the same behavior (hooks not running in headless mode) and is still open.Our workaround (git pre-commit hooks) works fine for enforcement, but it'd be great to get confirmation on whether
-pmode hook support is intended or a known limitation.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.