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" --jsonCommands
| Command | Description |
|---|---|
login | Browser 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 |
uninstall | Remove Zeph from all detected agents (--dry-run, --purge) |
verify | Check installation health across detected agents (--ping for a live API call) |
check-update | Check whether a newer Zeph version is on npm |
notify | Send a push notification |
list | List 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 |
test | Send 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 · gemini | Run 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 |
listener | Usually 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
| Flag | Description |
|---|---|
--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) |
--auto | Apply 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
| Flag | Description |
|---|---|
--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) |
--stop | Stop the running daemon and clear its PID and version stamps |
--restart | Stop 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
| Flag | Description |
|---|---|
--limit <n> | Number of pushes (1–20, default 5) |
--type <type> | Filter by push type |
Global options
| Flag | Description |
|---|---|
--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) |
--json | Output JSON format |
--version | Print 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:
| Order | File | Set 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-default | The --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
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Quota exceeded |
| 3 | Authentication failed (also: listener auth close 4001, 4002, 4003) |
| 127 | A required external binary such as tmux or claude was not found on PATH |
Environment variables
| Variable | Description |
|---|---|
ZEPH_API_KEY | API key, used when --key is not provided |
ZEPH_HOOK_ID | Hook ID for install and verify, consulted when --hook is not passed. The two-way MCP tools (zeph_ask and friends) need one |
ZEPH_BASE_URL | API base URL (default: https://api.zeph.to/v1) |
ZEPH_WS_URL | WebSocket endpoint for zeph listener. No default — required |
ZEPH_TMUX_SOCKET | Explicit 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_ID | AI session ID, used when --session is not provided |