claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

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

Open Cygnusfear opened this issue 3 months ago • 3 comments

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)

  2. 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.

Cygnusfear avatar Dec 18 '25 04:12 Cygnusfear

Investigation Details

How We Discovered This

We noticed that our TotalRecall memory plugin wasn't capturing any conversation data from Dec 17-18, despite:

  • MCP server working correctly
  • 596 transcript files existing in ~/.claude/projects/
  • Hooks being loaded and matched according to debug logs

Root Cause Diagnosis

Using debug logs at ~/.claude/debug/, we traced the issue:

  1. Hooks ARE loaded: Loaded hooks from standard location for plugin totalrecall
  2. Hooks ARE matched: Matched 1 unique hooks for query
  3. Hooks are NOT executed: No subsequent execution logs, no process spawned

The key differentiator is isLocal: true in installed_plugins.json. This flag is set when installing plugins locally vs from a git remote.

Workaround Implementation

We moved critical hooks from the plugin's hooks/hooks.json to the user's ~/.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/Users/alexander/.cargo/bin/totalrecall hook queue-synthesis",
            "timeout": 30000
          }
        ]
      }
    ]
  }
}

Important: Use absolute paths in settings.json since it's not relative to the plugin directory.

Verification

After applying the workaround:

$ totalrecall dream status
Queue Statistics:
  Pending:     1   # Was 0 before
  Processing:  0
  Completed:   0
  Failed:      0

The Stop hook now executes and queues transcripts for synthesis.

Additional Bug Found During Investigation

We also discovered a separate bug in our plugin where the JSONL parser expected type: "message" but Claude Code transcripts use type: "user" and type: "assistant". This is unrelated to the hooks execution bug but worth noting for other plugin developers parsing transcript files.

Cygnusfear avatar Dec 18 '25 04:12 Cygnusfear

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/11939
  2. https://github.com/anthropics/claude-code/issues/11509
  3. https://github.com/anthropics/claude-code/issues/10225

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

github-actions[bot] avatar Dec 18 '25 05:12 github-actions[bot]

Experiencing the same on 2.0.73 on Ubuntu 24 via WSL and Claude Code Web.

This seems to be an issue related to upgrading beyond 2.0.70. To fix, uninstall all plugins and clear prior installation from plugin cache at ~/.claude/plugins/cache and then reinstall the plugins freshly and they should work. Note I also updated to 2.0.74 before starting the next session.

https://github.com/anthropics/claude-code/issues/12634#issuecomment-3674215064 https://github.com/anthropics/claude-code/issues/11509

sylvansys avatar Dec 19 '25 22:12 sylvansys

Also having this issue 2.0.76 macos terminal. Specifically, post tool use hooks are running but NOT blocking claude code when installed at project level. Same hooks work fine at user level.

mintmcqueen avatar Jan 02 '26 00:01 mintmcqueen

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

github-actions[bot] avatar Feb 01 '26 10:02 github-actions[bot]

do not autoclave, this is still happening

RBozydar avatar Feb 04 '26 22:02 RBozydar

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] avatar Mar 05 '26 22:03 github-actions[bot]