Skip to content

fix(hookify): make package import independent of the install directory name - #81672

Open
ozdemirsarman wants to merge 1 commit into
anthropics:mainfrom
ozdemirsarman:fix/hookify-package-import
Open

fix(hookify): make package import independent of the install directory name#81672
ozdemirsarman wants to merge 1 commit into
anthropics:mainfrom
ozdemirsarman:fix/hookify-package-import

Conversation

@ozdemirsarman

Copy link
Copy Markdown

Fixes #69665
Fixes #81448

The problem

The hook entry points make the hookify package importable by putting
os.path.dirname(CLAUDE_PLUGIN_ROOT) on sys.path and relying on the plugin
directory being named exactly hookify.

A marketplace install does not satisfy that — the plugin is unpacked into a
versioned directory (.../hookify@0.1.0), so hookify is not a name Python can
resolve there. Every hook event hits the ImportError branch and the plugin emits
Hookify import error: No module named 'hookify' instead of evaluating any rule.
The plugin looks installed but is inert, on every single hook event.

Repro

With a real rule at .claude/hookify.dangerous-rm.local.md and input
{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}:

# plugin at .../hookify@0.1.0  (marketplace layout)
before: {"systemMessage": "Hookify import error: No module named 'hookify'"}
after:  {"systemMessage": "**[block-dangerous-rm]** ..."}

The fix

Rather than inferring the package name from the directory layout, register the
package explicitly with its __path__ pinned to the plugin root
(hooks/_bootstrap.py). That makes the import independent of the directory name.

It also removes the dependency on CLAUDE_PLUGIN_ROOT being set and correct: the
root falls back to a path derived from __file__ when the variable is missing or
does not point at a hookify checkout.

Testing

  • All four entry points (PreToolUse, PostToolUse, Stop, UserPromptSubmit) under a
    hookify@0.1.0 directory.
  • CLAUDE_PLUGIN_ROOT unset — works.
  • CLAUDE_PLUGIN_ROOT pointing at a nonexistent path — works.
  • Plain hookify/ directory layout — unchanged, still works.

…y name

The hook entry points made the `hookify` package importable by putting
`os.path.dirname(CLAUDE_PLUGIN_ROOT)` on `sys.path` and relying on the plugin
directory being named exactly `hookify`.

A marketplace install does not satisfy that: the plugin is unpacked into a
versioned directory (`.../hookify@0.1.0`), so `hookify` is not a name Python
can resolve there. Every hook event then hits the ImportError branch and the
plugin emits `Hookify import error: No module named 'hookify'` instead of
evaluating any rule - the plugin appears installed but is inert.

Instead of guessing the package name from the directory layout, register the
package explicitly with its `__path__` pinned to the plugin root
(hooks/_bootstrap.py). This also removes the dependency on CLAUDE_PLUGIN_ROOT
being set or correct: the root is derived from `__file__` when the variable is
missing or does not point at a hookify checkout.

Verified against a versioned marketplace layout, with a real rule file in
.claude/hookify.dangerous-rm.local.md:

  before: {"systemMessage": "Hookify import error: No module named 'hookify'"}
  after:  {"systemMessage": "**[block-dangerous-rm]** ..."}

Also verified for all four entry points (PreToolUse, PostToolUse, Stop,
UserPromptSubmit), with CLAUDE_PLUGIN_ROOT unset, with CLAUDE_PLUGIN_ROOT
pointing at a nonexistent path, and for the plain `hookify/` directory layout
that already worked.

Fixes anthropics#69665
Fixes anthropics#81448
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant