hookify plugin: Import error due to CLAUDE_PLUGIN_ROOT not available as env var

Resolved 💬 2 comments Opened Mar 1, 2026 by cardinalconseils Closed Mar 29, 2026

Bug Description

All 4 hookify Python hook scripts (pretooluse.py, posttooluse.py, stop.py, userpromptsubmit.py) fail with:

Hookify import error: No module named 'core'

This surfaces as a systemMessage on every Stop event (and potentially other events):

"Stop says: Hookify import error: No module named 'hookify'"

Root Cause

The hook scripts rely on CLAUDE_PLUGIN_ROOT being available as an environment variable:

# hooks/pretooluse.py (and all other hook scripts)
PLUGIN_ROOT = os.environ.get('CLAUDE_PLUGIN_ROOT')
if PLUGIN_ROOT and PLUGIN_ROOT not in sys.path:
    sys.path.insert(0, PLUGIN_ROOT)

However, Claude Code only interpolates ${CLAUDE_PLUGIN_ROOT} in the command string of hooks.json:

"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.py"

The command string interpolation works — the correct Python script is found and executed. But inside the Python process, os.environ.get('CLAUDE_PLUGIN_ROOT') returns None because the variable was never exported as an environment variable.

When PLUGIN_ROOT is None, the if PLUGIN_ROOT and ... guard prevents sys.path from being updated, so from core.config_loader import load_rules fails with ImportError.

Suggested Fix

Add a __file__-based fallback. Each hook script lives at <plugin_root>/hooks/<script>.py, so dirname(dirname(__file__)) reliably resolves to the plugin root:

# Prefer env var, fall back to deriving from __file__
PLUGIN_ROOT = os.environ.get('CLAUDE_PLUGIN_ROOT') or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PLUGIN_ROOT not in sys.path:
    sys.path.insert(0, PLUGIN_ROOT)

This needs to be applied to all 4 files:

  • hooks/pretooluse.py
  • hooks/posttooluse.py
  • hooks/stop.py
  • hooks/userpromptsubmit.py

Environment

  • Claude Code (latest as of March 2026)
  • macOS Darwin 25.2.0
  • Python 3.x
  • hookify plugin installed via Claude Code marketplace

Workaround

Users can manually patch the cached plugin files at:

~/.claude/plugins/cache/claude-plugins-official/hookify/<hash>/hooks/*.py

But these changes are lost on plugin cache refresh.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗