-
Notifications
You must be signed in to change notification settings - Fork 113
Description
Bug: Rules with specific event type are loaded for all hook events when tool is not Bash/Edit/Write
Summary
Rules configured with a specific event type (e.g., event: stop) are incorrectly loaded and evaluated for unrelated hook events (e.g., PreToolUse) when the tool being used is not Bash, Edit, Write, or MultiEdit.
Environment
- Plugin: hookify
- Files affected:
hooks/pretooluse.pycore/config_loader.py
Steps to Reproduce
- Create a hookify rule with
event: stop:
<!-- .claude/hookify.test-stop-only.local.md -->
---
name: test-stop-only
enabled: true
event: stop
action: block
conditions:
- field: transcript
operator: contains
pattern: hello
---
This message should ONLY appear on Stop events, not PreToolUse.- Start a Claude Code session and type "hello" in a message
- Observe that the warning appears when Claude uses tools like
Task,Read,Glob, etc.
Expected Behavior
Rules with event: stop should only be evaluated during Stop hook events, not during PreToolUse, PostToolUse, or UserPromptSubmit events.
Actual Behavior
Rules with event: stop are evaluated during PreToolUse events when the tool is anything other than Bash, Edit, Write, or MultiEdit (e.g., Task, Read, Glob, Grep, WebFetch, etc.).
Root Cause Analysis
File: hooks/pretooluse.py (lines 37-44)
event = None
if tool_name == 'Bash':
event = 'bash'
elif tool_name in ['Edit', 'Write', 'MultiEdit']:
event = 'file'
rules = load_rules(event=event) # event=None for other tools!For tools not in the explicit list, event remains None.
File: core/config_loader.py (lines 219-221)
# Filter by event if specified
if event: # BUG: When event=None, this entire block is skipped
if rule.event != 'all' and rule.event != event:
continueWhen event=None, the filter condition if event: evaluates to False, so no filtering occurs. All enabled rules are returned regardless of their event setting.
Suggested Fix
Change the filter logic in config_loader.py to always filter rules with specific events:
# Always filter by event - rules with specific events should only match that event
if rule.event != 'all':
if event is None or rule.event != event:
continueThis ensures:
- Rules with
event: allcontinue to match all events (unchanged) - Rules with specific events (e.g.,
stop,bash,file) only match when that exact event is requested - When
event=Noneis passed, onlyevent: allrules are loaded
Alternative Fix
Update pretooluse.py to use a default event type for unhandled tools:
event = 'tool' # Default for generic tools
if tool_name == 'Bash':
event = 'bash'
elif tool_name in ['Edit', 'Write', 'MultiEdit']:
event = 'file'This would require users to use event: tool or event: all for generic tool matching.
Impact
- Severity: Medium
- User Impact: Unexpected warnings/blocks during normal tool usage
- Workaround: Users must set
tool_matcherto explicitly filter, but this doesn't fully solve the issue for Stop events which have no tool_name
Related
- Affects all hook types that may pass
event=Nonetoload_rules()