Get Moshi
hooks

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.

updated yesterday22 min readpage 21 / 32

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:

macOS
$brew tap rjyo/moshi
$brew trust 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:

headless macOS
$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

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:

macOS
$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:

Linux
$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.json and the needed Codex config flag.
  • OpenCode: .opencode/plugins/moshi-hooks.ts in 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 if 24543 is already taken.
  • moshi-hook context — one-shot terminal-context probe. Reads $TMUX_PANE, $ZELLIJ, $HERDR_ENV from 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:

approval_required
An agent needs permission or a user answer.
task_complete
A turn or idle task finished.
session_started
A new agent session or first prompt started.
tool_running
A tool started running, throttled to reduce noise.
tool_finished
A tool finished, throttled to reduce noise.

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 legacy compatibility endpoint:

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. This endpoint is direct-only and does not support unified: true.

warn

This legacy endpoint is scheduled for retirement on June 15, 2026. After that date, it returns 410 legacy_agent_events_retired.

Host-resident daemons should prefer POST /api/v1/hosts/:hostId/events with the host secret; custom unified pushes should use POST /api/webhook with unified: true.

Host status dot

When Show hook status is on, each saved host carries a small colored dot on its server icon, reflecting the last moshi-hook probe. The same five states drive the filter chips above the host list:

running
Green. Installed, the daemon is up, and its gateway answers on 127.0.0.1:24543 — context detection, diff, and browser preview all work.
update
Amber. Running, but older than the moshi-hook version this build of Moshi expects. Upgrade it.
wrong port
Red. The daemon is running but nothing answers on 127.0.0.1:24543, usually because it bound to a different port. Restart it on the default address.
not running
Solid grey. The binary is installed but the daemon is not up. Start it.
not installed
Hollow grey. Moshi could not find moshi-hook on the host (or the probe could not run).

Tapping a host's dot opens a sheet that explains the state and, when something needs attention, gives copy-paste fix commands for macOS or Linux. You can turn the dots off in Moshi's settings.

Moshi runs the probe over a non-interactive login shell and resolves moshi-hook (and the moshi alias) from ~/.local/bin, ~/bin, /opt/homebrew/bin, /usr/local/bin, and /opt/local/bin, then from your login shell's own PATH. A standard Homebrew or install.sh install lands in one of those directories, so it is detected without any extra PATH setup. The running vs not running call is authoritative: Moshi opens a real connection to the gateway port rather than trusting a process name.

A host that shows not installed when moshi-hook really is installed usually means a custom install location — see moshi-hook is installed but shows as not installed.

Diagnostics

Useful commands:

moshi-hook checks
$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.