@zephyrcloudio/harness-cli
The ze-harness command-line client and its embedded-or-remote composition edge. Requires Node.js
26.7 or later.
Command help and shell completion scripts come from Effect 4's CLI command tree. Run
ze-harness --help or ze-harness session --help to inspect commands and flags.
Run ze-harness completions --help for installation examples. Generate a script without
starting the daemon (redirect the script to a file):
ze-harness completions bash > ~/.local/share/bash-completion/completions/ze-harness
ze-harness completions zsh > ~/.zsh/completions/_ze-harness
ze-harness completions fish > ~/.config/fish/completions/ze-harness.fish
For Zsh, add ~/.zsh/completions to fpath before running compinit. Bash needs
bash-completion installed; Fish loads its completion directory automatically.
ze-harness
ze-harness tui <SESSION_ID>
ze-harness --config ze-harness.config.json run "hello"
ze-harness --remote http://127.0.0.1:4317 --server my-server chat <SESSION_ID>
Interactive TUI
Run ze-harness with no arguments in a terminal to open the interactive TUI. It launches with
--experimental-ffi; the CLI re-executes itself automatically when the flag is missing.
ze-harness
ze-harness tui
ze-harness tui <SESSION_ID>
ze-harness -s <SESSION_ID>
Use -s <SESSION_ID> (or --resume <SESSION_ID>) to resume a Session. The --session flag
on session fork assigns the new fork's ID; it does not resume a Session.
The opening screen has a single input. Type a prompt and press Enter to start a Session, or press Ctrl+X O to open an existing Session by ID.
From a source checkout, pnpm clean:host stops the local daemon and moves Ze Harness's XDG
config, data (including Sessions and saved provider logins), cache, and ~/.ze-harness into a
recoverable backup under ~/.Trash on macOS (or ~/.local/share/ze-harness-backups elsewhere).
It does not change project config files or shell-provided API keys. If a provider key is exported
in your shell, it can still make models available after the reset.
On Windows, this source-checkout helper still uses the Unix-style default data path.
After migration, set XDG_DATA_HOME to the absolute native data parent (normally
$env:LOCALAPPDATA) before running it so it backs up the data rather than the legacy junction.
On first launch with no usable model, the TUI opens Connect provider automatically. When a
provider is already connected (for example through a shell-exported API key), it skips the prompt.
It also opens when no model provider is available. Run ze-harness tui --onboarding
to open provider setup again on demand (ze-harness --onboarding also works). Press
Ctrl+X P (or Ctrl+Alt+P) to reopen it later without leaving the TUI. Choose OpenAI's Codex login
to open the displayed URL and enter its device code, or choose an API key connection. For API keys,
the TUI briefly suspends while its terminal prompts securely for the key. After connection, it refreshes models.
Ctrl+Alt+O hides optional first-run tips on future launches, independently of the one-time provider prompt; the connection action remains visible until a provider is available. A missing provider still needs to be connected
before sending a prompt. The preference is stored alongside model favorites under
model-preferences.json in the durable state directory.
Local skill discovery includes ~/.codex/skills, ~/.claude/skills, ~/.agents/skills,
$XDG_CONFIG_HOME/opencode/{skill,skills}, and ~/.ze-harness/skills, plus matching
project-directory sources. Additional directories can be set in the host's skills configuration.
Keyboard controls. Ctrl+X is the leader key: press it, then the second key.
- Enter — send a prompt, follow up on the running Turn, or accept the highlighted slash command
- Ctrl+V (also Ctrl+X V) — paste an image or text from the local clipboard
- Ctrl+X R — remove the last attached image; click an attachment to remove it
- Shift+Enter (also Ctrl+J or Alt+Enter) — newline in composer
- Tab — complete the selected slash command when command suggestions are open, otherwise cycle the primary agent (Shift+Tab cycles back)
- ↑/↓ (also Ctrl+P / Ctrl+N) — move in the open suggestion menu; Esc hides it
- Esc — close overlay, cancel a pending paste, hide the suggestion menu, cancel the active Turn, or return to the parent Session from a sub-agent, in that order
- Ctrl+X M / A / K — open the model, agent, or skill picker
- Ctrl+X E — cycle the selected model's reasoning effort
- Ctrl+X P (or Ctrl+Alt+P) — connect a provider
- Ctrl+X S — list Sessions of this project, newest activity first
- Ctrl+X N — start a new Session
- Ctrl+X C — compact the Session context
- Ctrl+X F — open the fork picker
- Ctrl+X G — list sub-agents; Ctrl+X ↓ opens the first one directly
- Ctrl+X T — toggle details for the latest tool call
- Ctrl+X O — open a Session by ID (Ctrl+O still works on the opening screen)
- Ctrl+C — quit; the Session keeps running in the background
- Ctrl+X Q or Ctrl+D — quit as well;
/exit(also/quit,/q) does the same from the composer - ↑/↓ · Enter · Tab · Ctrl+R · Esc — move, select, favorite, refresh, and close in the model picker
- ↑/↓ · Enter · Ctrl+R · Esc — move, select, refresh, and close in the agent picker
- ↑/↓ · Tab · Space/Enter · Ctrl+R · Esc — move, details, toggle, refresh, and close in the skill picker
- ↑/↓ · Enter · Esc — move, select, and close in the fork and sub-agent pickers
- ↑/↓ · Enter · Ctrl+R · Esc — move, select, refresh, and close in the session picker
- ←/→/Tab · ↑/↓ · Enter · Esc — choose, scroll, select, and reject (or go back) in permission requests
- Ctrl+B — detach foreground sub-agents to the background (otherwise moves left)
- Alt+↑ — return to the parent Session from a sub-agent
- Alt+← / Alt+→ — move between sub-agent siblings
- Ctrl+Alt+K — pin or unpin the shortcut panel; it also appears while a leader sequence is pending
Paste screenshots with Ctrl+V on the opening screen or in a conversation. You can also
paste or drop a local PNG, JPEG, GIF, or WebP file path (one file per paste). Images appear
above the input; press Enter to send them with your text, or on their own. Images stay
attached if submission fails and clear when you switch Sessions. Press Esc to cancel a
pending paste and continue editing. Existing /image, /base64, and /send draft commands also
work with pasted attachments.
Clipboard reading uses macOS system tools, PowerShell on Windows/WSL, or wl-paste
(from wl-clipboard) / xclip on Linux. It runs on the machine running the TUI, including
when connected to a remote Ze server. If your terminal intercepts Ctrl+V, use Ctrl+X V for image clipboard reads.
Ordinary terminal paste still handles text and file paths.
In an SSH terminal without access to a graphical clipboard, paste a path available to the TUI.
Selecting text with the mouse or clicking the short Session ID copies to the local clipboard
through the same system tools (pbcopy, PowerShell, wl-copy/xclip/xsel), plus an OSC 52
escape for terminals that honor it, including over SSH.
The TUI shows you for prompts carrying this machine's stable author identity and a short
author ID for other writers. Entries written before authorship fall back to the connection
match: you for the current client, a short Client ID otherwise.
Permission requests appear inline. Reply with Allow once, Allow always, or Reject;
Esc rejects the open request. The first durable reply wins. Allow always confirms the exact Session patterns proposed by the Tool.
The status row shows when approval or a response is required. Brief stream disconnects resume from
the last update without a notice. A longer outage shows Reconnecting within about two seconds and
keeps its error above the composer until the stream catches up; meanwhile active Turns refresh once
a second so pending requests still appear, and the working spinner pauses. A stream that cannot
resume is restarted with backoff and reloads Session history.
Running tool activity appears as one collapsed line. Click a line to expand its latest bounded preview
and full-output workspace artifact path when one exists.
Completed tool output appears under its tool call. Output longer than three lines is collapsed with an
inline expansion control.
Execute groups show a summary of the tools the agent ran. Click the group to switch between that
summary and its code and output; click N more to reveal hidden calls. Read previews show file
contents with line numbers, shell previews show command output, and edits show diffs. Paths are
relative to the Session's working directory, including when connected to a remote server.
For a single call, details opens its result without repeating the summary header. Ctrl+X T
switches Execute source/output. Ctrl+X J selects the next child (Ctrl+X Shift+J the previous),
Ctrl+X D toggles that child's details, Ctrl+X L reveals ten more calls, and
Ctrl+X Shift+L folds the selected revealed range, or the latest revealed range. Trace shortcuts
close source/output first. Clipped shell commands and full failure diagnostics remain inspectable;
native tools never show raw argument blocks. Pause stays a non-expandable duration/status row with
its full failure diagnostic.
Rejecting an approval inside Execute skips that operation; the script may continue. Cancel the
Turn to stop the whole execution.
Reasoning stays in an expandable block. Its header shows the observed thinking duration once the
block finishes alongside a first-line preview. Completed Turns show the model and elapsed execution
time below the response, excluding queue wait.
New TUI Sessions receive a small built-in assistant system prompt; the opening screen does not ask
users to configure it. They also start in the directory the client runs from: the CLI sends its
working directory and the server stores it (relative inside the server root, absolute
otherwise). Pass --workdir PATH for an explicit directory, or --no-workdir to use the
server root. When the client directory cannot be used at all, the Session uses the server
root and the TUI says so.
Type / on the opening screen or in the conversation composer to load available commands.
The opening screen lists only the commands that work before the first prompt
(/new, /queue, /sessions, /models, /effort, /skills, /exit); full command text for anything else
still reports why it needs a Session. /new [TEXT] starts a new Session and switches to it,
leaving the previous Session running on the server; with TEXT that prompt is submitted
as the first Turn. Ctrl+X N does the same without a prompt. /exit (aliases /quit, /q)
quits the application from either screen. /sessions [QUERY] lists
this project's Sessions newest-first and switches on select; the previous Session keeps
running on the server. /queue TEXT always creates a
later Turn. /steer TEXT always follows up on the running Turn and reports an error rather than
silently queueing when that Turn is no longer steerable. /models [QUERY] opens the model picker;
picking a model that advertises reasoning effort levels then prompts for the level.
/effort [LEVEL|default] sets the effort for the next Turn directly, bare /effort opens the
effort picker, and Ctrl+X E cycles through the advertised levels. The header shows the model
the next Turn will use, including the selected effort, plus live context-window use like 87.9K (8%)
once a Turn reports model usage. /agents [QUERY] opens the agent picker with
the primary agents; the header shows the agent the next Turn will use. /skills [QUERY] opens a
multi-select picker. Selected Skills load into the next queued Turn and then clear. /fork [SEQUENCE|TURN-ID] forks the Session
before a Turn and opens the fork; bare /fork opens the fork picker. Favorites, recent models, and
per-model effort levels persist across runs in model-preferences.json under
the durable state directory.
Task tool rows show a live one-line summary of their sub-agent: the latest tool while it runs,
tool-call count and duration once it settles, and a (background) marker when the chain detached.
Click a task row, type /subagents, or press Ctrl+X G to open a sub-agent and watch its Turns, thinking, and tool
calls live; the composer then prompts that sub-agent, which keeps its pinned agent. /back,
Esc, or Alt+↑ returns to the parent, and Alt+← / Alt+→ moves between siblings. Only
direct sub-agents open; Ctrl+B detaches blocking foreground chains to the background.
Durable state paths
The database, provider auth.json, model preferences, author identity, and daemon
state share one directory. An absolute XDG_DATA_HOME selects
$XDG_DATA_HOME/ze-harness on every platform; relative values are ignored.
The default is ~/.local/share/ze-harness on Unix and %LOCALAPPDATA%\ze-harness
on Windows, with %USERPROFILE%\AppData\Local\ze-harness as the fallback when
LOCALAPPDATA is missing or relative. XDG_CONFIG_HOME and XDG_CACHE_HOME are unchanged.
Existing Windows ~/.local/share/ze-harness data stays in use while its registered
daemon is live. Stop the daemon, then invoke the CLI again to migrate to the native
location. Migration renames the directory and leaves a junction at its old location
so existing launchers and absolute paths still work. Locked or cross-volume directories
remain on the legacy root with a warning; independent directories at both locations
produce an error. See migration guidance
before resolving a conflict or moving data manually. Absolute --database and
ZE_HARNESS_DB overrides still select their own database; relative ZE_HARNESS_DB
values resolve under the shared state directory.
Local and remote commands
Local commands connect to one shared daemon on this machine: the first command starts it
in the background and later commands reuse it, so Sessions and running Turns survive
closing the client. service status, service start, service stop, and
service restart manage the daemon explicitly; restarting picks up configuration,
plugin, MCP server, and Skill changes. First start wins: a --database or --config
flag that differs from what the daemon started with is refused, so restart the daemon
or pass --embedded for that command. Stopping or restarting ends running Turns:
the CLI names them first so nothing is lost silently. serve starts a foreground
server instead.
With --embedded, commands run in-process without the daemon (scripts and tests).
With --remote, commands use only the network Client against the named --server
server. The default local database is one shared ze-harness.db file under
the durable state directory; the daemon and
--embedded commands may open it at once (SQLite serializes writers, and a
busy database surfaces a retryable error rather than corrupting state).
Provider API keys can be managed through the same local or remote Client path:
ze-harness provider list
ze-harness provider connect openai --label personal
ze-harness provider login openai
ze-harness provider disconnect openai
provider connect requires an interactive terminal and reads the key without echoing it, so the
secret does not appear in shell history or process arguments. The local command stores the key once
in the global auth.json in the durable state directory, shared
by every working directory and database; --label is accepted but labels persist on remote servers
only. With --remote URL --server NAME, the remote server stores it instead. Provider list and
command results are JSON and never include credential values. Keys connected before the global
auth.json stay in their per-project databases; reconnect once with provider connect.
provider login PROVIDER signs in to a provider that supports login; provider list shows each
provider's logins. Open the printed URL, enter the displayed code, and approve the login. The
host runs the exchange, so the command works over SSH and waits until you approve, the provider
rejects the request, the code expires, or you press Ctrl-C (which cancels the login). The URL and
code appear on stderr; stdout contains only the safe connection result. Use --method ID when a
provider offers several login methods.
openaisigns in with a ChatGPT account (enable device code authorization in your ChatGPT security settings if OpenAI rejects it). Requests from that login go to the ChatGPT Codex Responses deployment; configured base URLs and routes apply only to API-key connections. Choose a model your ChatGPT plan supports withrun --model openai/MODEL.tapsigns in to TAP with your TAP account; a first sign-in creates it. Before the code, login shows the notice the TAP app shows: by continuing you acknowledge Zephyr Cloud's Terms of Service and Privacy Policy, and agree to receive occasional product emails and notifications.tap/*models are TAP credits and free models; your own provider keys stay under that provider's id, such asopencode-go/*. To use a key you stored in TAP (or the TAP Gateway dashboard) instead of a local one, set the provider'sviatotap, for example"providers": { "tap": true, "openrouter": { "via": "tap" } }.openrouter/*requests then go through the TAP Gateway (at the origin of thetapbase URL) with your TAP sign-in, andprovider listshows"source": "via", "via": "tap", available once you are signed in to TAP. Errors name TAP, for example "No openrouter key is stored in TAP".- Providers configured with
login.device.discoveryURLuse the standard OAuth 2.0 device flow. An optionallogin.device.notice(textandlinks, each link alabelandurl) is shown before the code.
A login replaces the provider's stored connection. The Node host saves access and refresh tokens
in the global auth.json, refreshes them as requests need them (including during a long Turn),
and retries once with a fresh token if the provider rejects an expired one. If the provider
rejects the refresh token, run provider login again. An explicitly configured
providers.<id>.options.apiKey takes precedence over a stored login. Use provider disconnect
to remove a login, or provider connect to switch to an API key. Local daemon, embedded, and
remote Node hosts support login; Cloudflare Worker hosts report login as unsupported.
run accepts --model PROVIDER/MODEL, --system-prompt TEXT, and --json. Images attach
with --image URL, --image-base64 MIME:BASE64, or --image-file PATH (a local JPEG, PNG,
GIF, or WebP file read from the machine running the CLI, also in --remote mode); the server
may enforce a configured per-image size cap. The prompt text precedes images in the submitted
content; images of the same option type retain their order. session create
accepts --model PROVIDER/MODEL, --system-prompt TEXT, and --workdir PATH (a server-rooted
working directory; the server root is the default). session list prints the current project's
Sessions oldest-first as JSON, and server delete cancels and deletes them all (workspace files
stay); both accept --all for every project. Projects resolve from git remote hashes like
opencode, with directories outside any repository sharing the global project. session fork <id> copies settled history into a new Session; --turn ID|SEQUENCE forks before
one Turn and --session ID selects the new identity. Forks inherit the parent workdir, model,
system prompt, and permission rules, and record lineage on the new Session.
session children <id> prints one Session's spawned children oldest-first as JSON, and
session approvals <id> prints the pending permission requests owned by its descendant
sub-agent Sessions oldest-first as JSON. session background <id> detaches its blocking
foreground sub-agent chains (their results arrive as queued turns). Task tool calls render
with their agent, state, and summary.
Append external context for the next admitted work without creating a Turn or calling a model:
ze-harness session conversation append <SESSION_ID> "Remember this for the next request."
cat context.txt | ze-harness session conversation append <SESSION_ID> -
The command prints the durable receipt as JSON. Use the same --database and working directory as
the local Session, or add --remote URL --server NAME for a network Server. Work already admitted
before the append does not see the entry.
Session system prompts are durable: they are stored with the Session, returned to Session readers, and sent with every model request in that Session. They must not contain credentials.
Remote permission and Session policy commands are:
ze-harness --remote URL --server NAME permission reply <sessionId> <requestId> <once|always|reject> [--message TEXT]
ze-harness --remote URL --server NAME permission saved list <sessionId>
ze-harness --remote URL --server NAME permission saved remove <sessionId> <savedPermissionId>
ze-harness --remote URL --server NAME session config show <sessionId>
ze-harness --remote URL --server NAME session config replace <sessionId> <expectedRevision> <configuration-json|'-'>
--message attaches feedback to a reject reply and is stored with the denial. A reject
also denies the Session's other pending requests. A reply addressed to a parent Session for
a descendant sub-agent request is forwarded to the owning Session, and run, chat, and
the TUI surface those descendant approvals inline so sub-agents never wait unseen. They learn
about sub-agent approvals and questions from the server activity stream (watchServer): each
subscribes live, then bootstraps once from session children and the pending lists when the
stream announces its cursor; nothing polls while idle.
The TUI and CLI choose only the reply. They cannot choose or widen the patterns saved by an
always reply.
In chat, enter /steer TEXT to redirect the currently running Turn. Any other nonempty line queues
a new Turn.
See the CLI quickstarts and database rules.
Install
Install the native executable without Node.js or GitHub authentication. stable tracks the
latest completed production release; dev tracks completed builds from main. Public
archives, signed metadata and installers are served from downloads.ze.dev.
curl -fsSL https://downloads.ze.dev/install.sh | sh
curl -fsSL https://downloads.ze.dev/install.sh | sh -s -- --channel dev
curl -fsSL https://downloads.ze.dev/install.sh | sh -s -- --version 0.3.0
On Windows PowerShell:
irm https://downloads.ze.dev/install.ps1 | iex
& ([scriptblock]::Create((irm https://downloads.ze.dev/install.ps1))) -Channel dev
Homebrew installs the stable SEA binary from the ZephyrCloudIO tap on supported macOS and glibc Linux machines:
brew install zephyrcloudio/ze/ze-harness
brew upgrade ze-harness
After the WinGet community submission is accepted and indexed, Windows x64 users can install and upgrade the portable package with:
winget install --id ZephyrCloudIO.ZeHarness -e
Before upgrading a WinGet installation, stop the local daemon and let active Turns finish:
ze-harness service stop
winget upgrade --id ZephyrCloudIO.ZeHarness -e
Stopping the daemon ends any Turns still running. Windows locks executables that are in use, and the local daemon runs from the installed executable, so WinGet cannot replace it while it runs.
Homebrew and WinGet installs are managed by those package managers, including future upgrades.
They do not use the native installer's launcher or its versioned install directory. The native
installer verifies signed release metadata and the archive checksum; Homebrew and WinGet verify
the archive SHA-256 in their package definitions. Uninstall with brew uninstall ze-harness or
winget uninstall --id ZephyrCloudIO.ZeHarness -e, respectively. These uninstall commands do
not remove CLI configuration or Session data.
Supported builds: macOS 13.5+ on Apple Silicon; Linux x64/ARM64 with kernel 4.18+,
glibc 2.28+, and libatomic; and Windows 10+/Server 2016+ on x64. Native installs
require internet access. The Unix installer needs a POSIX shell, curl, openssl,
mktemp, and tar (Linux) or unzip (macOS); Windows needs PowerShell 5.1+.
Windows ARM64, Intel macOS, and Alpine/musl are not currently release targets.
A version pin selects an immutable archive; rerun the installer to change versions.
In PowerShell use -Version, -Dir, and -NoModifyPath; on Unix use --version,
--dir, and --no-modify-path.
The launcher is installed in ~/.ze-harness/bin (or a custom --dir/-Dir).
Executables and the install marker are stored under
${XDG_DATA_HOME:-~/.local/share}/ze-harness on Unix; Windows uses
$env:XDG_DATA_HOME\ze-harness when XDG_DATA_HOME is an absolute path, otherwise
$HOME\.local\share\ze-harness. On invocation, the CLI attempts the Windows
state migration, preserving the installer's path with a junction;
an in-use executable can defer the rename. The installer does not alter the CLI's existing
XDG_CONFIG_HOME configuration or XDG_CACHE_HOME cache. Unix installs add the launcher
directory to the shell profile unless disabled; Windows installs add it to the user PATH.
Each installed version keeps LICENSE and THIRD_PARTY_LICENSES.txt beside the executable.
Open a new terminal after installing. If an old unmanaged executable occupies the launcher
path, move it aside before installing. New installs record the install channel or an explicit
version pin alongside the launcher path. ze-harness update --check checks the signed channel
pointer. ze-harness update verifies the signed manifest and archive checksum before installing.
With a registered local daemon it stages the release and, when no Turns are running, restarts the
daemon onto it; with running Turns it leaves the daemon unchanged and says so. Once active Turns
finish, ze-harness service restart activates a prepared update and starts the daemon from the new
executable. That explicit restart can interrupt active Turns, so wait for them to finish before
using it. A staged release that the installed CLI already matches or passes (for example after
reinstalling with the installer) is discarded instead of blocking a restart.
Upgrading the local daemon
A daemon keeps running the executable that started it, so after an upgrade it must restart onto the new version. That handoff happens without a separate command:
- The installers finish by running
ze-harness service restart --if-idle. Pass--no-restart-daemon(-NoRestartDaemonon Windows) to skip it. ze-harness updatehands off after staging, as above.- When the installed native CLI is newer than the running daemon, the first local command hands
off and prints
Local daemon updated from <old> to <new>.
Every handoff restarts only an idle daemon: running Turns, Sessions that cannot be inspected, or a
daemon whose protocol differs leave it running and unchanged. A handoff never moves the daemon to an
older release. ZE_HARNESS_DISABLE_AUTOUPDATE=1 turns off the automatic handoff on ordinary
commands. If the new version cannot start, the CLI restarts the previous executable so work can
continue, reports why, and does not retry the automatic handoff for that version until an explicit
service restart succeeds.
While a daemon starts, the CLI shows its progress: a bar on a terminal, or one line per backup or
migration step otherwise. Before migrating a database that already holds data, the daemon copies
it to ze-harness.db.pre-<migration>.bak; the bar follows that copy byte by byte, then each
migration. A large database can take a while, and the CLI waits for as long as the daemon is alive
and reporting. A failed migration rolls back completely, and the CLI shows the daemon's own error.
Legacy native installs lack reliable pinning
metadata and must explicitly opt in with ze-harness update --auto on or reinstall.
Channel-following native installations check for a new signed release on local daemon startup
and every six hours while it runs. A newer version is verified and staged in a separate process,
without interrupting the daemon or activating an incompatible old TUI. Run ze-harness update
when current work finishes to switch both the CLI launcher and the daemon to a staged release. Use ze-harness update --auto off to
disable background checks, or --auto on to enable them. Existing native installs do not get
background updates unless explicitly enabled with --auto on; explicit version pins stay pinned.
Homebrew and WinGet updates use their own package managers.
The CLI observes Ze-Harness-Version: <protocol>/<release> on normal daemon and remote-server
responses, so connecting does not add a handshake request. A running TUI shows a notice when
the version differs or changes, including on a stream reconnection. Protocol differences warn
that operations may fail; response observation does not cancel current work or retry prompts.
Non-TUI commands send these notices to stderr, preserving stdout.
When a TUI sees a newer server release and native auto-update is enabled, it independently
checks its configured signed release channel once for that CLI session. The server header
never selects a download or changes the channel/pin. A separate CLI process verifies and stages
an available channel update; only successful staging produces the downloaded/restart notice.
It never restarts a remote server or silently switches this CLI. When local work finishes, exit
the TUI and run update to activate the release and restart the daemon onto it.
Repeated update commands reuse a verified staged copy of the same channel release.
Native version slots are retained until uninstall. Automatic pruning is deferred until the
host can track all live CLI/TUI processes using older executables, not only the daemon.
ze-harness uninstall removes the native launcher and versioned executables while leaving
Sessions and credentials intact. Stop the local daemon first. Use
ze-harness uninstall --remove-user-data --yes to also remove the local XDG data, config, and
cache directories and user-authored global skills in ~/.ze-harness/skills. Symlinked application
data directories are refused rather than deleting their targets. This does not delete custom databases outside those directories or project
files. On Windows cleanup runs after the CLI exits. Remove any PATH entry added by the installer
from your shell profile or Windows user PATH separately. For Homebrew and WinGet installations,
the CLI provides the package-manager uninstall command instead of modifying managed files.
Binary releases
SEA builds generate THIRD_PARTY_LICENSES.txt with generate-license-file and append the
Node.js notice fetched from the build runtime's exact release tag. Building the native
archives requires network access to raw.githubusercontent.com for that notice.
Each release is stored at https://downloads.ze.dev/releases/v<version>/ with
manifest.json, manifest.json.sig, checksums.txt, and the platform archives.
The channels at /channels/stable.json and /channels/dev.json each contain a
signed pointer bound to its channel. Installers contain the release certificate,
verify the pointer and detached manifest signatures with RSA/SHA-256, then verify
the archive SHA-256 from the signed manifest before activating it.
The public certificate for independent verification is at
/keys/<sha256-of-certificate>.crt (DER).
Release assets are also retained on their GitHub Releases for future public visibility.
Neither this release signature nor an archive checksum replaces OS-native code signing.
For release maintainers: set GitHub Actions variable ZE_RELEASE_CERT_BASE64 to the
base64 DER X.509 RSA certificate, secret ZE_RELEASE_PRIVATE_KEY to its matching PEM
private key, variables ZE_R2_ACCOUNT_ID and ZE_R2_BUCKET, and secrets
ZE_R2_ACCESS_KEY_ID and ZE_R2_SECRET_ACCESS_KEY to write-scoped R2 credentials.
For an initial keypair, generate it outside the repository:
openssl req -x509 -newkey rsa:3072 -nodes -days 3650 \
-subj '/CN=Ze Harness Release' -keyout release-key.pem -out release.crt.pem
openssl x509 -in release.crt.pem -outform DER -out release.crt
openssl base64 -A -in release.crt # ZE_RELEASE_CERT_BASE64
openssl x509 -in release.crt.pem -noout -fingerprint -sha256
Store the entire release-key.pem contents as the private-key secret. Only the
certificate is public. The release workflow checks that the certificate matches
the signing key before publication.
Keep the signing key outside the repository, record and publish its SHA-256 certificate
fingerprint through an independently controlled channel. Public certificates have
immutable fingerprinted paths; changing the release signing key requires a separate
installer migration supporting both keys before switching the signed release channel.
R2 must expose the bucket publicly as downloads.ze.dev; release jobs verify reads
through that domain before advancing channel pointers. Configure signing and R2 settings
before merging: the dev release job on every main push intentionally fails closed without
them. Versioned R2 objects, including dev versions, are retained so pinned installs remain
available even after old GitHub dev prereleases are pruned; plan R2 storage accordingly.
The publisher uploads up to three immutable objects concurrently and verifies each through
the public domain, retrying transient read errors. Each public read defaults to a 120-second
timeout; set ZE_R2_READBACK_TIMEOUT_MS on the release job if the network needs longer.
If publication stops partway, use Re-run failed jobs in GitHub Actions to reuse the
same built archives. Re-run all jobs rebuilds archives with new timestamps and may
produce different hashes, which the immutable-object preflight rejects. In that case,
publish a new version rather than overwrite already published versioned objects.
Stable release publication also runs .github/workflows/package-managers.yml after R2 is
verified. It reads the immutable signed manifest, verifies each archive's SHA-256 and legal
notices, tests the generated Homebrew formula on macOS, and uploads the formula and WinGet
manifests as a workflow artifact. The Homebrew job commits the formula directly to
homebrew-ze; independently, the WinGet job
commits one version into ZephyrCloudIO/winget-pkgs
and opens a PR against microsoft/winget-pkgs:master. A package-manager failure does not undo
the SEA release; rerun failed jobs, or dispatch Package managers with a published stable
version for a backfill. Install the existing zephyr-workflows-automation GitHub App on both
public repositories with Contents write permission. The jobs mint short-lived, repository-scoped
App tokens using ZE_WORKFLOW_AUTOMATION_CLIENT_ID and
ZE_WORKFLOW_AUTOMATION_PRIVATE_KEY, already used by the release-please workflow. To open the
upstream PR, set the separate ZE_WINGET_SUBMIT_TOKEN Actions secret to a classic personal
access token with only the public_repo scope and an expiration. GitHub does not yet support
fine-grained personal access tokens for contributing to public repositories outside your
organizations; this classic scope is limited to public repositories but cannot target only
Microsoft's repository. The user token is used only in the WinGet PR job, never for commits or
downloads.
The WinGet PR discloses that the source repository is currently private and leaves Windows-local
validation unchecked; Microsoft runs its own checks, and acceptance and indexing may lag the
stable release. Verify on Windows with winget validate --manifest <directory> and
winget install --manifest <directory> using the workflow artifact.
The binary behaves like the Node CLI with two packaging limits: MCP stdio servers run without the process-tree supervisor (only the direct child is reaped on shutdown), and the TUI extracts its native assets to a versioned cache folder on first launch. macOS may require the user to approve an unnotarized executable in Privacy & Security.
Injected-host mode
serve --injected-host is the official injected-host entrypoint for embedding supervisors;
the supervisor launches this binary instead of building its own. It serves the same harness HTTP/SSE protocol on loopback with
credentialMode: 'injected-only': stored records, configured keys, and environment reads
fail closed instead of resolving ambient authority. Provider, attachment, and tool frames
arrive through the injected ports (embedder-implemented providers, content resolution after
visibility filtering, host custody around final dispatch), never from the environment.
The ready line prints <url> mode=injected-host for the supervisor to parse.
Agent Client Protocol
ze-harness acp serves the Agent Client Protocol over
standard input and output, so ACP clients such as Zed or T3 Code can drive Ze Harness as their
agent. Point the client at the ze-harness executable with the single argument acp. The
command runs an embedded host and needs no daemon; stdout carries only protocol frames and
every diagnostic goes to stderr.
Each ACP session is one Ze Session in the client's working directory, with symlinks resolved.
Prompts queue Turns that stream text, reasoning, tool calls, and usage back as session updates.
Tool approvals arrive as ACP permission requests with Allow once, Always allow in this
session (when the Tool proposes save patterns), and Reject. Questions become form
elicitations when the client supports them and are dismissed otherwise. Primary agents such as
build and plan are offered as session modes; the model and reasoning effort are session
config options. Sessions can be listed, loaded with their history replayed, resumed, forked,
and closed. ACP has no steering: a prompt sent while a Turn runs is rejected, and clients
cancel and resend instead.
MCP servers the client sends are attached to that Session only. Stdio servers run as local
subprocesses of the acp process, with its user's authority. Their header and environment
values stay in the acp process memory and are never written to the database, config, or
events; clients resend them whenever they load or resume a Session. Ze sends MCP credential
headers only over HTTPS, so an http:// server with headers is reported on stderr and does not
connect. SSE MCP servers are not supported.
ACP Sessions use their own database, acp.db in the shared state directory, so clients that
list the daemon's Sessions do not see editor work. Pass --database or set ZE_HARNESS_DB to
share a database instead. Providers, configuration, and plugins load as they do for local
commands, so connect providers once with ze-harness provider connect.
Public TypeScript exports
CliCompositionCliInputErrorCliIOdefaultDatabasePathdetectTuiRequestGlobalOptionsrunClirunClientCommandTUI_FFI_FLAGTUI_REEXEC_ENVTuiCommandsTuiLaunchTuiRunnerVERSION
Command reference
The output of ze-harness --help.
DESCRIPTION
Run and manage AI coding Sessions with Ze Harness
USAGE
ze-harness <subcommand> [flags]
FLAGS
--remote string Connect to a remote Harness instead of the local daemon
--embedded Run in-process instead of using the local daemon
--resume, -s string Resume an existing Session in the TUI
--server string Select a server; requires --remote
--token string Authenticate to a remote Harness with a bearer token
--config string Use a local configuration file; unavailable with --remote
--database string Use a local SQLite database path or ':memory:'
--mcp-callback-port string Listen locally for MCP OAuth callbacks
GLOBAL FLAGS
--help, -h Show help information
--version, -v Show version information
--wizard Start wizard mode for a command
--completions <bash|zsh|fish|sh> Print shell completion script (choices: bash, zsh, fish, sh)
--log-level <all|trace|debug|info|warn|warning|error|fatal|none> Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
SUBCOMMANDS
tui Open the interactive terminal interface
run Run one prompt and print the result
chat Start a line-oriented interactive chat
completions Print a shell completion script; see the installation examples below
update Check for and install the latest version from the installation channel
uninstall Remove this native installation while retaining user data
session Create and manage Sessions; some commands require a remote server
provider Manage model provider connections
mcp Use MCP servers, resources, prompts, and OAuth
permission Reply to and manage tool permissions on a remote server
question Answer user questions asked by tools on a remote server
compaction Inspect and cancel context compactions
turn Manage Turns on a remote server
plugins Inspect and manage plugins
serve Start a local Harness HTTP server
acp Serve the Agent Client Protocol over standard input and output
service Manage the local Harness daemon
server Manage a remote Harness server
runtime Manage the remote Harness runtime
EXAMPLES
# Open the interactive TUI in a terminal
ze-harness
# Resume a Session in the TUI
ze-harness -s 550e8400-e29b-41d4-a716-446655440000
# Open the provider setup dialog
ze-harness tui --onboarding
# Run one prompt locally
ze-harness run "Explain this repository"
# List Sessions on a remote server
ze-harness --remote http://127.0.0.1:4317 --server main session list
# Show help for a command
ze-harness session --help
# Install shell completions
ze-harness completions --help