[BUG] SessionStart hooks never execute for local file-based marketplace plugins
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
SessionStart hooks (and likely all hooks) never execute when plugins are loaded from a local file-based marketplace. The issue appears to be that hooks are only discovered in ~/.claude/plugins/*/hooks/hooks.json, but local file-based marketplace plugins are referenced in-place and never copied to that directory.
Key observations:
- Plugin commands and skills work correctly (proving the plugin is loaded)
- Hook execution shows success message:
SessionStart:Callback hook success: Success - But the hook script output is never captured or provided to Claude's context
- The
/hookscommand does not list hooks from local marketplace plugins
This differs from issue #10997 which affects GitHub marketplace plugins where hooks fail on first run but work after caching. With local file marketplaces, hooks never work because there's no caching/copying step.
Environment
- Claude Code Version: 2.0.37
- Platform: macOS (Darwin 24.6.0)
- Installation: CLI
- Marketplace Type: Local file-based (in-repo)
What Should Happen?
Local file-based marketplace plugins should have full feature parity with GitHub marketplace plugins, including hook execution. The hooks system should:
- Discover hooks from plugins regardless of installation location
- Resolve ${CLAUDE_PLUGIN_ROOT} correctly for in-place plugins
- Execute hooks and capture their output
- List hooks in /hooks output
Error Messages/Logs
Steps to Reproduce
- Create a test plugin with a SessionStart hook
Create this directory structure:
mkdir -p test-marketplace/test-plugin/{.claude-plugin,hooks,commands}
Create test-marketplace/.claude-plugin/marketplace.json:
{
"name": "test-marketplace",
"owner": {
"name": "Test",
"email": "[email protected]"
},
"metadata": {
"description": "Test marketplace",
"version": "1.0.0",
"pluginRoot": "."
},
"plugins": [
{
"name": "test-plugin",
"source": "./test-plugin",
"description": "Test plugin with SessionStart hook",
"version": "1.0.0",
"author": {
"name": "Test",
"email": "[email protected]"
}
}
]
}
Create test-marketplace/test-plugin/.claude-plugin/plugin.json:
{
"name": "test-plugin",
"version": "1.0.0",
"description": "Test plugin",
"author": {
"name": "Test",
"email": "[email protected]"
}
}
Create test-marketplace/test-plugin/hooks/hooks.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/test-hook.sh"
}
]
}
]
}
}
Create test-marketplace/test-plugin/hooks/test-hook.sh:
#!/bin/bash
cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "TEST HOOK EXECUTED: If you can see this message, the SessionStart hook is working."
}
}
EOF
Make the script executable:
chmod +x test-marketplace/test-plugin/hooks/test-hook.sh
Create a simple test command test-marketplace/test-plugin/commands/test.md:
---
name: test
description: Test command to verify plugin is loaded
---
Reply with "Test plugin is loaded and working!"
- Install the marketplace and plugin
cd test-marketplace
claude /plugin marketplace add .
claude /plugin install test-plugin@test-marketplace
- Verify plugin is loaded (commands work)
claude /test
Expected: Should see "Test plugin is loaded and working!"
Actual: ✅ This works - confirms plugin is loaded
- Test if SessionStart hook executes
Start a new Claude session:
claude
Then ask Claude: "Can you see any test hook message in your context?"
Expected: Claude should see the "TEST HOOK EXECUTED" message in its context
Actual: ❌ Claude has no such message in context
- Verify hook is not registered
In the Claude session, run:
/hooks
Expected: Should list the SessionStart hook from test-plugin
Actual: ❌ Hook is not listed
- Verify hook script works when run directly
bash test-marketplace/test-plugin/hooks/test-hook.sh
Expected: Should output valid JSON
Actual: ✅ Outputs valid JSON correctly
---
This shows that:
- The plugin loads (commands work)
- The hook script is valid (runs correctly when executed directly)
- But the hook is never discovered or executed by Claude Code
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.0.37
Platform
AWS Bedrock
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Related Issues
- #10997 - SessionStart hooks don't execute on first run with GitHub marketplace plugins (different - affects only first run,
not all runs)
- #9354 - ${CLAUDE_PLUGIN_ROOT} variable issues (related but different)
- #10871 - Plugin-registered hooks executed twice (affects working plugin hooks)
Workaround
Temporarily register hooks globally in ~/.config/claude/hooks/hooks.json with absolute paths to the plugin scripts. However,
this defeats the purpose of plugin-based hooks and doesn't scale for distributed plugins.
Impact
This prevents developers from:
- Building plugins with hooks in local development environments
- Distributing plugins via file-based marketplaces with hook functionality
- Testing hook behavior before publishing to GitHub marketplaces
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗