Zeph Docs
CLI & SDK

Command Reference

Every zeph command, its flags, exit codes, and environment variables.

# Send a notification
zeph notify --title "Deploy done" --body "v2.1.0 shipped"

# Send with priority
zeph notify --title "Build failed" --priority high --url https://ci.example.com/123

# List recent pushes
zeph list
zeph list --limit 10 --type note

# Dismiss a push
zeph dismiss push_01JXY...
zeph dismiss --all

# Rename the current agent session (shows in the app's Agents list)
zeph rename "Prod deploy"
zeph rename --clear                # reset to the default name

# Test connection
zeph test

# Run an agent in a named tmux session, so the listener can reach it
zeph cc                       # claude
zeph codex                    # codex
zeph cursor                   # cursor-agent (Cursor CLI, not the IDE)
zeph gemini                   # gemini

# Run the resident listener (foreground; background it as you like)
zeph listener
zeph listener --ws-url wss://...   # override config

# JSON output
zeph notify --title "Hello" --json

Commands

CommandDescription
loginBrowser sign-in: auto-fetch API key and hook into ~/.zeph/config.json over a localhost loopback (--web-url, --timeout). No copy-paste
install (alias: setup)One-command setup: detect agents, save config, install rules, hooks, and MCP. With no saved config it opens browser login automatically. --only claude,cursor,… skips the picker
uninstallRemove Zeph from all detected agents (--dry-run, --purge)
verifyCheck installation health across detected agents (--ping for a live API call)
check-updateCheck whether a newer Zeph version is on npm
notifySend a push notification
listList recent push notifications
dismiss <id>Dismiss a push, or --all
rename <name>Set the current agent session's display name in the app. Run it inside a zeph cc session; --clear resets. It auto-detects the tmux session and this machine's listener device id, so the alias lands on the right device
testSend a test push to confirm the API key and connection work. Your devices actually buzz — this is a real notification, not a dry run
cc · codex · cursor · geminiRun the agent in a zeph-<project> tmux session, auto-suffixed -2, -3, … on attached collisions. Auto-spawns the background listener on first invocation so the phone picker just works. Trailing arguments pass through to the agent. These four are the tmux-drivable agents, a smaller set than the eight zeph install configures — zeph cursor launches cursor-agent, Cursor's terminal TUI, not the IDE
listenerUsually unnecessary — zeph cc autospawns it. Resident daemon: subscribes via WebSocket, reports tmux session inventory every 5 seconds, injects agent.command pushes into the matching session
remote-hook <agent>Not run by hand. The prompt-submit hook that zeph install wires into Codex and Gemini calls it to flag a message as phone-injected. See CLI & SDK → Remote Control

Notify options

FlagDescription
--title <text>Push title (default: "Task done")
--body <text>Push body (default: "<project> · <branch>" if cwd is a git repo, else "<project>")
--url <url>URL to include
--type <type>Push type: note, link, file, hook
--priority <p>Priority: low, normal, high, urgent
--device <id>Target device ID
--session <id>AI session ID, so the push threads into that session's chat (or the ZEPH_SESSION_ID env var)
--autoApply the push gate before sending — honors the /zeph-quiet and /zeph-loud push-mode dial, per project or machine-wide with --global. A gated-out push exits silently with code 0
--pushmode-default <m>The mode --auto assumes when the project has no dial: quiet (built-in), normal, or loud. A dial the user set always wins
--marker <m>Push Signal marker for --auto: skip, push, high
--tools <n>, --nonreadonly <n>Turn tool counts feeding --auto's heuristic (the defaults assume real work)

The defaults are tuned for hook-driven invocations — for example a Stop hook calling zeph notify --title "Task done" with no body. You see which project and branch finished without writing per-IDE wrappers. Pass --body "" explicitly to suppress it.

Listener options

FlagDescription
--ws-url <url>WebSocket endpoint (or the ZEPH_WS_URL env var, or wsUrl in ~/.zeph/config.json)
--key <api-key>API key (or the ZEPH_API_KEY env var)
--base-url <url>REST API base URL (or the ZEPH_BASE_URL env var, or baseUrl in ~/.zeph/config.json)
--stopStop the running daemon and clear its PID and version stamps
--restartStop it and relaunch detached, logging to ~/.zeph/listener.log

The listener reconnects with exponential backoff plus jitter, from 1 second to a 30-second cap. The heartbeat is a ping every 25 seconds with a 10-second pong timeout. On an authentication failure close (4001, 4002, 4003) the listener exits with code 3 instead of looping forever — fix the key and restart.

List options

FlagDescription
--limit <n>Number of pushes (1–20, default 5)
--type <type>Filter by push type

Global options

FlagDescription
--key <api-key>API key (or the ZEPH_API_KEY env var)
--base-url <url>API base URL (or the ZEPH_BASE_URL env var)
--jsonOutput JSON format
--versionPrint version

Mute and push mode

Both live as state files under ${XDG_STATE_HOME:-~/.local/state}/zeph, keyed by a cksum hash of the project directory. Claude Code's /zeph-mute, /zeph-quiet, /zeph-loud, and /zeph-normal write them; the CLI reads them — mute on every notify, push mode on --auto.

Notifications are silently skipped when a mute file exists for the current project:

STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/zeph"
HASH=$(printf '%s' "$PROJECT_DIR" | cksum | cut -d' ' -f1)

# Mute (created by /zeph-mute in the Claude Code plugin)
mkdir -p "$STATE_DIR" && touch "$STATE_DIR/muted-$HASH"

# Unmute
rm -f "$STATE_DIR/muted-$HASH"

Push mode is a one-word file — quiet, loud, or normal — resolved in this order, first hit wins:

OrderFileSet by
1$STATE_DIR/pushmode-<hash>/zeph-quiet · /zeph-loud · /zeph-normal
2/tmp/zeph-pushmode-<hash>Older versions, honored only when you own the file
3$STATE_DIR/pushmode-defaultThe --global form of any dial — the machine-wide default
4--pushmode-default <mode>The calling hook (the installed ones pass normal)
5(nothing above)quiet

Row 5 changed. An install with no dial anywhere used to be normal. It is now quiet, so upgrading turns the routine per-turn push off until you run /zeph-normal.

Row 4 is why the hooks this CLI installs are unaffected: they name normal themselves, since a hook that supplies no turn counts also supplies no high marker, and quiet would make it permanently silent rather than merely quieter. Row 4 sits below the state files on purpose — the flag names a default, it does not override a dial the user set.

A dial file that exists but reads empty resolves to normal, not to row 5. An empty file is a failed write, and resolving breakage to silence leaves no symptom to debug.

Mute has no -default form on purpose: it is keyed on presence, not content, so a global mute could never be lifted for a single project.

Legacy /tmp/zeph-muted-<hash> files are still honored when owned by the current user — the state directory moved out of world-writable /tmp.

The CLI checks CLAUDE_PROJECT_DIR, CURSOR_PROJECT_DIR, and WINDSURF_PROJECT_DIR, and falls back to the cwd.

Exit codes

CodeMeaning
0Success
1General error
2Quota exceeded
3Authentication failed (also: listener auth close 4001, 4002, 4003)
127A required external binary such as tmux or claude was not found on PATH

Environment variables

VariableDescription
ZEPH_API_KEYAPI key, used when --key is not provided
ZEPH_HOOK_IDHook ID for install and verify, consulted when --hook is not passed. The two-way MCP tools (zeph_ask and friends) need one
ZEPH_BASE_URLAPI base URL (default: https://api.zeph.to/v1)
ZEPH_WS_URLWebSocket endpoint for zeph listener. No default — required
ZEPH_TMUX_SOCKETExplicit tmux socket path for the listener, skipping auto-discovery. Use it when your tmux runs with -L <name> or a custom -S <path>
ZEPH_SESSION_IDAI session ID, used when --session is not provided

On this page