zot. Yet another coding agent harness.
Lightweight. Single binary. Written (vibe-slopped) in Go. In beta forever.
ask anything, or type /help to see commands.
curl -fsSL https://www.zot.sh/install.sh | bash
zot is a minimal terminal coding agent, shipped as one static Go binary. No runtime. No Docker. No plugin system with a dedicated package manager. Just the agent loop done well. Drop it on your $PATH
and go.
It talks to Anthropic, OpenAI/Codex/Responses, Kimi, DeepSeek, Google Gemini/Vertex, GitHub Copilot, Bedrock, Azure OpenAI, OpenRouter, Groq, Cerebras, xAI, Together, Hugging Face, Mistral, Moonshot, Z.AI, Xiaomi, MiniMax, Fireworks, Vercel AI Gateway, OpenCode, Cloudflare AI, and local OpenAI-compatible models (like ollama), edits your files, runs your shell, and even answers your Telegram DMs. Bring your own API key or log in with a Claude, ChatGPT/Codex, Kimi Code, or GitHub Copilot subscription. DeepSeek and Google are API-key only.
Four ways to run it:
interactive- full TUI with streaming output, slash commands, queued messages, and an inline side-chat.** print**-zot -p
, one-shot, final assistant text to stdout. Great for shell pipelines.json-zot --json
, NDJSON events to stdout. Perfect for scripts and CI.rpc-zot rpc
, long-lived child process, NDJSON commands on stdin and events on stdout. Drop zot into apps written in any language.See the wire format.
zot ships a broad provider catalog so /login
, /model
, --provider
, and --list-models
all speak the same IDs.
Subscription-capable- Anthropic Claude Pro/Max (anthropic
), OpenAI Codex / ChatGPT Plus/Pro (openai-codex
), Kimi Code (kimi
), and GitHub Copilot (github-copilot
).Direct API providers- Anthropic, OpenAI Chat Completions, OpenAI Responses, DeepSeek, Google Gemini, Kimi/Moonshot, Moonshot CN, Groq, Cerebras, xAI, Together AI, Hugging Face Router, OpenRouter, Mistral, Z.AI, Xiaomi/MiMo token-plan regions, MiniMax global/CN, Fireworks, Vercel AI Gateway, and OpenCode/OpenCode Go.Cloud/platform providers- Amazon Bedrock, Google Vertex AI, Azure OpenAI, Cloudflare Workers AI, and Cloudflare AI Gateway.** Local/compatible**- Ollama and OpenAI-compatible local endpoints via--base-url
.
Use /login
to store API keys or subscription credentials. The model picker only shows models from providers currently available through env vars, auth.json
, Kimi CLI fallback, or local Ollama.
--list-models
and /model
show the merged catalog across every provider. Built-in entries cover Claude, GPT/Codex, Gemini/Gemma, Kimi/Moonshot, DeepSeek, Groq-hosted Llama/Gemma/Compound, OpenRouter-routed models, Bedrock model IDs, Vertex model IDs, Azure OpenAI deployments, Copilot models, and other provider-specific entries.
zot also merges live IDs discovered from GET /v1/models
using stored API keys, cached for six hours in $ZOT_HOME/models-cache.json
. Speculative catalog entries are included too; they start working as soon as the upstream provider enables them.
Custom models can be added with $ZOT_HOME/models.json
. User entries take precedence over both baked-in and live-discovered models, and support provider IDs such as groq
, openrouter
, github-copilot
, amazon-bedrock
, google-vertex
, azure-openai-responses
, fireworks
, vercel-ai-gateway
, mistral
, and xai
.
Four built-in tools. Zero ceremony. The minimum viable toolbox for an agent that actually ships code.
read
-
read text files, or render PNG / JPG / GIF / WebP inline on modern terminals.
write -
create or overwrite files, making parent directories as needed.
edit -
one or more exact-match replacements in an existing file.
bash -
run a shell command in the session cwd with merged stdout/stderr and a timeout.
Type /jail
to confine every tool to the current directory. Bash refuses sudo
, rm -rf /
, and other obvious escape patterns. It's a guardrail against accidents, not a hard security boundary.
zot can be extended in any language via a subprocess plus JSON-RPC protocol. Extensions can register slash commands, expose new tools to the model, intercept tool calls for permission gates, and open interactive extension-owned panels inside the TUI.
Nothing installs automatically. Opt in per machine with zot ext install
, or per run with zot --ext ./path
while hacking on one. Each extension gets its own directory, so persistent state like todo data, settings, or caches can live beside the extension itself.
zot ext install ./my-extension # copy into $ZOT_HOME/extensions/
zot ext list # show what's loaded
zot ext logs my-extension -f # tail stderr
zot update # also fast-forwards every git-installed extension
Reference implementations live under examples/extensions/
in Go, TypeScript, and Node. The protocol now also covers extension-driven panels, key routing, and redraws for building interactive tools inside zot itself. Read the protocol spec.
Skills are per-folder SKILL.md
files with a YAML frontmatter header. zot discovers them at startup, surfaces their names in the system prompt, and exposes a built-in skill
tool the model uses to load the body on demand. The agent pulls in exactly the instructions it needs, nothing more.
Built-in and user-installed skills load by default from .zot/skills/
, $ZOT_HOME/skills/
, .claude/skills/
, or .agents/skills/
. Use --no-skill
to disable all skill discovery. Read the skill format.
Every turn is appended to a JSONL transcript under $ZOT_HOME
. Resume the last session with -c
, pick one with -r
, or browse them all from /sessions
. Jump between past turns with /jump
without editing history. Free up context without losing the thread with /compact
, which summarizes the transcript into a single message and keeps the last few exchanges verbatim. zot auto-compacts when you cross 85% of the model's context window.
/session
covers the heavier ops on the running transcript. export
writes it to a portable .zotsession
file (default ~/Downloads
) so you can hand it to another machine or user; import
pulls one back in as a first-class resumable session. fork
branches from any past user message into a new session (parent + fork point recorded in the new meta) so you can try a different direction without polluting the original thread; tree
shows every branch in this directory with parent/child indentation and lets you switch into any of them.
Export covers only the main chat thread — messages, tool calls, tool results, compactions, and usage. /swarm
subagents are not bundled: their per-agent state lives in a unix-socket inbox and a session file on disk, neither of which round-trips through a JSONL file. To share what an agent said, copy it out of the transcript view manually.
Type /btw
to open a side-chat overlay with the full main session as frozen context. Ask quick clarifying questions without bloating the main thread. Nothing the side chat says is appended to the transcript or persisted to the session file, so your running context window stays lean.
/btw # open the overlay
/btw does PUT replace the whole resource?
Background subagents that run alongside your main session. Type /swarm
to open the dashboard, then n
to spawn one. Each agent is a separate zot
subprocess with its own model loop, its own persistent session file, and its own chat in the dashboard — but they all run in the same working directory as the host, so they see and edit the same files you do. You keep working in your main session; the subagents work in parallel.
Agents edit the same files you do. They use the same read
/ write
/ edit
/ bash
tools as the main agent against the host's working directory. There's no per-agent worktree or branch. If you need parallel edits on isolated checkouts, set that up yourself with git worktree
outside zot.
Press enter
on any row to open that agent's transcript — a chat overlay with an always-on inline composer at the bottom, streaming auto-follow, and a busy spinner showing the agent's current activity (thinking
, tool: edit_file
, etc.). Type and hit enter
to send a follow-up; esc
returns to the dashboard.
Common shapes from the command line so scripts and headless flows work the same as the dashboard:
/swarm # open the dashboard
/swarm new <task> # spawn an agent
/swarm new --model gpt-5 <task> # pin the new agent to a model
/swarm logs <id> # jump straight into one transcript
/swarm send <id> <text> # send a follow-up without the dashboard
/swarm resume # pick a stopped agent to bring back
/swarm kill <id> # stop a running agent (its state stays)
/swarm remove <id> # delete the agent's session and state
Agents are scoped to the session that spawned them and only show up in that session's dashboard. They persist across zot restarts under $ZOT_HOME/swarm/agents/<id>/
; press R
on a detached row to bring one back on the same session and inbox socket so the conversation continues where it left off.
Everything per-agent (session file, events log, inbox socket, meta) lives under $ZOT_HOME/swarm/agents/<id>/
. The agent's actual code edits land directly in your repo; track them with normal git status
/ git diff
.
/session export
does not bundle subagents — a .zotsession
is just the main chat transcript, and a swarm agent's state (session file, unix-socket inbox) is machine-local and can't round-trip through one. To share what an agent said, copy it out of the transcript view manually.
With /settings -> auto-swarm on, the main agent can also fork sub-agents on its own when a request naturally splits into independent parallel work. See
Settingsfor the details.
Type /settings
to open a dialog with every persistent setting. up
/down
to navigate, enter
or space
to change the selected row, esc
to close. Changes are written to $ZOT_HOME/config.json
and take effect on the next turn — no restart needed.
render images when supported — draw screenshots and images returned by read
inline using the terminal's image protocol (Ghostty, Kitty, iTerm2, WezTerm), or fall back to a text placeholder. Auto-detected from TERM_PROGRAM
; the toggle overrides the detection. The row is greyed out and forced off on terminals that don't speak any image protocol.
auto-swarm — let the main agent spawn background sub-agents in parallel via a built-in swarm_spawn
tool. Off by default. When on, the tool is registered with the running agent and the system prompt gains a short addendum telling the model to delegate independent sub-tasks proactively (“implement A and B”, “investigate three files”). The main turn keeps running immediately after each spawn; you can monitor / message / kill the sub-agents from /swarm just like manually-spawned ones.
As soon as the last sub-agent in a batch finishes its initial task, zot injects a single [auto-swarm update]
message back into the main chat recapping each agent's status, task, and transcript tail. The main agent then writes a short follow-up summary referencing the agents by id, so you stay in one conversation while the work fans out. Flipping the toggle off mid-session removes swarm_spawn
from the live agent and strips the addendum on the next turn — the model stops trying to delegate.
thinking level — choose reasoning depth for supported models. It is off by default. The available levels are off (no reasoning), minimum (~1k thinking tokens), low (~2k), medium (~8k), high (~16k), and maximum (~32k). zot maps those levels to each provider's closest supported wire format, such as token budgets for Claude and Gemini, Gemini's thinking-level enum, or OpenAI-style reasoning effort. The current status bar shows it as thinking: <level>
, and the setting can also be changed per run with --reasoning
.
Run with --no-yolo
to confirm every tool call before it runs. A dialog shows the tool name and a one-line preview of its args with four choices: yes, yes-always-this-tool-this-session, yes-always-this-session, no. Type /yolo
inside the TUI to drop the gate for the rest of the session.
The flag is interactive-only. In -p
, --json
, and rpc
modes tools still run freely so scripts and automation keep working.
When a turn fails because of a recoverable provider error — expired token (401
), permission denied (403
), rate limit (429
), provider outage (502
/503
/504
), or a transient network failure — zot opens an inline rescue picker over the chat instead of just painting a red banner.
The picker is the same vertical list / fuzzy filter UI as /model
, but only shows models from providers you're currently logged in to (env vars, auth.json
, Kimi CLI fallback, ollama). That includes API keys stored for any provider in the built-in catalog. The failed model is excluded. Pick one and enter
retries the same prompt on the new model.esc
dismisses.
Before the rescue picker even has a chance to fire, every provider client does up to two silent retries with short backoff (250ms, 750ms) on 502
/503
/504
and connection-reset / EOF-before-headers errors. Most edge-proxy blips disappear without you ever seeing the picker.
A rescue retry intentionally drops launch-time --api-key
and --base-url
overrides before rebuilding the agent — those are usually the cause of the failure — and re-resolves credentials from env vars, auth.json
, and provider defaults. Use /model
when you want overrides to stick.
No configuration is required — the candidate list is built dynamically from your active credentials. Bad-request, context-length, and serialization errors are NOT routed to the rescue picker because switching models won't fix them; those still surface as a normal error.
Drop a SYSTEM.md
in $ZOT_HOME
to replace the built-in identity and guidelines for every run. Per-invocation, --system-prompt
still wins; delete the file to revert to the default. Use --append-system-prompt
(repeatable) to layer extra instructions on top without blowing the default away.
Two ways to drive zot from another program. Both share the same event schema, so transcripts captured by one can be replayed through the other.
Go, in-process- importgithub.com/patriceckhart/zot/packages/agent/sdk
. OneRuntime
per project;Prompt(ctx, text, images)
returns a channel ofEvent
. Example inexamples/sdk/
.Any language, out-of-process- spawnzot rpc
as a subprocess and exchange NDJSON over its stdin/stdout. Reference clients for Python, Node, shell, and Go live underexamples/rpc/
Pair zot with a BotFather token and DM it from anywhere. It reads, writes, edits, and runs, same as the TUI. Photos and image attachments are forwarded to vision-capable models.
Two ways to run it. Inside a live TUI session, type /telegram
(or /tg
) to connect. DMs become prompts in the same session; what you type in the TUI mirrors back to Telegram as you: …
with replies prefixed zot: …
so the Telegram thread stays a complete record. A · tg ·
tag appears in the status bar while connected.
Or run a headless background daemon for a server:
zot telegram-bot setup # paste your BotFather token
zot telegram-bot start # detach and run in the background
zot tg logs -f # tail the bot log
macOS and Linux:
curl -fsSL https://www.zot.sh/install.sh | bash
Windows (PowerShell):
iwr -useb https://www.zot.sh/install.ps1 | iex
Go:
go install github.com/patriceckhart/zot/cmd/zot@latest
Prefer prebuilt tarballs? Every release ships archives for Linux, macOS, and Windows on amd64 and arm64 (except windows/arm64) with a checksums.txt
file.