Skip to content

fix: wrap plugin hooks in a top-level "hooks" key so Claude Code loads them - #1102

Open
zackkatz wants to merge 1 commit into
tirth8205:stagingfrom
zackkatz:fix/plugin-hooks-wrapper
Open

zackkatz wants to merge 1 commit into
tirth8205:stagingfrom
zackkatz:fix/plugin-hooks-wrapper

Conversation

@zackkatz

@zackkatz zackkatz commented Oct 8, 2026

Copy link
Copy Markdown

Linked issue

No open issue. Current Claude Code reports this when it loads the plugin:

Failed to load hooks from .../code-review-graph/<sha>/hooks/hooks.json:
[ { "code": "custom", "path": [], "message": "hooks.json must have 'hooks' (the hook matchers) or 'modules' (hooks modules), or both" } ]

What & why

hooks/hooks.json holds a bare event map ({"SessionStart": [...], "PostToolUse": [...]}). Claude Code requires a plugin hooks file to put that map under a top-level "hooks" key, so it skips the file and none of the plugin's hooks run.

The plugins reference states it directly:

A hooks file wraps the event map in a top-level "hooks" key, the shape hooks/hooks.json uses. A file that contains only the event map, without that wrapper, fails to load.

The wrapper was in this file until 66ccaef (#283). The error in #283 came from a settings.json written by the installer and listed hooks: Expected array, but received undefined under each event, which points to the missing matcher/hooks entries. 66ccaef fixed those and also removed the wrapper. This PR puts the wrapper back and keeps the matchers.

Changes:

  • hooks/hooks.json: event map moved under "hooks". Matchers, commands, and timeouts are unchanged.
  • tests/test_packaging.py and tests/test_skills.py: both read the shipped file, so they now expect the wrapper. The packaging test fails if the wrapper is removed again.
  • CHANGELOG.md: entry under Unreleased.

How it was tested

Claude Code loading the plugin (Claude Code 2.1.294, claude --plugin-dir <checkout> -p "reply ok" --debug-file <log>):

Checkout Debug log
staging before this PR [ERROR] "Failed to load hooks for crg: ... hooks.json must have \hooks` (the hook matchers) or `modules` (hooks modules), or both"andRegistered 15 hooks from 23 plugins`
This branch No error. Registered 18 hooks from 23 plugins

The difference of 3 is this plugin's SessionStart hook plus its two PostToolUse hooks.

claude plugin validate . passes on both versions, so it does not catch this. Only loading the plugin shows the error.

Test suite:

uv run pytest tests/ --tb=short -q
# 1 failed, 4267 passed, 850 skipped, 2 xfailed, 2 xpassed

The one failure is tests/test_embedding_initialization.py::test_windows_server_still_prewarms_before_mcp_run (NameError: name 'windows_events' is not defined). It fails the same way on unmodified staging on macOS and is not related to this change.

uv run pytest tests/test_packaging.py -m packaging -k hook_template -q
# this branch: 1 passed
# same test with the old hooks.json: 1 failed
#   AssertionError: shipped hook template must wrap its events in 'hooks': ['PostToolUse', 'SessionStart']

uv run ruff check tests/test_packaging.py
# All checks passed!

Checklist

  • Tests added for new functionality
  • All tests pass: uv run pytest tests/ --tb=short -q (except the Windows test above, which also fails on staging)
  • Linting passes
  • Type checking: no Python source in code_review_graph/ changed
  • Lines are at most 100 characters
  • Docs updated where behavior changed (CHANGELOG)

…s them

Claude Code rejects a plugin hooks/hooks.json that holds a bare event map:
"hooks.json must have `hooks` (the hook matchers) or `modules` (hooks
modules), or both". The plugin's SessionStart and PostToolUse hooks were
therefore never registered. The plugins reference requires the event map
under a top-level "hooks" key, which is the shape this file had before
66ccaef. That commit fixed the missing matchers from tirth8205#283 and also removed
the wrapper, which the settings.json error in that issue did not ask for.

Tests that read the shipped file now expect the wrapper.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant