Command Palette

Search for a command to run…

ZarynZaryn
Command reference·4 min read

Scripting and AI Agents

Use zev from scripts, CI, servers and AI agents: tokens, JSON output and the MCP server.

Every collaboration command in zev can be driven by a script, a CI job, or an AI agent just as well as by a person at a keyboard. The rules below are the contract such tools can rely on. ## The contract - **`--json` output goes to stdout**, as a single JSON document. Everything meant for humans — progress, prompts, warnings, errors — goes to stderr. Capturing stdout therefore captures only data. - **Exit code `0` means success, `1` means failure.** On failure, the explanation is on stderr and stdout is empty, so a failed command never produces output that could be mistaken for a result. - **Colors never leak into captured output.** They are turned off automatically whenever stderr is not a terminal, and always when the `NO_COLOR` environment variable is set. ```bash if result=$(zev pr list --json 2>/dev/null); then echo "$result" else echo "zev failed" >&2 fi ``` There is one deliberate exception to "stdout is empty on failure": asking to merge a pull request that is already merged exits with `1` and prints a machine-readable error, so a tool can tell this case apart from a real failure: ```json {"error":"pull request is already merged","status":"merged"} ``` ## Device login and MCP for AI agents An agent should never have an account of its own, and never your key. Let it sign in through you instead: ```bash zev login zaryn.xyz --device --client "Claude Code" ``` It prints a link and a code. You open the link, check the code, untick anything it doesn't need, and approve. The agent then holds its own token: named after it, limited to the scopes you approved, valid for 90 days, listed in Settings where you can revoke it, and recorded in the audit log under its own name. To give the agent Zaryn as tools, add the MCP server that ships with zev: ```bash claude mcp add zaryn -- zev mcp # Claude Code codex mcp add zaryn -- zev mcp # Codex ``` `zev mcp` offers `zaryn_*` tools for repositories, files, history, search, issues, pull requests, CI and advisories. It can open issues and comment (with `repo:write`); merging, deleting and changing settings are left to people. Agents can also start an account for a person (`zev login … --register <name> --email <address>`), but only the person can finish it, with the code from their inbox. See [Zaryn for AI agents](/agents). ## Never blocking on a prompt A command only prompts for information you did not supply, so passing everything as flags makes it fully non-interactive: - `zev issue --title <t> --desc <d>` creates an issue without prompting. - `zev issue comment <n> --body <text>` comments without prompting. - `zev pr --yes` opens a pull request using the drafted title and description, targeting `main`; add `--target <branch>` for a different target. - `zev issue close <n> --yes` and `zev pr merge <n> --yes` skip the confirmation. With `--json`, confirmations are skipped as well, since a prompt would hang a non-interactive session. ## Credentials without a login ```bash export ZEV_SERVER=zaryn.xyz export ZEV_TOKEN=zev_pat_... zev issue list --repo alice/project --json ``` `ZEV_SERVER` and `ZEV_TOKEN` take precedence over a saved login, and `--server` / `--token` take precedence over both. Combined with `--repo owner/name`, this lets a tool operate on any repository from any directory, with no local checkout at all. Reads on public repositories succeed anonymously, so an invalid token is only detected when a command needs to write — creating, commenting, closing, or merging returns HTTP 401 and exit code `1`. ## JSON shapes **`zev issue list --json`** ```json {"issues":[{"number":1,"title":"Login button misaligned","status":"open","author":"alice","created_at":"2026-09-22 23:38:51+03:30"}]} ``` **`zev issue view <n> --json`** combines the issue and its comment thread: ```json {"issue":{"number":1,"title":"...","description":"...","status":"open","author":"alice","created_at":"..."}, "comments":{"comments":[{"id":7,"author":"alice","body":"...","created_at":"..."}]}} ``` **`zev pr list --json`** ```json {"pull_requests":[{"number":1,"title":"Add notes on the fix","source_branch":"fix-login-button","target_branch":"main","status":"open","author":"alice","created_at":"..."}]} ``` `zev pr view <n> --json` returns a single pull request with the same fields plus `description`. **`zev status --json`** ```json {"branch":"main","staged":[],"modified":["a.py"],"deleted":[],"untracked":["notes.txt"]} ``` `branch` is `null` when HEAD is detached. ## Example: open a pull request and capture its number ```bash zev add . zev commit "Automated change" zev push number=$(zev pr --yes --json 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin)['number'])") echo "Opened pull request #$number" ``` ## Example: comment on every open issue ```bash zev issue list --json 2>/dev/null \ | python3 -c "import json,sys; [print(i['number']) for i in json.load(sys.stdin)['issues'] if i['status']=='open']" \ | while read n; do zev issue comment "$n" --body "Triaged automatically." --json > /dev/null done ``` ## Checking the working tree before acting ```bash dirty=$(zev status --json | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d['staged'])+len(d['modified'])+len(d['deleted']))") if [ "$dirty" != "0" ]; then echo "Uncommitted changes present; not switching branches." >&2 exit 1 fi zev checkout release ``` `zev checkout` performs this same safety check on its own and exits with `1` rather than overwriting anything, so the explicit check above is only needed when a tool wants to decide what to do before trying.