diff --git a/README.md b/README.md index c4f94b6..27598cc 100644 --- a/README.md +++ b/README.md @@ -106,25 +106,19 @@ The .NET and Node builds are wire-compatible (same port, same `X-Mailbox` header ## Use from Claude Code (plugin) -Easiest path for colleagues — install the marketplace once, then the plugin: +Easiest path — everything happens inside Claude Code: ``` /plugin marketplace add https://git.kuns.dev/releases/ClaudeMailbox /plugin install claude-mailbox@claude-mailbox +/claude-mailbox:mailbox-doctor ``` -The plugin only wires the `UserPromptSubmit` hook. **The daemon binary is a separate prerequisite** — install it via the npm package above (or the bootstrap one-liner) and run `claude-mailbox install-autostart` so it starts on boot. +The doctor command checks for the `claude-mailbox` binary, installs it if missing (via npm), runs `install-autostart` if the daemon isn't registered, prompts you for a mailbox name and writes it to `.claude/settings.json`, and finishes with a self → self smoke test. -Then set a per-machine mailbox name: +After that, unread messages appear in context before every prompt. If the daemon is unreachable later, the hook emits a one-line setup hint instead of staying silent — missing setup is loud, not invisible. -```sh -setx CLAUDE_MAILBOX_NAME alice # Windows -export CLAUDE_MAILBOX_NAME=alice # macOS / Linux (in ~/.zshrc or ~/.bashrc) -``` - -Restart Claude Code and unread messages will appear in context before every prompt. If the daemon is not reachable, the hook emits a one-line setup hint instead of staying silent — so a missing daemon is loud, not invisible. - -See [`plugin/README.md`](./plugin/README.md) for the full walkthrough. +See [`plugin/README.md`](./plugin/README.md) for details, including why each Claude session needs its own mailbox name. ## Use from a Claude session diff --git a/plugin/README.md b/plugin/README.md index e08fbcd..8f2e6a1 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -1,68 +1,59 @@ # claude-mailbox plugin -Lets Claude Code automatically pull unread messages from a local `claude-mailbox` daemon before every prompt and inject them into the conversation context. +Lets Claude Code pull unread messages from a local `claude-mailbox` daemon before every prompt and inject them into the conversation context. -## Three-step setup - -The Claude Code plugin itself is just the glue — you still need the daemon binary on PATH and a mailbox name. - -### 1. Install the daemon (one-time, per machine) - -```sh -npm config set @kuns:registry=https://git.kuns.dev/api/packages/releases/npm/ -npm install -g @kuns/claude-mailbox -claude-mailbox install-autostart # registers per-OS autostart (no admin needed by default) -``` - -Verify with: - -```sh -claude-mailbox status # expect: Running -``` - -### 2. Install the plugin - -In Claude Code: +## Setup (two steps, all inside Claude Code) ``` /plugin marketplace add https://git.kuns.dev/releases/ClaudeMailbox /plugin install claude-mailbox@claude-mailbox +/claude-mailbox:mailbox-doctor ``` -### 3. Choose a mailbox name +The doctor command walks the rest: -Set the env var Claude Code will inherit (any unique name — your username, machine name, whatever): +1. checks whether the `claude-mailbox` binary is on `PATH` — installs it (`npm install -g @kuns/claude-mailbox`) if missing, asks before doing anything that might need elevation +2. checks the daemon status — runs `install-autostart` and/or `start` until it reports `Running` +3. ensures `CLAUDE_MAILBOX_NAME` is set in `.claude/settings.json` env — prompts for a name if not, writes it idempotently +4. runs a self → self smoke test to verify the round-trip works -```sh -# Windows (PowerShell, persistent) -setx CLAUDE_MAILBOX_NAME alice +Restart Claude Code after the doctor finishes (only needed if the mailbox name was newly written). Unread messages will then appear in context before every prompt. -# macOS / Linux (add to ~/.zshrc or ~/.bashrc) -export CLAUDE_MAILBOX_NAME=alice -``` +## Why a mailbox name? -Restart Claude Code so the new env var is picked up. +Each Claude session has an identity used to address peer sessions — like an email address. If you run a `backend` session and a `frontend` session in parallel, they need different names so they can send messages to each other. -## What happens at runtime +For a single Claude Code instance just wanting notifications, any stable kebab-case name works. The name lives in **per-project** `.claude/settings.json` env, so different worktrees / projects automatically get different mailboxes. + +## What the hook actually does Before every prompt the plugin runs `claude-mailbox check --hook`, which: - prints unread mailbox messages in a Claude-friendly format and marks them delivered, - stays **silent** when the inbox is empty or `CLAUDE_MAILBOX_NAME` is not set, -- emits a one-line setup hint when the daemon is unreachable (so missing step 1 is loud). +- emits a one-line setup hint when the daemon is unreachable, so a missing daemon is loud, not invisible. -## Sending yourself a message (smoke test) +Cost: one local HTTP round-trip plus Node coldstart per prompt (~100ms on Windows). + +## Commands + +| Command | What it does | +|---|---| +| `/claude-mailbox:mailbox-doctor` | Diagnose + auto-fix the full setup. | +| `/claude-mailbox:mailbox-status` | Read-only health check. No changes. | + +## Smoke test (manually, after doctor finishes) ```sh -claude-mailbox send --from probe --to alice --body "hello from the CLI" +claude-mailbox send --from probe --to --body "hello" ``` -Then start a new Claude Code session — the message should appear in context before Claude's first reply. +Then start a new Claude Code prompt — the message should appear in context before Claude's first reply. ## Uninstall ``` /plugin uninstall claude-mailbox@claude-mailbox npm uninstall -g @kuns/claude-mailbox -claude-mailbox uninstall-autostart # if you used it +claude-mailbox uninstall-autostart # if you used it ``` diff --git a/plugin/commands/mailbox-doctor.md b/plugin/commands/mailbox-doctor.md new file mode 100644 index 0000000..78c9edc --- /dev/null +++ b/plugin/commands/mailbox-doctor.md @@ -0,0 +1,85 @@ +--- +description: Diagnose and auto-fix the Claude-Mailbox setup (binary install, daemon autostart, mailbox name, smoke test). +allowed-tools: Bash, Read, Edit, Write +--- + +You are running the **Claude-Mailbox doctor**. Walk through these checks in order. After each check, print a one-line status (✓ / ✗) and the action you took. At the very end, print a final summary block. + +Throughout, prefer the dedicated tools (`Read`, `Edit`, `Write`) for files. Use `Bash` only for `claude-mailbox` subcommands, `npm`, `where`/`which`, and `cat /etc/os-release` style lookups. Never run `sudo` automatically — if elevation is needed, stop and ask the user how to proceed. + +--- + +## Step 1 — daemon binary on PATH + +Run: `claude-mailbox --version` + +- **Exit 0** → binary present. Record the version string. ✓ continue. +- **Command not found / non-zero exit** → binary missing. Tell the user the install command for their platform and ask before running it: + + | Platform | Install command | + |---|---| + | Windows | `npm install -g @kuns/claude-mailbox` (no admin) | + | macOS / Linux | `npm install -g @kuns/claude-mailbox` (may need `sudo` depending on Node setup; ask the user if `npm install` fails with EACCES) | + + Prerequisite: `npm config get @kuns:registry` should return `https://git.kuns.dev/api/packages/releases/npm/`. If it doesn't, set it first: + + ``` + npm config set @kuns:registry=https://git.kuns.dev/api/packages/releases/npm/ + ``` + + After install, re-run `claude-mailbox --version`. If it still fails, stop and report the error. + +## Step 2 — daemon autostart and running state + +Run: `claude-mailbox status` + +- **`Running`** → ✓ continue. +- **`Stopped`** → run `claude-mailbox start`. Re-check status. +- **`NotInstalled`** → run `claude-mailbox install-autostart`, then `claude-mailbox start`. Re-check status. + +If status doesn't become `Running` after the fix, stop and report what `status` and `start` printed. + +Sanity check: `curl -sf http://127.0.0.1:47822/health` (or use Bash to fetch it). Expect a JSON body with `"status":"ok"`. + +## Step 3 — mailbox name in project settings + +The hook reads the mailbox name from `$CLAUDE_MAILBOX_NAME`. Claude Code injects env vars from `.claude/settings.json` into hook commands, so the cleanest place to set it is per-project. + +1. Read `.claude/settings.json` in the current working directory (it may not exist yet — that's fine). +2. Check if `env.CLAUDE_MAILBOX_NAME` is set. +3. If **set**: ✓ continue, record the name. +4. If **not set**: + - Ask the user for a mailbox name. Suggest a default based on the cwd basename (e.g., for `C:\Private\Claude-Mailbox` suggest `claude-mailbox`). Names should be short, kebab-case-ish, unique among parallel Claude sessions. + - Read existing `.claude/settings.json` if present, otherwise start with `{}`. + - Set/merge `env.CLAUDE_MAILBOX_NAME` to the chosen name. Preserve any other existing settings. + - Write back with 2-space indentation. + - Tell the user they need to **restart this Claude Code session** for the env to take effect in the hook — but the smoke test below can still run because we'll pass `--name` explicitly. + +## Step 4 — smoke test + +Use the resolved name from step 3 (either pre-existing or just chosen). Run: + +``` +claude-mailbox send --from doctor-probe --to --body "ping from /claude-mailbox:mailbox-doctor" +claude-mailbox check --name +``` + +- The `check` output should be a JSON array containing exactly one message with `"from": "doctor-probe"` and that body. +- If yes: ✓ smoke test passed. +- If no (empty array, error, or wrong message): ✗ report what was returned. + +## Step 5 — final summary + +Print a compact block with these fields, one per line: + +``` +Claude-Mailbox doctor + binary: + daemon: Running | Stopped | NotInstalled (and what you did) + health: ok | unreachable + mailbox name: (source: existing | newly written to .claude/settings.json) + smoke test: passed | failed + restart hint: +``` + +If everything is ✓ and `restart hint: yes`, end with: "Restart Claude Code (or open a new session) so the UserPromptSubmit hook picks up `CLAUDE_MAILBOX_NAME`." If `restart hint: no`, end with: "You're good to go — unread messages will appear before your next prompt." diff --git a/plugin/commands/mailbox-status.md b/plugin/commands/mailbox-status.md new file mode 100644 index 0000000..9c6cc7f --- /dev/null +++ b/plugin/commands/mailbox-status.md @@ -0,0 +1,23 @@ +--- +description: Read-only Claude-Mailbox health check. No changes, no installs — just report. +allowed-tools: Bash, Read +--- + +Report the Claude-Mailbox setup status without making any changes. If something is wrong, **tell** the user but **do not** fix it — suggest `/claude-mailbox:mailbox-doctor` for that. + +Print exactly this block, filling in each line: + +``` +Claude-Mailbox status + binary: + daemon: + health: <"ok" if GET http://127.0.0.1:47822/health returns 200, else "unreachable"> + mailbox name: + pending: ` if name is set, else "n/a"> +``` + +End with one line: + +- All good → `Status: OK` +- Missing daemon or unset name → `Status: Setup incomplete. Run /claude-mailbox:mailbox-doctor to fix.` +- Daemon installed but stopped → `Status: Daemon is not running. Try \`claude-mailbox start\` or run /claude-mailbox:mailbox-doctor.`