Get Moshi
hooks

Hooks

Install moshi-hook so supported coding agents can talk to Moshi — and see which integrations include inbox events, approvals, and the native Chat View.

updated 2 days ago9 min readpage 22 / 37

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.

For every persistent daemon option—including background discovery, usage polling, notification suppression, and Browser Preview port filtering—see Hook settings.

The same daemon powers the in-app Diff viewer and Browser preview, and feeds the iOS Live Activity.

New to agent workflows? Best iOS Terminal App for AI Coding Agents covers why hooks, mosh, and push matter, and Moshi Agent Hooks is the deep dive on the inbox, Live Activities, and Apple Watch.

Supported agents

Hook support and Chat View support are separate. Hooks & inbox means moshi-hook can install the agent's integration and normalize its lifecycle events for the inbox, notifications, Live Activity, and supported approval flows. Chat View means Moshi can also render that agent's live terminal session as a native conversation with messages and tool cards.

Agent
Hooks & inbox
Chat View
Claude Code
Codex CLI
OpenCode
Antigravity
Cursor
Kimi
Qwen Code
Grok Build
Pi
Oh My Pi (OMP)
Hermes
What Chat View looks likeA live Codex session rendered as Chat View — the same terminal transcript shown as messages, tool cards, and mini diffs.

Chat View is currently experimental and requires a live host-gateway connection to the agent's terminal session. See Chat View for setup, the interface, transport details, and current limitations.

The exact hook event surface differs by agent, but Moshi normalizes events into the same small set of inbox categories. Any other CLI still runs normally in Moshi's terminal; it just does not get the agent-aware features in this table.

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 (including WSL)

Same steps on a bare-metal Linux box, a VPS, or WSL on Windows — enable OpenSSH in the distro, then:

Linux / WSL
$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 or WSL 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.
  • Antigravity: ~/.gemini/config/hooks.json (or $ANTIGRAVITY_CONFIG_DIR/hooks.json).
  • Cursor: ~/.cursor/hooks.json.
  • Kimi: $KIMI_CODE_HOME/config.toml (defaults to ~/.kimi-code/config.toml; legacy Kimi CLI uses $KIMI_SHARE_DIR/config.toml).
  • Qwen: ~/.qwen/settings.json.
  • Grok Build: ~/.grok/hooks/moshi-hooks.json.
  • Pi: ~/.pi/agent/extensions/moshi-hooks.ts.
  • OMP: ~/.omp/agent/extensions/moshi-hooks.ts (or the selected OMP profile).
  • Hermes: ~/.hermes/plugins/moshi-hooks and the plugin entry in ~/.hermes/config.yaml.

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: every agent in the supported matrix posts 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.

Data privacy

moshi-hook is built so that the heavy, sensitive data stays between your host and your phone, and only small notification summaries touch Moshi's server.

Stays local, or goes directly between host and app:

  • Full Claude Code and Codex transcripts. When you open a session's conversation in Moshi, the transcript streams from the host to the phone through the SSH-forwarded local gateway — it never passes through the Moshi backend.
  • Diff payloads. The Diff viewer reads changes locally on the host and serves them to the app over the same local gateway.
  • Your source files. File contents are never part of the cloud agent-event payload.
  • Terminal traffic. Your actual sessions run over SSH, Mosh, or your Tailscale tailnet, directly to the host.

Reaches Moshi's server:

  • Notification and inbox event summaries — the inbox categories above.
  • Up to 200 characters of your prompt, used as the event body.
  • Up to 80 characters of the assistant's response, used as the event title.
  • The command or question behind an approval request, up to 256 characters, so you can decide from the notification.
  • Metadata: project name, session ID, agent, model, tool name, terminal identifiers, account ID, and context-window percentage.
  • Pairing, usage synchronization, approval decisions, and WebSocket control traffic.

Everything sent to the server is what you see rendered in a push notification or inbox row — nothing more is collected. For how the app itself stores credentials and what iCloud sync covers, see Security and sync.

Opt out of usage collection

Background usage collection (rate-limit polls on the host and snapshot uploads) is optional. To stop it on a host without unpairing:

moshi-hook set usage-collection off
brew services restart moshi-hook                 # macOS / Homebrew
systemctl --user restart moshi-hook.service      # Linux / systemd

Approvals, inbox events, Chat View, and the local gateway keep working. Only the Usages rings stop updating from that host until you re-enable collection or run moshi-hook usage --sync by hand. See Agents & Usages.

Third-party harnesses

Custom harnesses that are not installed through moshi-hook, such as oh-my-pi notification plugins, can send completion messages through the custom webhook:

curl -X POST https://api.getmoshi.app/api/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "token": "YOUR_API_TOKEN",
    "title": "Task complete",
    "message": "oh-my-pi finished a turn"
  }'

Add "unified": true to notify every opted-in device on the same Moshi license. Host-resident daemons that need structured agent state, Live Activities, or approval routing should use POST /api/v1/hosts/:hostId/events with the host secret.

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
$moshi-hook set
$moshi-hook set suppress-push-while-unlocked on

The human-readable status output includes the tmux, Zellij, and Herdr paths resolved by the running daemon. Use it to spot a service PATH mismatch; status --json deliberately skips that daemon lookup. See Hook settings for every moshi-hook set option, or A multiplexer works in the shell but Moshi still misses it to distinguish a daemon-path problem from an SSH-preflight problem.

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.