/reload-plugins does not refresh in-memory hook script bodies
Summary
After running /reload-plugins, plugin hook scripts that have been edited or updated on disk continue to execute their previously-cached version for tool calls in the current session. The reload report says hooks were reloaded, but the actual in-memory script bodies are not refreshed. A full Claude Code session restart is required to pick up new hook code.
Expected behavior
After /reload-plugins, subsequent tool invocations should execute the current on-disk version of each plugin's hook scripts.
Actual behavior
/reload-plugins appears to refresh hook registrations (the count is reported correctly: e.g. Reloaded: ... · 9 hooks · ...), but the script bodies used to evaluate hook decisions remain whatever was loaded when the session started. Edits or plugin upgrades on disk are not picked up until the session is fully restarted.
Reproduction
- Start a Claude Code session with at least one plugin installed that registers a
PreToolUse:Bashhook. - Edit one of that plugin's hook scripts on disk to change its behavior — for example, prepend a no-op early return:
``bash``
# In a hook that previously blocked some command:
exit 0 # added at top
Or update the plugin to a new version that includes such a change.
- Run
/reload-plugins. The reload reports the hooks were reloaded. - Trigger a tool call that the hook used to react to.
- Observe: the hook still behaves per its pre-edit version. Restart Claude Code and repeat — the new behavior is now in effect.
Concrete evidence from a real session
In a Claude Code session running plugin standard-tooling@1.4.5, the plugin's hooks/scripts/block-raw-git-commit.sh was updated in v1.4.4 to gate on a is_managed_repo check (PR standard-tooling-plugin#104) so the hook is a no-op in repositories that have not opted in to the plugin's standards.
After upgrading to 1.4.5 and running /reload-plugins:
| Check | Result |
|---|---|
| On-disk hook script content | Has the gating logic |
| is_managed_repo shell helper invoked directly with the unmanaged repo's path | Returns UNMANAGED (correct) |
| Direct bash block-raw-git-commit.sh with the same JSON input Claude Code would send | Exits 0, no block output (correct) |
| Claude Code's PreToolUse hook execution for the same git commit tool call | Still blocks with the pre-gating denial message |
The on-disk script and direct invocation work as expected. Only the in-session Claude Code hook execution preserves the old behavior.
Workaround
Fully restart the Claude Code session. Hook scripts are then loaded fresh from disk and the updated behavior takes effect.
Why this matters
For any plugin that ships hook updates, users currently have to restart their session to pick up fixes. /reload-plugins appears to do the right thing (reports a reload, refreshes plugin metadata, skills, agents), so users reasonably believe their hooks have been refreshed too. The discrepancy between the reload's reported success and the actual behavior is confusing and can cause significant time loss debugging plugin behavior.
Environment
- macOS Tahoe 26.3
- Claude Code: 2.1.119
- Plugin:
standard-tooling@1.4.5(source: https://github.com/wphillipmoore/standard-tooling-plugin)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗