Plugin framework deletes external hooks/hooks.json from working tree
Bug description
When a plugin uses an external hooks/hooks.json file (the default location per plugin docs), Claude Code deletes the file and its parent directory from the working tree after loading it. This causes a persistent unstaged deletion in git status.
Reproduction steps
- Create a plugin with
.claude-plugin/plugin.jsonand ahooks/hooks.jsonfile:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── hooks/
│ └── hooks.json ← gets deleted
└── scripts/
└── my-hook.sh
- Start a Claude Code session in the plugin directory (or use
--plugin-dir) - Observe that
hooks/hooks.json(and the entirehooks/directory) is deleted from disk within seconds
Observed behavior
- The
hooks/directory and all its contents are deleted from the working tree - This includes untracked files placed in
hooks/(not justhooks.json) git statusshowsdeleted: hooks/hooks.json- Restoring the file via
git checkout HEAD -- hooks/hooks.jsontriggers another deletion within seconds - The deletion also occurs when
plugin.jsonexplicitly declares"hooks": "./hooks/hooks.json" - The deletion also occurs when pointing to a different path like
"hooks": "./config/hooks.json"— the file at the resolved path is deleted - Files in directories not targeted by the
hooksfield are unaffected (confirmed by control test)
Expected behavior
External hook files should remain on disk after being loaded, especially for local development plugins. The framework should read the file without deleting it.
Workaround
Inline the hooks configuration directly in plugin.json using the object form:
{
"name": "my-plugin",
"hooks": {
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/my-hook.sh"
}
]
}
]
}
}
}
Environment
- Claude Code: latest (2026-04-08)
- macOS Darwin 25.4.0
- Plugin loaded via local working directory (
.claude-plugin/plugin.jsonpresent in CWD)
Hypothesis
The plugin framework appears to apply a cache-and-cleanup pattern (designed for marketplace plugins copied to ~/.claude/plugins/cache/) to local development plugins as well, removing the source hook file after reading it into the hook registry.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗