Summary
The hookify plugin (plugins/hookify/) fails to import its own engine on every hook invocation when installed via the marketplace. Each PreToolUse/PostToolUse/Stop/UserPromptSubmit event emits:
Hookify import error: No module named 'hookify'
Because the hook scripts catch the ImportError and sys.exit(0), the failure is non-fatal but silent: no user rule is ever evaluated, so the plugin is effectively inert while spamming this systemMessage.
- Plugin version:
0.1.0
- Installed commit:
ca9f6045fc90c8244f9e787fb57d54b380f9a27c
- Platform: Windows 11, Python 3.13
- Install path:
~/.claude/plugins/cache/claude-code-plugins/hookify/0.1.0/
Root cause
The hook entry scripts (hooks/pretooluse.py etc.) and core/rule_engine.py import via the top-level package name:
from hookify.core.config_loader import load_rules
from hookify.core.rule_engine import RuleEngine
To make that resolve, hooks/*.py add os.path.dirname(CLAUDE_PLUGIN_ROOT) to sys.path, assuming the plugin directory is literally named hookify (true in-repo: plugins/hookify/). But the marketplace installer places the plugin under a version directory:
.../claude-code-plugins/hookify/0.1.0/ <- CLAUDE_PLUGIN_ROOT
core/ hooks/ matchers/ utils/ <- no top-level `hookify` package
So dirname(CLAUDE_PLUGIN_ROOT) is .../hookify (the version container), not a directory that contains an importable hookify package. import hookify therefore fails, and so does every hookify.core.* import — including the cross-import inside core/rule_engine.py.
Reproduction
cd ~/.claude/plugins/cache/claude-code-plugins/hookify/0.1.0
CLAUDE_PLUGIN_ROOT="$(pwd)" echo '{}' | python3 hooks/pretooluse.py
# => {"systemMessage": "Hookify import error: No module named 'hookify'"}
Suggested fixes (any one)
-
Make imports root-relative. Since hooks/*.py already add CLAUDE_PLUGIN_ROOT to sys.path, change from hookify.core.X -> from core.X in the 4 hook scripts and in core/rule_engine.py (lines importing hookify.core.config_loader). Self-contained, no path assumptions.
-
Ship a package marker. Add plugins/hookify/__init__.py and have the installer preserve a hookify-named package dir, so import hookify resolves regardless of the version-dir layout.
-
Don't assume the plugin dir name. The sys.path bootstrap in hooks/*.py hardcodes the assumption that the plugin folder is named hookify; make the engine importable by relative path instead.
Workaround (for affected users)
Drop a one-file shim that re-points a hookify package at the plugin root (the scripts already put the root on sys.path):
# .../hookify/0.1.0/hookify/__init__.py
import os
_PLUGIN_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _PLUGIN_ROOT not in __path__:
__path__.append(_PLUGIN_ROOT)
After this, all four hooks load and rules evaluate correctly. (Lost on plugin reinstall/update.)
Summary
The hookify plugin (
plugins/hookify/) fails to import its own engine on every hook invocation when installed via the marketplace. Each PreToolUse/PostToolUse/Stop/UserPromptSubmit event emits:Because the hook scripts catch the
ImportErrorandsys.exit(0), the failure is non-fatal but silent: no user rule is ever evaluated, so the plugin is effectively inert while spamming thissystemMessage.0.1.0ca9f6045fc90c8244f9e787fb57d54b380f9a27c~/.claude/plugins/cache/claude-code-plugins/hookify/0.1.0/Root cause
The hook entry scripts (
hooks/pretooluse.pyetc.) andcore/rule_engine.pyimport via the top-level package name:To make that resolve,
hooks/*.pyaddos.path.dirname(CLAUDE_PLUGIN_ROOT)tosys.path, assuming the plugin directory is literally namedhookify(true in-repo:plugins/hookify/). But the marketplace installer places the plugin under a version directory:So
dirname(CLAUDE_PLUGIN_ROOT)is.../hookify(the version container), not a directory that contains an importablehookifypackage.import hookifytherefore fails, and so does everyhookify.core.*import — including the cross-import insidecore/rule_engine.py.Reproduction
Suggested fixes (any one)
Make imports root-relative. Since
hooks/*.pyalready addCLAUDE_PLUGIN_ROOTtosys.path, changefrom hookify.core.X->from core.Xin the 4 hook scripts and incore/rule_engine.py(lines importinghookify.core.config_loader). Self-contained, no path assumptions.Ship a package marker. Add
plugins/hookify/__init__.pyand have the installer preserve ahookify-named package dir, soimport hookifyresolves regardless of the version-dir layout.Don't assume the plugin dir name. The
sys.pathbootstrap inhooks/*.pyhardcodes the assumption that the plugin folder is namedhookify; make the engine importable by relative path instead.Workaround (for affected users)
Drop a one-file shim that re-points a
hookifypackage at the plugin root (the scripts already put the root onsys.path):After this, all four hooks load and rules evaluate correctly. (Lost on plugin reinstall/update.)