# Plush for Claude Code

Goal: give Claude Code first-class Plush tools for notifications, phone questions, Live Activities, and widgets, plus a completion notification with a useful session title and Claude Code source icon.

## Install

Requires Node.js 20+, Claude Code, and a saved Plush login.

```sh
npm install -g @easypush/plush
plush login "$PLUSH_API_KEY"
```

Then run these inside Claude Code:

```text
/plugin marketplace add DjDeveloperr/Plush
/plugin install plush@easypush
/reload-plugins
```

The plugin bundles the Plush MCP server, an agent skill, and an asynchronous native `Stop` hook. It does not put the API token in project files or Claude settings; MCP and the hook read the login saved by `plush login`.

If you previously installed the standalone hook, remove it before enabling the plugin so completions do not notify twice:

```sh
plush uninstall-claude-hook
```

`plush install-claude-hook` remains a fallback for installations that cannot use plugins.

## Ask from Claude

Use the plugin's `plush` MCP tools. `ask_question` supports open-ended text or up to three quick options while retaining an open-ended response. Set `wait: true` when Claude cannot continue without the answer. Responses return to the same MCP call, so no session guessing or separate Claude process is required.

The same MCP server can list devices, send pushes, start/update/end Live Activities, and create/update/delete persistent widgets from JSON or JSX/TSX files.

## Notification Shape

The hook reads Claude Code's official `Stop` JSON from stdin. It uses `last_assistant_message` as the body, `session_id` for thread grouping, the latest `custom-title` record from `transcript_path` when available, and the current workspace as a title fallback. It sends:

- `app.name = "Claude Code"`
- `app.iconUrl = "https://claude.ai/favicon.ico"`
- communication metadata with the same Claude icon for avatar presentation
- one visible title and no duplicate communication subtitle
- a maximum 3,000-character body by default

The hook exits quietly when credentials are missing, ignores recursive `stop_hook_active` stops, deduplicates identical events for one minute, and never writes the API key into Claude settings.

## Environment

- `PLUSH_CLAUDE_ENABLED=0`: disable the hook without removing it.
- `PLUSH_CLAUDE_TARGET=<device-or-all>`: override the destination.
- `PLUSH_CLAUDE_APP_NAME=<name>`: override the in-app source name.
- `PLUSH_CLAUDE_APP_ICON_URL=<https-url>`: override the source/avatar image.
- `PLUSH_CLAUDE_COMMUNICATION=0`: use a normal notification instead of communication presentation.
- `PLUSH_CLAUDE_MAX_BODY_CHARS=<512-3600>`: change the visible body cap.
- `PLUSH_CLAUDE_DEBUG=1`: print hook errors to stderr for local diagnosis.

## Unsolicited Phone Replies

Questions initiated through the Plush MCP server return safely to their originating tool call. Unsolicited replies to an old Claude completion notification are not enabled: a Stop hook does not provide a safe stable binding to the exact active Claude session when several sessions are running, and Plush will not guess.

When Claude's channel contract has a stable per-session routing identity, Plush can bridge hosted question responses through a two-way channel without spawning a second Claude process or using `claude --resume`.

## References

- Plush API: https://easypush.app/doc.md
- Claude Code plugins: https://code.claude.com/docs/en/plugins
- Claude Code MCP: https://code.claude.com/docs/en/mcp
- Claude Code hooks: https://code.claude.com/docs/en/hooks
- Claude Code Channels: https://code.claude.com/docs/en/channels-reference
