yet another coding agent harness, lightweight and written (vibe-slopped) in go.
one static binary. two providers atm (anthropic, openai/codex). four tools (read, write, edit, bash). three run modes (interactive tui, print, json). no extensions. no plugins. no ceremony. no community.
go install github.com/patriceckhart/zot/cmd/zot@latestor from source:
git clone https://github.com/patriceckhart/zot
cd zot
make build # produces ./bin/zot
make install # into $GOPATH/binthe easiest way is to just run zot and type /login. the tui opens even without credentials and walks you through a browser-based login flow.
--api-keyflagANTHROPIC_API_KEY/OPENAI_API_KEYenv var$ZOT_HOME/auth.json(api key or oauth token; mode 0600)
$ZOT_HOME defaults to:
- macOS:
~/Library/Application Support/zot - linux:
$XDG_STATE_HOME/zotor~/.local/state/zot - windows:
%LOCALAPPDATA%\zot
run zot and type /login. pick one of two methods:
- api key — a small local web server starts on
127.0.0.1:<free-port>, your browser opens a form, you paste yoursk-ant-...orsk-...key. zot probes the provider once and saves it toauth.jsonif accepted. - subscription — use your claude pro/max or chatgpt plus/pro subscription. the oauth flow pins the callback to a fixed port per provider (
localhost:53692for anthropic,localhost:1455for openai) because those are the only ports their auth servers will redirect to.- anthropic uses the claude code oauth flow; messages go to
api.anthropic.comwith a bearer token and the claude-code identity headers. - openai uses the codex cli oauth flow; messages go to
chatgpt.com/backend-api/codex/responseswith thechatgpt-account-idextracted from the returned id_token.
- anthropic uses the claude code oauth flow; messages go to
note on subscription login: the oauth client ids used are the ones published in anthropic's claude code cli and openai's codex cli. reusing them from a third-party tool is against their terms of service and may be revoked at any time. use it at your own risk; the api-key flow is the safe default.
all data lives under $ZOT_HOME:
$ZOT_HOME/
├── config.json # last-used provider/model/theme, saved automatically
├── auth.json # api keys and oauth tokens (mode 0600)
├── sessions/ # jsonl transcripts, one dir per cwd
├── models-cache.json # live /v1/models discovery cache (6h ttl)
└── logs/ # app log files
zot # interactive tui
zot "fix the failing test" # tui, pre-filled prompt
zot -p "list all go files" # print final text, exit
zot --json "refactor main.go" # newline-delimited json events, exit
zot --continue # resume the most recent session for this cwd
zot --resume # pick a session to resume
zot --list-models # show supported models
zot --help| flag | description |
|---|---|
--provider anthropic|openai |
pick the provider |
--model <id> |
pick the model (see --list-models) |
--api-key <key> |
override api key |
--base-url <url> |
override provider base url (tests / self-hosted) |
--system-prompt <text> |
replace the default system prompt |
--append-system-prompt <text> |
append text to the system prompt (repeatable) |
--reasoning low|medium|high |
enable reasoning on supported models |
-c, --continue |
resume the latest session for this cwd |
-r, --resume |
pick a session to resume |
--session <path> |
resume a specific session file |
--no-session |
don't read or write session files |
--cwd <path> |
use <path> as the working directory |
--no-tools |
disable all tools |
--tools <csv> |
only enable the listed tools |
--max-steps <n> |
cap agent loop iterations (default 50) |
read— read text files (or inline images: png / jpg / gif / webp)write— create or overwrite files, making parent directories as needededit— one or more exact-match replacements in an existing filebash— run a shell command in the session cwd, with merged stdout/stderr and a timeout
when the sandbox is on (see /lock), all four tools refuse paths outside the session cwd.
- interactive (default): chat tui with streaming output, spinner, cost meter, slash commands.
- print:
zot -p "prompt"runs the agent to completion and writes only the final assistant text to stdout. - json:
zot --json "prompt"emits one json object per agent event to stdout, newline-delimited. the schema is documented ininstructions.md§8.
type / in the tui to open the autocomplete popup. available commands:
| command | description |
|---|---|
/help |
show key bindings and commands |
/login |
log in via api key or subscription (opens a dialog) |
/logout [provider] |
clear credentials for anthropic, openai, or all when omitted |
/model |
pick a model from a list (or /model <id> to set directly) |
/sessions |
resume a previous session for this directory |
/compact |
summarize the transcript into one message to free up context |
/lock |
confine tools to the current directory |
/unlock |
allow tools to touch paths outside again |
/clear |
clear the chat transcript |
/exit |
exit zot |
shows previous sessions for the current working directory, newest first, with timestamp, model, message count, cost, and the first user prompt. pick one with ↑/↓, enter to resume, esc to cancel. zot swaps the current session file for the selected one and replays the full transcript (including tool calls) into the agent. sessions remember the model they ended on, so resuming picks up on that exact model even if your global default changed.
sends the current transcript through the model with a structured summarization prompt. the returned summary replaces the transcript as one synthetic user message, with the last few exchanges kept verbatim for continuity. status bar's ctx N/M (P%) meter resets. use it when the context meter creeps past ~80%.
enforces a sandbox rooted at the cwd shown in the status bar. read / write / edit resolve their target path (including through symlinks) and refuse anything outside the sandbox. bash refuses obvious escape patterns: sudo, rm -rf /, leading cd / / cd .. / cd ~, chmod -R, dd of=/, etc. status bar shows · locked · ~/your/cwd while active.
this is a guardrail against accidents, not a hard security boundary. if you need real isolation, run zot under docker or a proper sandbox.
every interactive or print/json run (unless --no-session) writes a jsonl transcript under $ZOT_HOME/sessions/<cwd-hash>/. resume any of them with --continue, --resume, --session <path>, or interactively via /sessions inside the tui.
--list-models or the /model picker shows the full catalog. three sources:
- catalog — models baked into zot, always available
- live — ids discovered from
GET /v1/modelsusing your stored api key (cached for 6h in$ZOT_HOME/models-cache.json, refreshed in the background on startup) - speculative — ids that appear in the upstream generator but aren't live on the public api yet; they'll 404 today and start working the moment the provider ships them
the context meter in the status line (ctx N/M (P%)) uses the model's advertised context window to show how much of it your last turn consumed.
when a tool returns an image (e.g. read on a png), zot renders it inline on terminals that support it: iterm2, wezterm, kitty, ghostty. on other terminals you see a text placeholder with mime type, pixel dimensions, and byte size. control with the ZOT_INLINE_IMAGES env var:
| value | effect |
|---|---|
| unset (default) | auto-detect based on TERM_PROGRAM |
iterm / iterm2 |
force iterm2 osc 1337 protocol |
kitty |
force kitty graphics protocol |
off / none |
always use the text placeholder |
frames containing images are full-repainted (no differential diff) to prevent stale image pixels from lingering through scroll. that costs one terminal flash per image-containing frame; set ZOT_INLINE_IMAGES=off if that bothers you.
| key | action |
|---|---|
enter |
submit |
alt+enter |
newline |
tab |
complete the selected slash command |
esc |
cancel the current turn (while busy); clear input (while idle) |
ctrl+c |
exit when idle; cancel the current turn while busy |
ctrl+d |
exit on empty input |
ctrl+l |
redraw the screen |
| key | action |
|---|---|
ctrl+a / ctrl+e |
jump to start / end of line |
alt+← / alt+→ |
jump one word back / forward |
ctrl+u / ctrl+k |
delete to start / end of line |
ctrl+w · alt+backspace |
delete the previous word |
up / down (editor non-empty) |
cycle through prompt history |
| key | action |
|---|---|
pgup / pgdn |
scroll one page up / down |
up / down (editor empty) |
scroll three lines up / down — this is how the mouse wheel reaches the scroll logic on most terminals |
make build # build ./bin/zot
make test # go test -race ./...
make lint # go vet + gofmt check
make fmt # gofmt -w .
make release # cross-compile linux/darwin/windows × amd64/arm64source layout:
cmd/zot/ main()
internal/agent/ cli wiring, arg parsing, system prompt, config
internal/agent/modes/ interactive tui, print, json, dialogs
internal/agent/tools/ read, write, edit, bash, sandbox
internal/auth/ credential store, api-key probe, oauth, login server
internal/core/ agent loop, sessions, cost tracking
internal/provider/ anthropic + openai streaming clients, model catalog
internal/tui/ terminal raw-mode, input parser, editor, renderer, markdown, view
MIT