The previous skill assumed a happy-path environment and silently broke on real installs. Hardened the flow: - Always pass --@kuns:registry on every npm call, so the upgrade survives an unreadable user .npmrc (e.g. roaming HOME on a network share that npm can't access). - Treat /health as the ground truth, not `claude-mailbox status`. Status only reflects the autostart wrapper and silently lies when the inner process crashed at boot. - Capture state before changing anything (CLI version, autostart state, port, daemon /health version) so failures can roll back to "do nothing — old daemon keeps running". - Detect the "daemon reachable but autostart NotInstalled" case (manual foreground serve) and refuse to swap binaries from under it. - After restart, poll /health for up to 10s and verify the live daemon's version matches LATEST; on timeout, dump the platform- appropriate daemon log tail instead of just reporting "Stopped". - Reorder: install first, then stop+start. Downtime is paid only after the new binary is verified on disk.
7.2 KiB
description, allowed-tools
| description | allowed-tools |
|---|---|
| Update the Claude-Mailbox daemon to the latest published npm version and restart it. | Bash |
You are running the Claude-Mailbox update command. Update the @kuns/claude-mailbox npm package and restart the daemon. Never run sudo automatically — if elevation is needed, stop and ask.
Throughout, treat the daemon's /health endpoint as the ground truth for "is the daemon running and on what version", not claude-mailbox status (which only reflects the autostart wrapper). Always use the registry override flag --@kuns:registry=https://git.kuns.dev/api/packages/releases/npm/ on every npm call, so the upgrade works even when the user's .npmrc is unreachable (e.g. roaming $HOME on a network share).
Step 1 — current state (must run before anything is changed)
Run, in order, and remember each result:
claude-mailbox --version- Exit 0 →
CURRENT_CLI = <stdout trimmed>. - Non-zero → stop. The binary isn't installed; suggest
/claude-mailbox:mailbox-doctorand exit.
- Exit 0 →
claude-mailbox status- Record as
AUTOSTART_STATE ∈ { Running, Stopped, NotInstalled }.
- Record as
- Read the configured port. Try
~/.claude-mailbox/mailbox.json; if absent or noportfield, use 37849. Call thisPORT. - Probe
curl -sf -m 2 http://127.0.0.1:$PORT/health.- On success, parse JSON →
CURRENT_HEALTH_VERSION = .version. SetDAEMON_REACHABLE = true. - On failure →
CURRENT_HEALTH_VERSION = null,DAEMON_REACHABLE = false.
- On success, parse JSON →
Note any inconsistencies (e.g. AUTOSTART_STATE = NotInstalled but DAEMON_REACHABLE = true means a manually-started foreground daemon) — they affect Step 5.
Step 2 — latest published version
Run: npm view --@kuns:registry=https://git.kuns.dev/api/packages/releases/npm/ @kuns/claude-mailbox version
If that fails for any reason (network, registry), fall back to:
npm view --registry=https://git.kuns.dev/api/packages/releases/npm/ @kuns/claude-mailbox version
Record the result as LATEST. If both calls fail, stop and report the network/registry error.
Step 3 — compare
CURRENT_CLI === LATESTANDCURRENT_HEALTH_VERSION === LATEST(orDAEMON_REACHABLE = false) → print "Already up to date (vLATEST)." and stop.CURRENT_CLI === LATESTbutCURRENT_HEALTH_VERSION !== LATEST→ the CLI is fresh but the running daemon is the old binary. Tell the user "Binary already at LATEST but the running daemon is still v$CURRENT_HEALTH_VERSION — restart needed." Then jump to Step 5 (no npm install).- Otherwise → tell the user
CURRENT_CLI→LATESTand ask for confirmation before proceeding.
Also warn before confirmation if AUTOSTART_STATE = NotInstalled AND DAEMON_REACHABLE = false:
Heads-up: autostart is not installed and no daemon is reachable on port $PORT. After the upgrade I can install the new binary, but I won't be able to start the daemon automatically — you'd need
/claude-mailbox:mailbox-doctorto wire up autostart, or runclaude-mailbox servemanually. Proceed anyway?
Step 4 — install the new package
On user confirmation:
npm install -g @kuns/claude-mailbox@latest --@kuns:registry=https://git.kuns.dev/api/packages/releases/npm/
- The scope override is mandatory — do not omit it, even if
npm config get @kuns:registrylooks right. It costs nothing and protects against unreachable user-level.npmrc. - On Linux/macOS the install may fail with EACCES. Do not run sudo automatically. Stop and ask how the user wants to proceed (e.g.
sudo, switch to nvm/fnm). - On any other failure, stop and report. Do not touch the daemon — leaving the old daemon running is the safe rollback.
After install, run claude-mailbox --version and confirm it now reports LATEST. If not (PATH shadowing, stale which), stop and report — the daemon is still on the old version, which is fine to keep running.
Step 5 — restart the daemon
Now swap the daemon over to the new binary.
- Stop the existing daemon if anything is running:
- If
AUTOSTART_STATE = Running→claude-mailbox stopand wait up to 5s for/healthonPORTto start failing (poll once per second). - If
DAEMON_REACHABLE = truebutAUTOSTART_STATE = NotInstalled→ a foreground/manual daemon is running. Tell the user:A daemon is reachable on port $PORT but autostart is not installed, so I can't stop it. Stop the manual
claude-mailbox serveprocess yourself, then re-run this command to finish the restart. Then stop here. - Otherwise nothing to stop.
- If
- Start the daemon, picking the path that matches
AUTOSTART_STATE:RunningorStopped(i.e. autostart is installed) →claude-mailbox start.NotInstalled→ skip the start. After the loop below times out, tell the user to run/claude-mailbox:mailbox-doctorto install autostart, then exit.
- Poll
curl -sf -m 1 http://127.0.0.1:$PORT/healthup to 10 times with 1s sleeps. Stop polling as soon as one returns JSON with"status":"ok". - Outcome:
- Health came up AND
version === LATEST→ ✓ proceed to Step 6. - Health came up but
version !== LATEST→ the wrapper started an old binary somewhere on PATH. Dumpwhich claude-mailbox/where claude-mailboxand stop with that info. - Health did not come up → dump the most recent daemon log to help diagnose. Try, in order, the first one that exists:
- Windows Scheduled Task / fallback:
%LOCALAPPDATA%\ClaudeMailbox\logs\daemon.log(tail the last 40 lines) - Windows Service variant:
Get-WinEvent -ProviderName ClaudeMailbox -MaxEvents 20viapowershell -NoProfile -Command "..." - macOS launchd:
~/Library/Logs/ClaudeMailbox/daemon.log(last 40 lines) - Linux systemd-user:
journalctl --user -u claude-mailbox -n 40 --no-pagerIf none exist or all are empty, just say "No daemon logs found; tryclaude-mailbox servein a terminal to see the error directly." Stop with that diagnostic; do not pretend the update succeeded.
- Windows Scheduled Task / fallback:
- Health came up AND
Step 6 — summary
Print exactly this block:
Claude-Mailbox update
previous version: <CURRENT_CLI>
new version: <claude-mailbox --version output>
daemon health: ok (v<version from /health>) | unreachable
daemon autostart: Running | Stopped | NotInstalled
pending messages: <total pending across all mailboxes — sum of pendingForYou from `claude-mailbox list`>
End with one of:
- New CLI version matches
LATESTAND/healthreturnsversion === LATEST→ "Update complete." - Anything else → "Update incomplete: <the first concrete failure from Step 4 or 5>."
Operating notes
- Always use the scope override flag (
--@kuns:registry=...) on every npm call. The user's.npmrcmay be on a network drive that npm can't read. - Never rely on
claude-mailbox statusalone to decide "the daemon is fine". Always cross-check with a/healthprobe — the status command only reflects whether the autostart task is in a Running state and doesn't notice if the process inside crashed at boot. - Never run
npm installwithout first locking in the current state. If the install fails, the safe rollback is to do nothing — the old daemon is still running. - Never
claude-mailbox stopbefore the install succeeds. Downtime is paid only after we know the new binary is on disk.