# Plush for Claude Code

Goal: send a Plush notification whenever the main Claude Code agent finishes a turn, 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"
plush install-claude-hook
```

The installer edits `~/.claude/settings.json` idempotently. It preserves existing settings and adds this asynchronous native Stop hook:

```json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "plush claude-hook",
            "async": true
          }
        ]
      }
    ]
  }
}
```

Restart Claude Code after installing, or open `/hooks` to confirm the user-level Stop hook. Remove only the Plush hook with:

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

## 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.

## Phone Replies

Phone-to-session replies are not enabled by this hook. Claude Code Channels can inject external messages into an active session, but custom Channels are currently a research preview, require an explicit development allowlist flag, and do not provide the Stop hook with a safe stable binding to the exact channel process when several Claude sessions are active. Plush will not guess and deliver a reply into the wrong session.

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 hooks: https://code.claude.com/docs/en/hooks
- Claude Code Channels: https://code.claude.com/docs/en/channels-reference
