Skip to content

[BUG] Desktop remote sessions: plugin sync to ~/.claude/remote/plugins/ omits hooks/, so plugin hooks never fire #83643

Description

@jaidhyani

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

  1. Install and enable a plugin that ships hooks/hooks.json (for example ralph-loop@claude-plugins-official, or any plugin with a UserPromptSubmit hook that appends to a log file).
  2. Start a terminal CLI session on the host. Send a prompt. The hook fires.
  3. Start a session on the same host from the desktop app (remote/SSH). Send a prompt. The hook does not fire.
  4. find ~/.claude/remote/plugins -name hooks.json returns nothing, while the plugin cache copy contains the file.
  5. 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)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions