--- description: Diagnose and auto-fix the Claude-Mailbox setup (binary install, port-conflict detection, daemon autostart, smoke test, optional base-prefix). allowed-tools: Bash, Read, Edit, Write --- You are running the **Claude-Mailbox doctor**. Walk through these checks in order. After each step, print a one-line `✓` / `✗` with the action you took. End with a summary block. Use `Bash` only for `claude-mailbox` subcommands, `npm`, `where`/`which`, and HTTP probes. Use `Read`/`Edit`/`Write` for `.claude/settings.json` and `mailbox.json`. Never run `sudo` automatically — if elevation is needed, stop and ask. ## Step 1 — daemon binary on PATH Run: `claude-mailbox --version` - **Exit 0** → ✓ record the version. Continue. - **Command not found** → binary missing. Install path: | Platform | Command | |---|---| | Windows | `npm install -g @kuns/claude-mailbox` (no admin) | | macOS / Linux | `npm install -g @kuns/claude-mailbox` (may fail with EACCES — never run sudo automatically; ask the user) | Prerequisite: `npm config get @kuns:registry` must point at `https://git.kuns.dev/api/packages/releases/npm/`. If not: ``` 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. ## Step 2 — port-conflict check (before autostart!) Default port is 47822. Probe whether anything is already on it: ``` curl -sf http://127.0.0.1:47822/health ``` - **Returns a JSON body with `"status":"ok"` and a `version` field that matches `claude-mailbox --version`** → it's already our daemon, ✓ skip to Step 4. - **Returns 200 with `"status":"ok"` but a different `version`** → it's an older claude-mailbox; treat as running, ✓. - **Returns non-200, non-JSON, or any other foreign response** → **port conflict**. Some other process owns 47822. - **Connection refused** → port is free, ✓ continue to Step 3. If port conflict detected: 1. Tell the user which process holds the port (Windows: `Get-NetTCPConnection -LocalPort 47822 | Select-Object OwningProcess`, then `Get-Process -Id `; macOS/Linux: `lsof -i :47822`). 2. Pick a free port. Default suggestion: **47900**. Verify it's free: `curl -sf http://127.0.0.1:47900/health` should fail with connection refused. 3. Read `~/.claude-mailbox/mailbox.json` (create empty `{}` if missing) and merge `{"port": }`. Write back. 4. Also write the override into `.claude/settings.json` env so the plugin's hooks find the right URL: ```json "env": { "CLAUDE_MAILBOX_URL": "http://127.0.0.1:" } ``` Merge into existing env, preserving other keys. 5. Mark `restart_needed = true`. ## Step 3 — daemon autostart and running state Run: `claude-mailbox status` - `Running` → ✓ continue. - `Stopped` → `claude-mailbox start`, re-check. - `NotInstalled` → `claude-mailbox install-autostart`, then `claude-mailbox start`, re-check. **Behavior on `install-autostart`:** The CLI tries a Scheduled Task first (`schtasks /RL LIMITED`, no admin). If Windows Group Policy returns "Access is denied", it falls back transparently to an `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` registry entry plus a hidden `node serve` process — same per-user persistence, no admin needed. The chosen mechanism is recorded in `~/.claude-mailbox/autostart-mode` and respected by `status`/`start`/`stop`/`uninstall-autostart`. If `install-autostart` still fails after both attempts (very rare — would mean both `schtasks` and `reg add` are blocked), stop and report what `status` and `start` printed. ## Step 4 — health probe Hit `http://127.0.0.1:/health` (use the configured port, not necessarily 47822). Expect a JSON body with `"status":"ok"` AND a `version` matching `claude-mailbox --version`. If unreachable or version mismatch, stop and report. ## Step 5 — mailbox identity (base prefix) **No prompt by default.** Each Claude Code session gets a unique mailbox name auto-derived from its `session_id` (e.g., `claude-a8b3c1d2`). Read `.claude/settings.json` and look for `env.CLAUDE_MAILBOX_NAME`. - If set → ✓ "Mailbox prefix is ``." (real name will be `-`). - If unset → ✓ "Mailbox name will be auto-derived (`claude-`)." Ask once: *"Want to flavor your mailbox names with a memorable prefix (e.g., `backend`, `frontend`)? (yes / no / ``)"* On yes/explicit name: merge `env.CLAUDE_MAILBOX_NAME = ` into `.claude/settings.json`, preserving other keys. Mark `restart_needed = true`. ## Step 6 — smoke test Use two ephemeral names — we don't need the real session name here: ``` claude-mailbox send --from doctor-probe-a --to doctor-probe-b --body "ping from doctor" claude-mailbox check --name doctor-probe-b ``` (If the port was changed in Step 2, pass `--url http://127.0.0.1:` to both.) The `check` output must be a JSON array with one message: `from: doctor-probe-a`, body matches. ✓ on success, ✗ otherwise. ## Step 7 — summary ``` Claude-Mailbox doctor binary: daemon: Running (port: , what you did if anything) health: ok port conflict: none | resolved (moved from 47822 to ) base prefix: smoke test: passed | failed restart hint: yes if restart_needed, otherwise no ``` End with one of: - All ✓ and no restart needed → "Setup is healthy. Your mailbox name this session will be revealed by the SessionStart hook on next session start." - All ✓ and restart needed → "Restart Claude Code (or open a new session) so the SessionStart hook picks up the new env values." - Anything ✗ → "Setup incomplete: ."