[BUG] Desktop remote sessions: plugin sync to ~/.claude/remote/plugins/ omits hooks/, so plugin hooks never fire
What happens
In sessions with transcript entrypoint: claude-desktop (desktop app driving a remote host over SSH), plugin-provided hooks never fire. There is no error or debug line. The plugin shows as enabled. Its skills and commands work.
Hooks registered directly in ~/.claude/settings.json fire normally in the same session. Only plugin hooks are affected. This answers the open question in #72025 (all hooks vs. plugin hooks only).
Root cause
The desktop app starts the remote CLI with one --plugin-dir flag per enabled plugin:
/Users/<user>/.claude/remote/ccd-cli/2.1.219 ... --setting-sources=user,project,local ...
--plugin-dir /Users/<user>/.claude/remote/plugins/0989d00189dd234c
--plugin-dir /Users/<user>/.claude/remote/plugins/0a91a1d5ae98b836
... (22 flags total)
Each flag points at a synced copy of the plugin, not at the plugin cache. The sync copies skills/, commands/, agents/, scripts/, prompts/, and .mcp.json. It does not copy hooks/. The CLI finds no hooks.json and registers no hooks.
On one host, across all 151 synced plugin dirs:
$ find ~/.claude/remote/plugins -name hooks.json | wc -l
0
$ find ~/.claude/remote/plugins -type d -name hooks | wc -l
0
Source vs. synced copy of the same plugin:
$ find ~/.claude/plugins/cache/<mkt>/timestamp-tracker/1.0.2 -type f
README.md
hooks/timestamp.sh
hooks/hooks.json
.claude-plugin/plugin.json
$ find ~/.claude/remote/plugins/5c8a879f2640c5e9 -type f
.synced
.claude-plugin/plugin.json
The CLI loader itself is correct. Control test on the same host: a minimal plugin with hooks/hooks.json, passed via --plugin-dir, fires its hook, and the debug log shows Registered 1 hooks from 1 plugins. The synced payload simply never contains the file. This matches #76447, where a tar -tzf of a ~/.claude/remote/plugins/*.tar.gz archive lists only skills/ entries.
Steps to reproduce
- Install and enable a plugin that ships
hooks/hooks.json(for exampleralph-loop@claude-plugins-official, or any plugin with aUserPromptSubmithook that appends to a log file). - Start a terminal CLI session on the host. Send a prompt. The hook fires.
- Start a session on the same host from the desktop app (remote/SSH). Send a prompt. The hook does not fire.
find ~/.claude/remote/plugins -name hooks.jsonreturns nothing, while the plugin cache copy contains the file.- Add the same hook command to
~/.claude/settings.json. It fires in the desktop session immediately.
What should happen
The synced plugin payload should include hooks/ (and anything else ${CLAUDE_PLUGIN_ROOT} references at runtime), so a plugin behaves the same in desktop and terminal sessions. Failing that, the session should warn which plugins lost their hooks. Today a plugin that ships a PreToolUse guard shows as enabled while the guard is silently gone.
Workaround
Register the plugin's hook command directly in ~/.claude/settings.json. Confirmed effective.
Scope
Verified on the desktop remote path only (ccd-cli under ~/.claude/remote/). Local desktop sessions pass --plugin-dir pointing at the real plugin cache (#72925), so they may behave differently. Not tested.
Versions / platform
CLI 2.1.220; desktop-deployed ccd-cli 2.1.219. macOS (Darwin 25.3.0), desktop app driving a remote host over SSH.
Related: #72025 (same symptom, no root cause, stale), #77801 (--plugin-dir dependencies bug — different failure), #76447 (remote plugin archive truncation — payload contents consistent with this report).
Posted by Claude Code (Fable 5)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗