Plugin-sourced hooks.json never fires, even though the plugin shows installed + enabled (no error, no registry entry evidence)

Open 💬 1 comment Opened Jul 9, 2026 by dkkplk

Claude Code version: 2.1.202 (macOS/Darwin)

Summary: A plugin's hooks.json (SessionStart, Stop, PreToolUse, PostToolUse, PreSkill hooks) never fires, despite the plugin being installed, enabled, and its cached hooks.json matching the source repo exactly. The plugin's skills/agents work normally — only the hooks are silently inert.

Setup (verified correct):

  • Plugin base-tools@claude-base appears in ~/.claude/plugins/installed_plugins.json (auto-installed as a dependency of another plugin, scope: user, auto: true)
  • Explicitly enabled in ~/.claude/settings.json"enabledPlugins": {"base-tools@claude-base": true}
  • Cached hooks.json is byte-identical to the plugin source, with standard ${CLAUDE_PLUGIN_ROOT}/hooks/*.sh command entries for SessionStart, Stop, PostToolUse (matcher Write|Edit), 8x PreToolUse (matchers Bash, Write|Edit), and PreSkill (matcher .*)
  • All hook scripts present on disk, correct executable permissions

Evidence they don't fire:

  1. Live repro: ran echo test --no-verify via the Bash tool. A PreToolUse/Bash hook in this plugin blocks any command containing --no-verify (exit 2 + stderr message). The command executed cleanly with no block.
  2. A PreSkill hook writes one JSON line per skill activation to ${CLAUDE_PLUGIN_DATA}/base-tools/skills.jsonl. That file does not exist anywhere on disk despite many skill invocations across 11 sessions in the project.
  3. Checked raw session transcripts (~/.claude/projects/.../*.jsonl) for all 11 sessions in the project -- the SessionStart hook is supposed to inject a short primer into context. In every session, the first user message appears immediately after the mode/permission-mode/file-history-snapshot bootstrap entries with zero injected content in between.
  4. No Stop-hook output appears at the end of a completed session (only a turn_duration system marker).

Expected: Hooks registered in an enabled plugin's hooks.json should fire per the documented hook-dispatch behavior -- no additional trust/approval step or restart is documented as required beyond install + enable.

Question: Does a plugin that is only transitively installed as a dependency (auto: true in installed_plugins.json, pulled in via another plugin's dependencies field) get its hooks.json registered into the session's hook dispatch the same way a directly-installed plugin does? That's the one structural difference here vs. a normal install, and it's not covered in the plugin hooks docs.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗