Hooks
Install moshi-hook so Claude Code, Codex, OpenCode, Gemini, Cursor, Kimi, and Qwen can talk to Moshi — and unlock context detection, diff view, and browser preview.
moshi-hook is the companion daemon that lets local coding agents report useful events to Moshi. It runs on your host, installs agent hook config, keeps a local Unix socket for hook calls, and maintains a WebSocket connection back to Moshi for approvals and status updates.
The same daemon powers the in-app Diff viewer and Browser preview, and feeds the iOS Live Activity.
Supported agents
The installer can wire these agents:
- Claude Code.
- Codex CLI.
- OpenCode.
- Gemini CLI.
- Cursor.
- Kimi.
- Qwen Code.
The exact event surface differs by agent, but Moshi normalizes events into a small set of inbox categories.
Install on macOS
Open Settings -> Hooks in Moshi and copy your pairing token. Then run:
$brew tap rjyo/moshi$brew install moshi-hook$moshi-hook pair --token <token from Moshi>$moshi-hook install$brew services start moshi-hook
On macOS, secrets are stored in Keychain by default. If you pair from SSH and Keychain is locked, unlock it first or use file-backed storage:
$security unlock-keychain ~/Library/Keychains/login.keychain-db$moshi-hook pair --token <token from Moshi>$moshi-hook pair --token <token from Moshi> --store file
Install on Linux
$curl -fsSL https://getmoshi.app/install.sh | sh$moshi-hook pair --token <token from Moshi>$moshi-hook install$moshi-hook serve
For a permanent Linux setup, run moshi-hook serve under your process manager of choice.
Update
On macOS, refresh the tap and upgrade:
$brew update$brew upgrade moshi-hook$brew services restart moshi-hook
On Linux, re-run the install script and restart your moshi-hook serve process:
$curl -fsSL https://getmoshi.app/install.sh | sh
Pairing and installed agent hooks survive an upgrade — no need to re-pair or re-run moshi-hook install.
What install changes
moshi-hook install writes Moshi-owned entries into supported agent config files. It is designed to leave user-owned hooks alone.
Managed locations include:
- Claude Code:
~/.claude/settings.json. - Codex:
~/.codex/hooks.jsonand the needed Codex config flag. - OpenCode:
.opencode/plugins/moshi-hooks.tsin the current project. - Gemini:
~/.gemini/settings.json. - Cursor:
~/.cursor/hooks.json. - Kimi:
~/.kimi/config.toml. - Qwen:
~/.qwen/settings.json.
Use moshi-hook uninstall to remove Moshi-owned entries.
What needs moshi-hook serve
moshi-hook is both a CLI and a long-running daemon. Some features rely on the daemon being up (it owns the local Unix socket that agents post events to, and the gateway on 127.0.0.1:24543 that the iOS app forwards to); others are one-shot CLI calls that work whether or not the daemon is running.
Needs moshi-hook serve running:
- Agent event delivery: Claude Code, Codex, OpenCode, Gemini, Cursor, Kimi, and Qwen all post hook events to the local Unix socket. Without the daemon, events go nowhere.
- Inbox notifications, Live Activity, and approval round-trips.
- In-app Diff viewer opened from a Moshi terminal session (the iOS app talks to the gateway port
24543). - In-app Browser preview.
- Live multiplexer detection — the signal that drives the swipe-to-switch-window gesture and the "in tmux/Zellij/Herdr right now" badge on a session. SSH preflight alone only tells Moshi the multiplexer is installed; the daemon's gateway tells it whether the current session is inside one.
Works without moshi-hook serve:
moshi <dir>— the project tmux launcher. Pure exec, no daemon.moshi diff <dir>— the standalone diff viewer opened in your laptop browser. Spawns its own short-lived server and exits when you close it; coexists with the daemon by falling back to an ephemeral port if24543is already taken.moshi-hook context— one-shot terminal-context probe. Reads$TMUX_PANE,$ZELLIJ,$HERDR_ENVfrom your own shell and prints the detected kind, session, pane, and cwd as JSON. Useful for debugging the tmux swipe gesture.moshi-hook pair,install,uninstall,status,update,version— all daemon-less.
The short rule: anything that streams state to the iOS app or accepts pushes from a coding agent needs serve; anything you invoke once from a shell does not.
Inbox categories
Moshi forwards a deliberately small set of events:
Moshi's inbox keeps one active row per agent session. New events for the same session update the row instead of creating a long stack of notifications.
Third-party harnesses
Custom harnesses that are not installed through moshi-hook, such as oh-my-pi notification plugins, can use Moshi's compatibility webhook:
curl -X POST https://api.getmoshi.app/api/v1/agent-events \
-H "Authorization: Bearer $MOSHI_USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source": "opencode",
"eventType": "notification",
"sessionId": "omp-session-1",
"category": "task_complete",
"title": "Task complete",
"message": "oh-my-pi finished a turn",
"eventId": "omp-event-1"
}'
Use category values from the table above. approval_required events should include pendingActionId, expiresAt, and hostId only when the harness can also route decisions back to the running host. Host-resident daemons should prefer POST /api/v1/hosts/:hostId/events with the host secret.
Diagnostics
Useful commands:
$moshi-hook status$moshi-hook status --json$moshi-hook logs -f$moshi-hook usage --sync
If hooks are installed but Moshi does not update, check that moshi-hook serve or the macOS service is running and paired to the same Moshi account/token shown in the app.