hookify plugin: prompt-event rules never fire on Windows (cp1252 encoding) + wrong field name 'user_prompt' vs 'prompt'

Open 💬 0 comments Opened Jul 13, 2026 by UPSMAN84

Bug

The hookify plugin's UserPromptSubmit rules silently never fire. The hook returns {} and injects nothing, so user-defined prompt gates (e.g. "invoke skill X before debugging") are dead. Because exit code is 0 with valid JSON, there is no signal to the agent that a rule failed to load.

Reproduced on Windows 11, Claude Code 2.1.207, plugin version 0.1.0.

Two root causes in plugins/hookify/core/

1. Encoding — files opened without encoding='utf-8'

  • config_loader.py:251open(file_path, 'r')
  • rule_engine.py:212open(transcript_path, 'r')

On Windows the default codec is cp1252. Rule .local.md files are UTF-8 and commonly contain arrows, warning emoji, em-dashes. cp1252 cannot decode those bytes, so UnicodeDecodeError is caught in load_rule_file and the rule is silently dropped.

stderr from a real run:

Error: Malformed rule file .claude/hookify.warn-backup-before-edit.local.md: 'charmap' codec can't decode byte 0x8f in position 167: character maps to <undefined>

2. Wrong field name — kills every prompt rule

rule_engine.py:228:

elif field == 'user_prompt':
    # For UserPromptSubmit events
    return input_data.get('user_prompt', '')

Claude Code's UserPromptSubmit payload puts the prompt under the key prompt, not user_prompt. So field: user_prompt conditions always extract '', the regex never matches, and no rule fires. This affects all prompt-event rules regardless of the encoding fix.

Repro

PLUGIN_ROOT="$HOME/.claude/plugins/plugins/hookify"
echo '{"hook_event_name":"UserPromptSubmit","prompt":"debug this please","cwd":"."}' \
  | CLAUDE_PLUGIN_ROOT="$PLUGIN_ROOT" python3 "$PLUGIN_ROOT/hooks/userpromptsubmit.py"
# Before fix: stderr shows charmap codec errors, stdout = {}

With a rule whose frontmatter is:

event: prompt
conditions:
  - field: user_prompt
    operator: regex_match
    pattern: (?i)debug

Suggested fix

# config_loader.py:251
with open(file_path, 'r', encoding='utf-8') as f:

# rule_engine.py:212
with open(transcript_path, 'r', encoding='utf-8') as f:

# rule_engine.py:228 - accept the key Claude Code actually sends
elif field == 'user_prompt':
    return input_data.get('user_prompt') or input_data.get('prompt') or ''

The user_prompt or prompt fallback keeps existing user rule files (which the examples generate with field: user_prompt) working without requiring users to edit them.

Environment

  • OS: Windows 11 (cp1252 default locale)
  • Claude Code: 2.1.207
  • Plugin: hookify 0.1.0 (claude-code-plugins marketplace)

View original on GitHub ↗