Local plugin hooks match but never execute (isLocal: true)

Resolved 💬 8 comments Opened Dec 18, 2025 by Cygnusfear Closed Mar 5, 2026

Summary

Local plugins (isLocal: true) have their hooks matched by Claude Code but the hooks are never actually executed. This appears to be the same root cause as #11509, #11939, and #12151.

Environment

  • Claude Code version: 2.0.64
  • Platform: macOS Darwin 24.6.0
  • Plugin installation: Local (isLocal: true in installed_plugins.json)

Steps to Reproduce

  1. Create a local plugin with hooks in hooks/hooks.json:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "my-plugin hook queue-synthesis",
            "async": true
          }
        ]
      }
    ]
  }
}
  1. Install the plugin locally (results in isLocal: true)
  1. Run Claude Code and observe debug logs

Expected Behavior

The Stop hook should execute when Claude finishes responding.

Actual Behavior

Debug logs show:

Loaded hooks from standard location for plugin totalrecall: /Users/.../.claude/plugins/cache/totalrecall-local/totalrecall/3.0.5/hooks/hooks.json
Loading hooks from plugin: totalrecall
Getting matching hook commands for Stop with query: undefined
Found 1 hook matchers in settings
Matched 1 unique hooks for query "no match query" (1 before deduplication)

But no execution follows. The command is never run. No process spawned. Manual execution of the same command works perfectly:

echo '{"session_id":"test","transcript_path":"/path/to/transcript.jsonl"}' | my-plugin hook queue-synthesis
# Returns: {"queued":true,"queue_id":1,"message":"Session chunk queued for synthesis"}

Workaround

Moving hooks from the plugin's hooks/hooks.json to the user's ~/.claude/settings.json works:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/absolute/path/to/my-plugin hook queue-synthesis",
            "timeout": 30000
          }
        ]
      }
    ]
  }
}

This confirms the hook configuration is correct - only the plugin hook execution path is broken.

Evidence from installed_plugins.json

"my-plugin@my-plugin-local": {
  "isLocal": true
}

Related Issues

  • #10875 - Plugin hooks JSON output not captured (CLOSED)
  • #11509 - Local file-based plugin hooks never execute
  • #11939 - Local plugin SessionStart hooks don't execute (CLOSED as dup)
  • #12151 - Plugin hook output not passed to agent (OPEN)

Impact

This bug completely breaks the hook functionality for any locally-developed plugin, forcing developers to either:

  1. Ask users to manually copy hooks to their settings.json
  2. Publish to git and install from remote (changes isLocal to false)

Neither is acceptable for local plugin development workflow.

View original on GitHub ↗

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