[BUG] Disabled plugin's PostToolUse hook still executes on every Edit/Write despite not being listed in /hooks

Status Closed — not planned
Reported on v2.1.227
Maintainer reply None cached
Activity 2 comments · opened Aug 11, 2026 · closed Aug 22, 2026

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?

Setting a plugin to false under enabledPlugins in ~/.claude/settings.json unloads that plugin's skills and agents but does not unregister its PostToolUse hook. The hook continues executing on every Edit and Write, creating directories inside unrelated project repositories. No settings file enables the plugin — the only reference anywhere is the false.

The disable is applied inconsistently: the plugin's skill and its agent both disappear from the session, which makes the still-running hook harder to notice. The hooks UI lists no PostToolUse entry, so nothing in the interface indicates any PostToolUse hook is active. Yet the hook still runs.

What Should Happen?

A plugin disabled via enabledPlugins should contribute nothing to the session. No skills, no agents, and no hooks. "Disabled" should mean "not running."

More importantly, the hooks listing must be complete and authoritative: any hook that executes must appear in /hooks, and a hook absent from /hooks must never run. Whatever the cause, a session silently executing third-party code on every file edit while the interface reports no such hook is the more serious defect.

Error Messages/Logs

No errors. The failure is silent. Observable side effect only:

$ # with "impeccable@impeccable": false in ~/.claude/settings.json
$ # perform any Write via Claude Code, then:
$ find . -name ".impeccable"
./.impeccable          # contains hook.cache.json

Plugin's hook definition (~/.claude/plugins/cache/impeccable/impeccable/3.6.0/hooks/hooks.json):

PostToolUse | matcher: Edit|Write|MultiEdit
    node "${CLAUDE_PLUGIN_ROOT}/skills/impeccable/scripts/hook.mjs"

Plugin remains installed despite being disabled (~/.claude/plugins/installed_plugins.json):

{ "version": 2, "plugins": { "impeccable@impeccable": [
    { "scope": "user", "version": "3.6.0",
      "installPath": "/Users/<me>/.claude/plugins/cache/impeccable/impeccable/3.6.0" } ] } }

Steps to Reproduce

  1. Install a plugin that registers a PostToolUse hook on Edit|Write|MultiEdit. Reproduced with impeccable@impeccable 3.6.0 from marketplace pbakaus/impeccable.
  2. Confirm the hook fires: perform any Write, observe .impeccable/hook.cache.json created beside the written file.
  3. Set "enabledPlugins": { "impeccable@impeccable": false } in ~/.claude/settings.json.
  4. Fully quit and relaunch Claude Code.
  5. Confirm the disable partially applied — the plugin's skill and its agent are both absent from the session's available listings.
  6. Verify no other settings file re-enables it (grep -rn impeccable ~/.claude/settings.json .claude/settings*.json) — the only match is the false.
  7. Perform any Write.
  8. A .impeccable/ directory is created again. The hook is still running.

Only claude plugin uninstall impeccable@impeccable stops it.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.227 (Claude Code)

Platform

Other

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

Hook registration appears keyed to installed state (installed_plugins.json) rather than to enabledPlugins, which would explain why skills and agents honor the flag and hooks do not.

This is a trust-boundary issue. A plugin the user believes is disabled keeps executing third-party code on every file edit and writing into their repositories. Not a vulnerability in the strict sense (the user installed and previously enabled it) but users reasonably read "disabled" as "not running," and if the hooks UI shows nothing, there is no surfaced signal that it still runs.

View original on GitHub ↗

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