Zero CLI is a small multi-agent coding CLI with a modern terminal UI. It combines one reusable agent runtime with Codex-compatible skills, dependency-aware workflows, bounded specialist delegation, multimodal routing, MCP, lifecycle plugins, and a native Agent Client Protocol (ACP) adapter.
Important
Zero CLI is still an MVP. The repository is packaged as a public Bun CLI and has a verified tag-release pipeline, but the first npm/GitHub release must still be created by a maintainer. The final section records the remaining security boundaries and deliberate scope choices.
The complete Mintlify documentation source lives in docs/. It includes task-oriented guides, configuration reference, protocol reference, operations material, and an implementation-level explanation of the recursive agent and subagent architecture.
cd docs
npm install
npm run devRun npm run validate from docs/ to check navigation, internal links, MDX, and Mintlify compatibility.
- Keep the model-facing core understandable:
read,write,run,find,search, anddelegate. - Make model choice semantic and configurable. Planning, implementation, review, exploration, and vision can each use a different model alias.
- Support two kinds of multi-agency: deterministic workflows for known processes and bounded delegation for specialist work discovered during a run.
- Preserve images, audio, PDFs, and other files as typed content, then route them according to declared model capabilities.
- Put permissions, budgets, cancellation, and loop guards in runtime code rather than relying on prompt instructions.
- Use the same runtime from the interactive TUI, headless CLI, and ACP adapter.
- Keep customization simple: JSONC and Markdown for normal configuration, trusted TypeScript modules for lifecycle hooks and custom tools.
┌────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ OpenTUI/Solid │ │ Headless / JSON │ │ ACP v1 over stdio│
└───────┬────────┘ └────────┬────────┘ └────────┬─────────┘
└────────────────────┼─────────────────────┘
▼
typed runtime events
│
┌──────────────▼──────────────┐
│ services / configuration │
│ model, tool, hook registries│
└──────────────┬──────────────┘
▼
┌─────────────────────────────┐
│ workflow runner │
│ re-entrant agent loop │
│ policy → approval → tool │
│ delegation and media routing│
└──────────────┬──────────────┘
┌────────┴────────┐
▼ ▼
Vercel AI SDK six capability tools
│ │
model providers files / shell / web
events ──► TUI reducer / NDJSON / SQLite
large output ──────────────► artifact store
The runtime owns the outer model/tool loop. The AI SDK normalizes providers, streams text and reasoning, and parses tool calls; Zero CLI selects the agent's tools, evaluates every call, executes approved calls, appends structured results, and continues until completion or a hard stop.
The main implementation areas are:
| Area | Location | Responsibility |
|---|---|---|
| CLI adapters | src/cli |
Argument parsing, headless output, initialization, approvals |
| TUI adapter | src/adapters/tui |
Timeline, composer, status, cancellation, approval queue |
| ACP subsystem | src/acp |
ACP server orchestration, sessions, content conversion, permissions, event mapping, and stdio transport |
| MCP subsystem | src/mcp |
Stdio/Streamable HTTP clients, tool/resource/prompt discovery, OAuth callbacks, and private token storage |
| Skills | src/skills |
Validated SKILL.md discovery, metadata catalog, and agent-scoped instruction access |
| Runtime | src/runtime |
Agent loop, workflows, content routing, hooks |
| Models | src/models |
Semantic aliases and AI SDK model execution |
| Tools and policy | src/tools, src/permissions |
Six tool contracts, path checks, process/network bounds, approvals |
| State | src/persistence, src/artifacts |
SQLite event log and content-addressed large outputs |
The stack is TypeScript, Bun, OpenTUI with SolidJS, Vercel AI SDK, Zod, JSONC, and Bun's SQLite driver.
Zero CLI requires Bun 1.3 or newer. Local find requires ripgrep, and unified-diff write operations invoke git apply, so install Git and ripgrep as well.
After a package release, install or run the scoped CLI:
bun install --global @egor-baranov/zero-cli
zero-cli
# Or run it without a global install
bunx @egor-baranov/zero-cliUntil then, run the source checkout:
bun install
bun run devInside the TUI, run /connect to configure OpenAI, Anthropic, Kimi Code, or Nebius Token Factory. OpenAI offers either a Platform API key or ChatGPT subscription sign-in through the official Codex authentication flow. Kimi offers browser sign-in through the official Kimi Code CLI (no key copying) or a Kimi Code Console API key. The command creates a model alias and immediately uses it for built-in Gateway-backed agents.
Trusted non-interactive adapters can start the same Kimi subscription flow with
zero-cli providers login kimi --json. Stdout is versioned NDJSON: an
authorization event contains only Kimi's official HTTPS verification URL and
optional one-time code, followed by exactly one complete or error event.
OAuth access and refresh tokens remain owned by the official Kimi Code CLI and
are never returned through this command.
zero-cli providers disconnect kimi --json removes Zero CLI's subscription
marker and any legacy Kimi API-key connection. The official Kimi CLI currently
does not expose a supported non-interactive logout command or ACP logout
capability, so this command does not delete its OAuth session. To remove that
session, open the official Kimi CLI with the same KIMI_CODE_HOME and run its
interactive /logout command. Zero CLI never reads or returns the underlying
access or refresh tokens.
Vercel AI Gateway remains available without /connect:
export AI_GATEWAY_API_KEY="your-ai-gateway-key"
bun run devWeb search uses the Brave Search API. Create a key in the Brave Search API dashboard and expose it without putting it in project configuration:
export BRAVE_SEARCH_API_KEY="your-brave-search-key"Running bun run dev with no arguments opens the TUI in the current directory. bun run start is an equivalent script.
The built-in default model alias uses openai/gpt-5.3-codex. Change only that built-in model without creating a configuration file with:
export ZERO_CLI_MODEL="provider/model-id"
bun run devAI_GATEWAY_API_KEY is consumed by the Vercel AI Gateway integration. A project model alias overrides the built-in default, so ZERO_CLI_MODEL has no effect on agents that use another configured alias.
Installed-package commands begin with zero-cli. From this source checkout,
replace that prefix with bun run dev --:
# Interactive TUI
zero-cli
# Headless task
zero-cli run "fix the failing parser tests"
# Read a task from stdin
printf '%s\n' 'explain the runtime' | bun run dev -- run
# Inspect or initialize configuration
bun run dev -- agents
bun run dev -- init
bun run dev -- trust
# Serve ACP over stdin/stdout
zero-cli acpGenerate completions without executing profile code:
zero-cli completion bash
zero-cli completion zsh
zero-cli completion fishWrite the output into the completion directory used by your shell, or source it from your profile. The generated scripts complete commands, fixed option values, directories, and attachment paths.
init creates zero-cli.jsonc and prompts/zero-cli/*.md, marks that workspace trusted in the user config directory, and refuses to shadow or overwrite any recognized project config path. Use trust when you created or audited a project configuration by hand.
# Run the general-purpose Zero agent
bun run dev -- run "design the migration"
# Run the deterministic plan → build → review workflow
bun run dev -- run --workflow feature "add request tracing"
# Override the selected model by alias
bun run dev -- run --model coder "repair the build"
# Let Zero CLI spend more rounds and use more specialists when useful
bun run dev -- run --depth pro "migrate the persistence layer"
# Continue the newest validated CLI checkpoint in this workspace
bun run dev -- sessions
bun run dev -- run --resume latest "continue with the next step"
# Attach files explicitly or inline
bun run dev -- run --attach spec.pdf --attach screenshot.png "implement the spec"
bun run dev -- run 'fix the layout shown in @"fixtures/mobile screenshot.png"'
# Choose a policy for automation
bun run dev -- run --permission-mode accept-edits "update generated types"
bun run dev -- run --yes "run with full tool access"
# Operate on another workspace
bun run dev -- run --cwd ../another-project "review this project"Run options are:
| Option | Meaning |
|---|---|
-w, --workflow <name> |
Run a configured sequence of agents |
-m, --model <alias> |
Override the model alias selected by the agent |
--reasoning-effort <level> |
Override reasoning with default, low, medium, high, xhigh, max, or ultra when supported |
--speed <mode> |
Override the service speed with standard or fast |
--depth <preset> |
Override work depth with flash, medium, pro, or ultra |
--resume <id|latest> |
Continue a validated CLI checkpoint in the same canonical workspace |
--attach <path> |
Add a workspace file; repeatable |
--add-dir <path> |
Permit another workspace directory; repeatable |
--permission-mode <mode> |
ask, read-only, accept-edits, or full-access |
-y, --yes |
Alias for --permission-mode full-access |
--json |
Write every runtime event as one JSON object per line |
--cwd <path> |
Select the workspace for this run |
--resume restores the saved orchestration mode, model identity, work depth,
and exact model history, so it cannot be combined with --workflow; pass --model only
when intentionally replacing a saved model whose configuration changed. A
normal model override is passed to every phase of a workflow; omit it when the
workflow should retain separate planner, builder, and reviewer models.
--depth overrides the configured workDepth for one run.
In a non-interactive process, a tool that needs approval is denied because there is no terminal from which to ask. CI jobs should select an explicit permission mode appropriate to the job rather than assuming ask will proceed.
Zero CLI includes a Harbor installed-agent adapter that evaluates the normal headless runtime in Terminal-Bench's disposable task containers. It packages the current checkout, installs it in each environment, applies an isolated full-access benchmark profile, and converts streamed runtime events into an ATIF v1.7 trajectory with embedded subagents (or v1.6 plus external child trajectories on legacy Harbor 0.5):
uv tool install harbor
export OPENAI_API_KEY=...
# Provider-free runtime and orchestration contract
bun run eval:local
# Inspect the exact command without starting a paid benchmark run
bun run bench:terminal -- --model openai/gpt-5.3-codex --dry-run
# Start with one task; options after -- are forwarded to Harbor
bun run bench:terminal -- \
--model openai/gpt-5.3-codex \
-- --include-task-name <terminal-bench-task-name>The launcher defaults to terminal-bench/terminal-bench-2, one attempt, one concurrent
trial, medium work depth, high reasoning, and standard service speed. Those
settings are explicit experiment inputs rather than hidden agent behavior.
See benchmarks/terminal_bench for
direct Harbor usage, Gateway/provider configuration, and reproducibility notes.
The TUI opens on a centered, responsive ZERO start screen with a compact two-line composer: the prompt sits above the fixed Zero root identity and clickable model, work-depth, reasoning, and speed controls, while the footer shows live context use and a real ⚙ settings button. Click it, press Ctrl-P or Ctrl-, or run /settings to open a full-terminal settings workspace with a persistent left sidebar. It covers session defaults, appearance, providers, models/LLMs, inspectable root/specialist profiles, workflows, skills, MCP/tools, web search, context compaction, multi-agent policy, permissions and limits, storage, plugins, and configuration sources. Run /workflow, /model, /reasoning, /speed, /depth, /connect, /mcp, /skills, /sessions, /goal, or /queue without arguments to browse or manage valid choices instead of guessing IDs. The user never needs to choose a specialist: Zero handles bounded work directly and delegates planning, exploration, isolated implementation, verification, review, or vision when useful. The model library is grouped by provider, and connected OpenAI, Anthropic, Kimi Code, Nebius, and ChatGPT/Codex catalogs load in place while configured Gateway and compatible models remain available. Sending the first task moves into the regular chat timeline. Active work has a live Working for … timer and animated Thinking... edge; after completion, intermediate reasoning and tools collapse beneath a clickable Worked for … row while the final answer and agent/model footer remain visible. Zero CLI keeps conversational model history under one root orchestrator.
Session model/workflow/depth/reasoning/speed changes made in Settings are snapshotted for the next run and never alter work already in flight. Agent profiles are configuration-owned and shown read-only: defaultAgent is the root while other profiles are internal delegation/workflow targets. Provider credentials continue to use the private credential store and are never rendered in Settings. Theme, native cursor, smooth-streaming, and Kitty/Ghostty image-mode preferences are stored separately in ~/.config/zero-cli/ui.json; appearance changes apply immediately to the mounted TUI. Structural project settings remain owned by the layered user/project JSONC files, so Settings displays every effective value and its apply boundary without flattening inherited configuration or writing credential-derived model aliases back into the project.
Available slash commands are:
| Command | Effect |
|---|---|
/connect |
Browse providers and open the setup flow |
/connect <openai|anthropic|kimi|nebius> |
Open setup directly at one provider |
/mcp |
Open the searchable MCP server manager |
/mcp <server> |
Open one configured MCP server directly |
/skills |
Open the searchable skill catalog |
/skills <name> |
Open one skill's details and instructions |
/workflow |
Open the searchable workflow picker |
/workflow <name> |
Select a deterministic workflow |
/workflow off |
Return to direct agent mode and clear model history |
/model |
Open the grouped, searchable model picker |
/model <alias> |
Override the model and clear history |
/model auto |
Use each agent's configured model |
/reasoning |
Open the reasoning-effort picker |
/reasoning <default|low|medium|high|xhigh|max|ultra> |
Override reasoning effort for this session when supported by the selected model |
/speed |
Open the model-speed picker |
/speed <default|standard|fast> |
Override speed for this session; default restores model configuration |
/depth |
Open the work-depth picker |
/depth <flash|medium|pro|ultra> |
Select the effort policy for subsequent tasks |
/search [query] |
Search persisted user and assistant messages across TUI chats and projects |
/sessions |
Open the searchable session manager |
/goal |
Open the durable-goal manager for this session |
/goal <objective> |
Start a bounded, evidence-driven Ralph loop using normal root runs and subagents |
/goal <status|pause|resume|cancel> |
Inspect or control the current durable goal |
/goal steer <message> |
Queue one-time direction for the goal's next iteration |
/queue |
Inspect and manage messages waiting behind the active run |
/new |
Start a fresh session and return to the start screen |
/clear |
Clear the visible timeline |
/settings |
Open the full settings workspace |
/quit, /exit |
Close the TUI |
/help |
Open the command reference popup |
Press Ctrl-F from the start screen or chat timeline, or run /search [query],
to search persisted user and assistant text across TUI chats and projects.
Selecting a result resumes its project and session, then reveals the exact
matching timeline entity. Active unsaved text is not part of this durable
index. Ghostty reserves Command-F for its native viewport search, so a TUI
cannot receive that key by default. To make Command-F send Zero CLI's Ctrl-F
shortcut instead, add keybind = super+f=text:\x06 to the Ghostty
configuration.
The session manager groups saved work by project, then by pinned/recent date, with
the current project first. Select a project header and press Enter or Space to
toggle it, use Left/Right to collapse or expand it, or click its disclosure
arrow. Search temporarily expands only matching projects without losing their
saved collapsed state. Press Enter on a session to resume it, Ctrl-N to start a
new one, Ctrl-F to pin or unpin, Ctrl-R to rename, Ctrl-D to request deletion,
or Escape to close. Deletion requires a Y confirmation. Messages submitted
while an agent is working are captured in a bounded FIFO queue and run
automatically in the same conversation. Open /queue or click its footer count
to inspect them; Enter expands a message, E edits it, D deletes it, and
Ctrl-Up/Ctrl-Down reorders it. Press Escape while a run is active to cancel it
and Ctrl-L to clear the timeline. When approval is requested, press Y to allow
once, A to allow that tool for the rest of the session, or N/Escape to deny.
The TUI requires stdin and stdout to be TTYs. Use run for pipes, scripts, and CI.
Configuration is validated with Zod and merged in this order:
- Built-in defaults.
~/.config/zero-cli/config.jsonc, when present.- The nearest trusted
zero-cli.jsonc,.zero-cli/zero-cli.jsonc, or.zero-cli/config.jsoncfound by walking upward from the workspace.
Project files are not loaded until you run zero-cli trust; this prevents an untrusted checkout from silently selecting an attacker-controlled model endpoint, widening permissions, or importing a TypeScript plugin. zero-cli init establishes that trust as part of its explicit setup action. Alternatively, set ZERO_CLI_CONFIG=/absolute/path/to/config.jsonc to select an explicit trusted file, or set ZERO_CLI_TRUST_PROJECT_CONFIG=1 for an intentional ephemeral override.
For migration compatibility, Zero CLI also recognizes the deprecated devout.jsonc, .devout/devout.jsonc, and .devout/config.jsonc project locations. New configuration should use a Zero CLI path. Deprecated DEVOUT_* environment-variable aliases remain accepted as fallbacks, but new scripts and deployments should use ZERO_CLI_* names.
Files use JSONC, including comments and trailing commas. Provider profiles, skills, MCP servers, model aliases, and agent entries merge by name; workflows, limits, and storage settings merge over the defaults; plugin path lists are combined without duplicates. Relative skill files, prompts, plugins, and MCP stdio working directories resolve from the file that declares them. A complete copy-and-edit starting point is available in zero-cli.example.jsonc.
An agent is deliberately small: a model alias, a prompt or prompt file, tool and skill allowlists, a permission and workspace mode, run budgets, optional accepted deliverable kinds, and an optional delegation allowlist. These entries are reusable profiles; a delegate chooses a profile instead of inventing a model, prompt, tool set, or authority for each child. The prompt assembler also adds runtime facts and up to 64 KiB from a workspace-root AGENTS.md.
workDepth is one effort toggle for agent and workflow work. It selects a bounded
runtime policy; it does not require the runtime to consume every round or spawn
every permitted agent. Work stops early when the task is complete.
| Preset | Model steps per run | Tool-call attempts per run | Repair rounds | Total runs | Concurrent children | Delegation depth | Campaign duration | Required gates |
|---|---|---|---|---|---|---|---|---|
flash |
8 | 32 | 1 | 1 | 1 | 0 | 2 minutes | none |
medium |
24 | 96 | 2 | 6 | 3 | 2 | 30 minutes | none |
pro |
64 | 256 | 6 | 24 | 6 | 4 | 2 hours | observed verification, independent review |
ultra |
100 | 500 | 12 | 64 | 8 | 8 | 8 hours | observed verification, independent review |
For pro and ultra, the controller remains task-sensitive: greetings
and read-only answers do not spawn ceremonial children. A root may plan in its
own context, delegate a planner, or submit a validated delegate.schedule
dependency graph, but no second structured-plan envelope blocks the first
mutation. After observed changes, the controller refuses to finish until a later run
succeeds and a separately configured review profile
approves the current revision. Review findings start another bounded
implementation/verification cycle, and any later mutation makes prior gates stale.
Mutation tracking includes the built-in write tool, write-risk extensions,
shared-write child results, and shell commands when Git can observe a workspace
delta. A failed verification command invalidates an earlier pass for the same
revision.
Preset values only narrow authority. Agent maxSteps, maxToolCalls, maxChildren, token and
timeout settings and the global multiAgent controls remain hard ceilings. Built-in
profiles use a 500-call emergency ceiling so the selected work depth normally controls
the effective allowance; a custom lower profile value is an explicit administrator cap.
Concurrency, child, and total-run ceilings bound any model-authored schedule, so a high
setting is a larger opportunity to parallelize useful work rather than a spawn quota.
Total runs counts the root, child launches, retries, and resumed child
executions. Review capacity is reserved instead of being available to ordinary
fan-out. Workflow phases share one root campaign, and the selected depth,
absolute deadline, and aggregate counters are persisted so restarting Zero CLI
does not renew a retained campaign's authority.
/goal <objective> runs a durable, bounded Ralph loop above the ordinary root
runtime. Every iteration has a unique run/task ID, may use configured
subagents normally, and advances only from validated changed-file,
verification, artifact, or response evidence. It remains inside the selected
work-depth policy, permissions, deadline, multi-agent ceilings, and root token
or cost budget; goal iteration and repeated-no-progress limits can only stop
work sooner.
Run /goal without arguments to inspect, pause, resume, cancel, or create goals
for the session. Goal, iteration, and steering state is persisted. With
goals.autoResume enabled, queued/running goals recover after restart by
cancelling any interrupted iteration and starting a fresh one from durable
state; interrupted commands and verification are never reported as complete.
See the durable-goal guide for the lifecycle
and configuration.
Zero CLI estimates each request against the selected model's configured
contextWindow (or context.fallbackWindowTokens) after reserving output,
tool-schema, and safety-buffer tokens. At the configured trigger, it preserves
the immutable canonical session history and builds a smaller active model view
from a validated structured checkpoint, artifact references, current runtime
and Git state, and a recent verbatim tail. Parent and child runs compact
independently; a parent retains a child's structured handoff without inheriting
the child's full trajectory.
The cutoff is token-budgeted and never splits a tool call/result, delegate
handoff, permission exchange, write result, or user turn. Older rehydratable
tool payloads may be replaced by compact metadata and stable artifacts before
the checkpoint model runs. Automatic token-pressure compaction, manual
/compact, and one bounded provider-overflow recovery path share the same
durable record. If model checkpointing fails, Zero CLI falls back to deterministic
state plus selected recent events rather than silently dropping the active user
task.
maxCampaignTokens and maxCostUsd cover the root, every child, retries,
workflow phases, and compaction calls. Per-agent maxTokens remains a narrower
per-run ceiling. When a campaign cap is active, otherwise concurrent model
calls use one root-level admission queue: each sibling recalculates its
allowance only after the preceding call's reported usage has been charged.
Cost is accumulated as integer micro-USD from provider usage;
because prices change, Zero CLI has no stale built-in price table. Enabling a
cost limit therefore requires explicit pricing for every configured agent
model, and cached-input discounts are used only when the provider reports
cache-read tokens. Admission uses an estimated input size and the provider's
output-token control; a provider that treats that control as advisory can still
overrun the boundary on its final request, while the recorded usage and cost
remain exact.
An agent's optional accepts list declares which delegated deliverables it is
suited for: analysis, plan, patch, review, or test_result. The
coordinator uses that metadata when choosing among the parent's
canDelegateTo profiles, with least-used deterministic routing and optional
planner hints; it never guesses from conventional profile names or bypasses the
delegation allowlist. Automatically routed read-only plan, analysis, and review work requires an
explicit shared-readonly workspace mode so they inspect the parent's live
checkout, including uncommitted changes. shared-write, git-worktree, and
scratch profiles are never candidates for those read gates, even when their
effective permission is read-only. Capability specialists such as the built-in
vision agent omit accepts, keeping them available for media routing without
turning them into generic repository-analysis candidates.
The providers object defines reusable connection profiles. Each profile selects openai, anthropic, kimi, nebius, or openai-compatible with type and may centralize baseURL, apiKeyEnv, and static headers. A model's provider can name that profile or use a concrete built-in provider directly; Gateway and ChatGPT/Codex subscription models stay available through direct gateway and openai-codex model entries. During loading, Zero CLI materializes every profile reference into a concrete model config, so the runtime and model picker continue to see the built-in provider types.
Provider profiles intentionally do not accept an apiKey value. apiKeyEnv is the name of an environment variable, not the credential itself. For OpenAI, Anthropic, Kimi, and Nebius profiles, omitting apiKeyEnv retains the normal canonical-environment-variable and /connect credential-store fallback. This keeps shareable zero-cli.jsonc files free of secrets.
Model-level baseURL and apiKeyEnv values override profile defaults. Profile headers and model headers are combined, with model values winning when the same header is present. Existing per-model provider configuration remains supported, so adopting profiles is optional.
Skills are provider-neutral, Codex-compatible instruction packages whose entry point is a SKILL.md file with YAML name and description frontmatter followed by Markdown instructions. The top-level skills map can register an additional package by path. A string is shorthand for an enabled skill; use the object form when a layer needs to disable one explicitly:
Zero CLI discovers direct child packages from these locations, in increasing precedence:
- Non-system packages in
~/.codex/skills/*/SKILL.md. - Zero CLI user packages in
~/.config/zero-cli/skills/*/SKILL.md. - Explicit entries in
~/.config/zero-cli/config.jsonc. - Trusted workspace packages in
<workspace>/.agents/skills/*/SKILL.md. - Explicit entries in the trusted project configuration.
A later package with the same ID replaces an earlier one. Workspace discovery is disabled until the workspace is trusted, even when it has no zero-cli.jsonc; audit the package and run bun run dev -- trust to enable it. Directories such as Codex's .system package directory are intentionally not imported.
For example, .agents/skills/release-notes/SKILL.md can contain:
---
name: release-notes
description: Use when drafting release notes from repository changes
---
# Release notes
Inspect the relevant commits and user-facing changes before drafting concise notes.{
"skills": {
"release-notes": "./automation/release-notes/SKILL.md",
"legacy-checks": {
"path": "./automation/legacy-checks/SKILL.md",
"enabled": false
}
},
// defaultAgent is the one user-facing root. The remaining profiles are
// internal specialists selected by workflows or delegate deliverables.
"agents": {
"build": {
"skills": ["release-notes"]
},
"review": {
"skills": ["*"]
}
}
}An agent receives only its allowlisted skill metadata; "*" selects every enabled skill. Zero CLI uses progressive disclosure: the model first sees each selected skill's name, description, and SKILL.md path, then uses read to load the complete file only when the user mentions $skill-name or the description matches the task. In the TUI, type $ to search the skills active for the current agent or workflow; completing a result inserts one highlighted, atomically editable reference. Referenced resources resolve relative to the skill directory, and validated external skill directories are available to read without becoming writable roots. A skill supplies instructions only—it cannot grant a tool, permission, writable root, or delegation target that the agent does not already have, so a custom skill-using agent must include read in its tool allowlist. Use /skills to search the catalog and see which packages are active for the current agent; /skills <name> opens a package's provenance and instructions directly.
The following shape assigns semantic aliases to phases. Replace the illustrative model IDs and endpoint with values available to your account.
{
"version": 2,
"defaultAgent": "build",
"workDepth": "medium",
"permissionMode": "ask",
"providers": {
"company-openai": {
"type": "openai",
"baseURL": "https://api.openai.com/v1",
"apiKeyEnv": "COMPANY_OPENAI_API_KEY"
}
},
"webSearch": {
"provider": "brave",
"apiKeyEnv": "BRAVE_SEARCH_API_KEY",
"safeSearch": "moderate"
},
"models": {
"planner": {
"provider": "company-openai",
"model": "planning-model",
"capabilities": ["text", "tools", "structured-output"]
},
"coder": {
"provider": "company-openai",
"model": "coding-model",
"contextWindow": 200000,
"capabilities": ["text", "tools"]
},
"reviewer": {
"provider": "company-openai",
"model": "review-model",
"capabilities": ["text", "tools"]
},
"vision-model": {
"provider": "gateway",
"model": "provider/multimodal-model",
"capabilities": ["text", "image", "file", "tools"]
}
},
"agents": {
"plan": {
"description": "Read-only planner",
"model": "planner",
"promptFile": "./prompts/zero-cli/plan.md",
"tools": ["read", "find", "delegate"],
"skills": ["*"],
"canDelegateTo": ["explore"],
"accepts": ["plan"],
"permissionMode": "read-only",
"workspaceMode": "shared-readonly",
"maxChildren": 64,
"maxSteps": 100,
"maxToolCalls": 500
},
"build": {
"description": "General-purpose root orchestrator",
"model": "coder",
"promptFile": "./prompts/zero-cli/build.md",
"tools": ["read", "write", "run", "find", "search", "delegate"],
"skills": ["*"],
"canDelegateTo": ["plan", "explore", "implement", "verify", "review", "vision"],
"accepts": ["patch", "test_result"],
"workspaceMode": "shared-write",
"maxChildren": 64,
"maxTokens": 200000,
"timeoutMs": 28800000,
"maxSteps": 100,
"maxToolCalls": 500
},
"explore": {
"description": "Repository explorer",
"model": "coder",
"promptFile": "./prompts/zero-cli/explore.md",
"tools": ["read", "find"],
"skills": ["*"],
"accepts": ["analysis"],
"permissionMode": "read-only",
"workspaceMode": "shared-readonly",
"maxChildren": 0,
"maxSteps": 100
},
"implement": {
"description": "Bounded implementation specialist",
"model": "coder",
"promptFile": "./prompts/zero-cli/implement.md",
"tools": ["read", "write", "find", "run", "search"],
"skills": ["*"],
"accepts": ["patch"],
"permissionMode": "accept-edits",
"workspaceMode": "git-worktree",
"maxChildren": 0,
"maxSteps": 100
},
"verify": {
"description": "Verification and test specialist",
"model": "coder",
"promptFile": "./prompts/zero-cli/verify.md",
"tools": ["read", "find", "run"],
"skills": ["*"],
"accepts": ["test_result"],
"permissionMode": "ask",
"workspaceMode": "shared-write",
"maxChildren": 0,
"maxSteps": 100
},
"review": {
"description": "Independent reviewer",
"model": "reviewer",
"promptFile": "./prompts/zero-cli/review.md",
"tools": ["read", "find"],
"skills": ["*"],
"accepts": ["review"],
"permissionMode": "read-only",
"workspaceMode": "shared-readonly",
"maxChildren": 0,
"maxSteps": 100
},
"vision": {
"description": "Image and document specialist",
"model": "vision-model",
"promptFile": "./prompts/zero-cli/vision.md",
"tools": ["read"],
"skills": ["*"],
"permissionMode": "read-only",
"workspaceMode": "shared-readonly",
"maxChildren": 0,
"maxSteps": 100
}
},
"workflows": {
"feature": {
"description": "Plan, implement, then review",
"steps": ["plan", "build", "review"]
}
},
"multiAgent": {
"maxDepth": 8,
"maxConcurrent": 8,
"maxChildrenPerAgent": 64,
"defaultContext": "summary",
"cancelChildrenWithParent": true,
"maxRetries": 1
},
"goals": {
"enabled": true,
"maxIterations": 64,
"maxNoProgressIterations": 3,
"iterationDelayMs": 0,
"autoResume": true
},
"limits": {
// Deprecated delegation aliases retained for older configuration files.
"maxDepth": 8,
"maxChildrenPerRun": 64,
"runTimeoutMs": 28800000,
"toolTimeoutMs": 120000,
"maxAttachmentBytes": 10485760
}
}contextWindow is optional model metadata. When the provider reports a context
limit (as Codex does) or this value is configured, the TUI shows both current
token use and a percentage; otherwise it shows token use without inventing a
capacity.
Workflow steps accept stable IDs and dependencies. Legacy string lists remain sequential, while graph-shaped steps can run independent read-only work in parallel and join it deterministically:
{
"workflows": {
"feature": {
"description": "Explore in parallel, then implement and review",
"steps": [
{ "id": "backend", "agent": "explore", "dependsOn": [] },
{ "id": "tests", "agent": "explore", "dependsOn": [] },
{ "id": "build", "agent": "build", "dependsOn": ["backend", "tests"] },
{ "id": "review", "agent": "review", "dependsOn": ["build"], "maxRetries": 1 }
]
}
}
}Duplicate IDs, unknown dependencies, and cycles fail configuration. Only shared-readonly phases overlap; a shared-write phase runs alone. Each phase receives bounded structured handoffs from its direct dependencies, retries are limited to three, failed dependencies skip their dependants, and all phases share one campaign root, deadline, token budget, and cost budget. Isolated worktree results still return patch artifacts for explicit inspection or adoption; Zero CLI does not silently merge parallel patches or guess through semantic conflicts.
Agent profiles may declare shared-readonly, shared-write, git-worktree,
or scratch workspace handling. shared-readonly also forces the child's
effective permission mode to read-only and is required for automatically routed
planning, analysis, and review so those agents see the parent's current working
tree. git-worktree creates a detached
checkout from the parent repository's HEAD; its changed files and binary-safe
diff return as result evidence without modifying the parent checkout. scratch
creates an empty private temporary directory for reproductions and experiments.
Isolated workspaces remain available for follow-up turns and are removed when
the runtime shuts down. maxChildren, maxTokens, and timeoutMs apply
additional per-profile ceilings. Global delegation policy lives under
multiAgent; limits.maxDepth and limits.maxChildrenPerRun remain accepted
as legacy aliases, while explicitly configured multiAgent values take
precedence.
Zero CLI also supports reusable profiles for generic OpenAI-compatible endpoints:
{
"providers": {
"local": {
"type": "openai-compatible",
"baseURL": "http://127.0.0.1:11434/v1",
"apiKeyEnv": "LOCAL_MODEL_API_KEY"
}
},
"models": {
"local-coder": {
"provider": "local",
"model": "your-model",
"capabilities": ["text", "tools"]
}
},
"agents": {
"build": { "model": "local-coder" }
}
}apiKeyEnv is optional; omit it for an endpoint that does not require authentication. When configured, the named environment variable must be present when that model is used. Static headers can be set on a profile or model, while model request options belong in settings. Models may set reasoningEffort (default, low, medium, high, xhigh, max, or ultra) and speed (standard or fast). Declare exact support with supportedReasoningEfforts and supportedSpeeds; the TUI filters its controls and the runtime validates every request against those values. max and ultra require explicit model metadata. ChatGPT subscription models receive this metadata from the live Codex catalog. Kimi account and Kimi API transports currently expose only provider-default reasoning and standard speed; choose an exact Kimi high-speed model instead of a per-request Fast tier. Headless runs can override supported values with --reasoning-effort and --speed.
The top-level mcp object configures local stdio or remote Streamable HTTP servers. An agent receives discovered tools only from the server IDs in its explicit mcpServers allowlist:
{
"mcp": {
"local-docs": {
"type": "stdio",
"command": "/absolute/path/to/docs-mcp",
"args": ["--workspace", "."],
"cwd": ".",
"env": { "LOG_LEVEL": "warn" }
},
"issues": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"oauth": true
},
"enterprise": {
"type": "http",
"url": "https://tools.example.com/mcp",
"oauth": {
"clientId": "registered-client-id",
"clientSecretEnv": "ENTERPRISE_MCP_CLIENT_SECRET",
"scopes": ["tools.read", "tools.write"],
"callbackPort": 53682
}
}
},
"agents": {
"build": {
"mcpServers": ["local-docs", "issues", "enterprise"]
}
}
}Stdio commands beginning with ./ or ../, and every relative cwd, resolve from the configuration file that declares them; bare command names use the safe inherited PATH. Project MCP configuration is ignored until the workspace is trusted because stdio entries execute programs. Enabled servers connect independently at startup, so one unavailable server does not prevent Zero CLI from opening. Discovered tools are registered as collision-safe names such as mcp__issues__create_u2d_issue; HTTP calls retain network risk and stdio calls retain execute risk, regardless of advisory server annotations, so they pass through the normal permission UI.
Run /mcp to search configured servers, inspect discovered tools, connect, refresh, disconnect, authenticate, or sign out. A server with oauth: true uses OAuth 2.1 protected-resource discovery, dynamic client registration when available, S256 PKCE, state, and the MCP resource indicator. The popup shows a clickable authorization URL and the automatic loopback callback while it waits. Tokens, refresh tokens, discovery metadata, and dynamic registration data are stored separately from project config in the private ~/.config/zero-cli/mcp-oauth.json file. The default callback is http://127.0.0.1:53682/oauth/callback; set ZERO_CLI_MCP_OAUTH_CALLBACK_PORT or oauth.callbackPort when a registered client requires another fixed port.
The same per-agent server allowlist scopes MCP resources and prompts. The
existing read tool opens mcp://content/<server> to discover a bounded
catalog, then follows the opaque resource or rendered-prompt references it
returns. Prompt arguments are schema-validated, pagination is bounded and
loop-detected, text and base64 payloads have byte/count limits, and remote
content remains untrusted model input. This keeps the public tool surface at
six tools while supporting MCP tools, resources, and prompts. MCP sampling,
elicitation, and task-based tool execution are not implemented.
For a pre-registered public client, set clientId without clientSecretEnv. For a confidential client, name the secret environment variable; never put the secret itself in JSONC. Strict RFC 9728 discovery and advertised PKCE S256 support are required by default. allowLegacyDiscovery: true exists only for a trusted older server that still treats its origin as the authorization server. Cross-origin discovery is restricted to publicly routable HTTPS destinations; set allowPrivateNetwork: true only when a trusted enterprise server intentionally uses a separate private-network authorization host.
/connect supports six concrete connection paths:
| Choice | Runtime path | Credential handling | Generated alias |
|---|---|---|---|
| OpenAI API key | Native AI SDK OpenAI Responses provider | Zero CLI private credential store or OPENAI_API_KEY |
openai |
| OpenAI ChatGPT subscription | Official Codex App Server and Codex-managed login | OAuth tokens remain in Codex's own store | openai-subscription |
| Anthropic API key | Native AI SDK Anthropic Messages provider | Zero CLI private credential store or ANTHROPIC_API_KEY |
anthropic |
| Kimi account | Official Kimi Code CLI browser login and ACP | OAuth tokens remain in the Kimi CLI's own store | kimi-subscription |
| Kimi Code API key | OpenAI-compatible Kimi Code endpoint | Zero CLI private credential store or KIMI_API_KEY |
kimi |
| Nebius Token Factory API key | OpenAI-compatible Token Factory endpoint | Zero CLI private credential store or NEBIUS_API_KEY |
nebius |
The recommended Kimi account option launches kimi login from the bundled official Kimi Code CLI and waits for its short-lived device authorization. Zero CLI captures the URL, requires the official HTTPS Kimi domain, and opens it with an absolute operating-system browser command; the CLI child receives no executable search path. Zero CLI never embeds Kimi's OAuth client identity and never reads, copies, refreshes, or stores the resulting tokens. Kimi owns them under the private ~/.config/zero-cli/kimi-code home; set ZERO_CLI_KIMI_CODE_HOME only for a trusted deployment that needs another isolated location. Subscription model steps run through kimi acp in a synthetic home and private non-project directory. Zero CLI pins the managed provider, OAuth, model-catalog, and search/fetch service configuration to Kimi's official endpoints, denies ACP host capabilities and direct Kimi-native execution, and rejects Kimi MCP/plugin/hook/instruction customization. Kimi may select compatible operations through its native Read, Glob, Grep, Bash, Write, Edit, WebSearch, and FetchURL interfaces; Zero CLI captures the call before execution, rejects the inner permission, maps it to read, find, run, write, or search, validates it against the current agent's tool set, assigns a fresh call ID, and executes it through the normal budget, scope, permission, event, diff, and artifact pipeline. Unsupported variants fail closed instead of changing semantics. Streamed thoughts and visible response text continue to flow into Zero CLI. Kimi ACP does not expose Zero CLI's granular reasoning levels or a per-request fast tier, so use Reasoning Default and Speed Standard; choose Kimi's high-speed catalog model explicitly when it is available. Kimi's internal transport session is removed after each CLI process exits so it does not become a second durable transcript.
The alternative Kimi Code API-key path uses https://api.kimi.com/coding/v1 and a key created in the Kimi Code Console. That key consumes membership quota and is distinct from both Kimi CLI OAuth credentials and Kimi Open Platform pay-as-you-go keys. Zero CLI identifies itself with its real zero-cli/<version> User-Agent as Kimi requires. Model discovery is live; kimi-for-coding is preferred when available, while kimi-for-coding-highspeed remains an explicit choice for eligible plans.
After connecting, the selected provider immediately replaces unauthenticated Gateway-backed models in the TUI. This fallback is resolved per agent: configured OpenAI, Anthropic, Kimi, Nebius, and Codex models—and mixed workflow phases using them—remain unchanged. A campaign already running on a direct provider also uses that effective model for Gateway-backed delegated planning, analysis, and review profiles, so a subagent cannot silently cross back to Vercel AI Gateway. Explicitly configured non-Gateway specialist models remain independent. Click the active model or run /model to choose an explicit global override. You can still select the generated alias with /model <alias> or assign it to any agent in JSONC. On later TUI launches, the most recently updated saved connection is restored as this fallback when neither a Gateway API key nor Vercel OIDC authentication is available. Stored aliases are restored for TUI, headless, and ACP runs; catalog choices made in the picker are session overrides and do not silently rewrite the stored provider default.
API keys are written outside the project to:
~/.config/zero-cli/credentials.json
The parent directory is required to be user-owned and is set to mode 0700; the file is 0600 on POSIX. Symlinked parents/targets and non-regular targets are rejected. Writes use no-follow handles, an exclusive same-directory temporary file, identity checks, and atomic rename. The file contains one API key and selected model per provider; it must be treated like a password and must never be committed. Set ZERO_CLI_CREDENTIALS_FILE only when a trusted deployment needs a different location. Canonical environment variables take precedence over stored keys, and a model- or profile-level apiKeyEnv takes precedence over both.
The ChatGPT option does not copy, parse, or expose Codex OAuth tokens and does not turn a ChatGPT subscription into a general OpenAI API key. It uses a Zero CLI-specific Codex home at ~/.config/zero-cli/codex, asks the official Codex App Server to start browser OAuth, displays its short-lived authorization link, and waits for the localhost callback. Device-code sign-in remains available as a headless fallback. After login, Zero CLI confirms that the account type is exactly chatgpt (API-key Codex login is rejected), then uses an isolated Codex App Server turn for subscription-backed model steps. App Server message and reasoning notifications are forwarded incrementally, while the completed structured response remains authoritative for validated Zero CLI tool calls. The auth home is private, uses a separate safe process cwd, and refuses a config.toml so account checks and model turns cannot inherit MCP startup configuration. The inner Codex turn runs ephemerally in a private empty directory with a minimal filesystem profile, network/web search disabled, and shell, MCP, plugin, app, connector, and multi-agent surfaces disabled. The model can only return structured Zero CLI tool requests, which still pass through Zero CLI's normal budgets and permission policy. Set ZERO_CLI_CODEX_HOME only for a trusted deployment that needs a different empty, config-free Codex home.
Direct provider configuration is also available without the interactive command:
{
"models": {
"openai-direct": {
"provider": "openai",
"model": "<OpenAI model id>",
"apiKeyEnv": "OPENAI_API_KEY"
},
"anthropic-direct": {
"provider": "anthropic",
"model": "<Anthropic model id>",
"apiKeyEnv": "ANTHROPIC_API_KEY"
},
"kimi-direct": {
"provider": "kimi",
"model": "kimi-for-coding",
"apiKeyEnv": "KIMI_API_KEY"
},
"nebius-direct": {
"provider": "nebius",
"model": "<exact Token Factory model id>",
"apiKeyEnv": "NEBIUS_API_KEY"
},
"codex-subscription": {
"provider": "openai-codex",
"model": "default"
}
}
}| Tool | Risk | Implemented behavior |
|---|---|---|
read |
read/network | Reads directories, numbered file ranges, images, PDFs, artifact://<sha256> references, and public HTTP(S) URLs; media remains typed content |
write |
write | Creates, edits, moves, and deletes workspace files/directories; edits support exact replacement or a checked unified diff |
run |
execute | Runs one bounded, non-interactive shell command from an allowed workspace directory |
find |
read | Uses ripgrep to search local filenames, symbols, or fixed-string/regex text while excluding secret-like paths |
search |
network | Searches the public web through Brave Search and returns bounded, sanitized title/URL/snippet records |
delegate |
delegate | Spawns and manages task-contracted child sessions through configured agent profiles |
For non-trivial runs, Zero CLI can also inject an intrinsic todo progress ledger. It is deliberately separate from the six capability tools: it cannot read or mutate the workspace, satisfy acceptance criteria, or act as verification. Each parent and child run owns an isolated ledger, and flash-depth runs omit it entirely to keep simple requests fast.
Tool outputs are structured. Large or truncated reads, local finds, command output, child reports, worktree patches, and diffs are stored by SHA-256 in Zero CLI's private user-state directory, with a compact artifact reference returned to the model. A later agent can load one with read({ "path": "artifact://<sha256>" }): text supports normal line ranges, supported media returns typed content, and the store verifies the bytes against the content ID. Oversized artifacts, files, and URL responses remain bounded before being returned.
find hides .git, .zero-cli, the deprecated .devout state directory, node_modules, and common secret/key filenames even when a model supplies a matching include glob. run applies a timeout, caps captured output, sends progress events, starts a non-login shell, and receives a filtered environment containing normal terminal/runtime variables rather than the entire Zero CLI process environment. URL read rejects non-public DNS answers and pins each request socket to a validated address while retaining normal hostname/TLS verification. Web search uses a fixed Brave endpoint, disables redirects, limits response size and result count, and never exposes its API key to the model. Search result snippets and URL contents are untrusted evidence.
Configuration version 2 introduces the six names above. Version-1 layers are migrated before merging: local search becomes find, apply_patch becomes write, and exec becomes run.
The policy is based on each tool's declared risk and is evaluated before the tool executes.
| Mode | Local read, find, and delegation |
write |
run, web search, URL read, or a custom network tool |
Direct secret-like read |
|---|---|---|---|---|
read-only |
allow | deny | deny | deny |
ask |
allow | ask | ask | ask |
accept-edits |
allow | allow | ask | ask |
full-access |
allow | allow | allow | allow |
Secret-like paths include common .env, credential, key, certificate, and package-registry credential filenames. This is a narrow filename heuristic, not a complete secret scanner.
An allow-always answer is scoped to the current session, tool name, effective risk, and operation. A local read approval does not approve URL fetches, and a write edit approval does not approve deletes. A delegated child receives the more restrictive of its own policy and its parent's effective policy, so a child cannot silently widen access. Agent tool allowlists are checked separately from the policy mode.
Zero CLI implements two complementary mechanisms:
- Workflows are application-controlled sequences such as
plan → build → review. They guarantee which configured agent and model receives each phase. - Delegation is a model-callable controller for bounded specialist tasks such as repository exploration, patching, review, testing, or media analysis.
The shipped root can automatically cover every model-facing deliverable: plan
for plans, explore for analysis, implement for isolated patches, verify
for test results, and review for independent review. These profiles are
internal defaults and remain fully replaceable through configuration.
A subagent is not a second runtime. Root and child agents both run through the same recursive agent loop and use the same lifecycle representation. Every run records its run, root, optional parent, task, agent, model, status, depth, workspace, effective permissions, budgets, and timestamps. Child lifecycle events retain the parent and root IDs, which lets the TUI, persistence layer, JSON event stream, and ACP updates present one run tree.
delegate does not accept an unstructured child prompt. A new child is spawned
with a task name, concrete goal, deliverable, and at
least one acceptance criterion:
{
"operation": "spawn",
"taskName": "locate-auth-flow",
"goal": "Find where OAuth callbacks are received and tokens are validated",
"scope": {
"paths": ["src/auth/**"],
"components": ["OAuth callback"]
},
"deliverable": "analysis",
"acceptanceCriteria": [
"Identify the callback entry point",
"Identify token validation",
"Provide exact file locations"
],
"contextMode": "summary",
"context": ["The failing provider is company-openai"],
"artifactRefs": [],
"background": true
}deliverable is one of analysis, plan, patch, review, or
test_result. scope.paths constrains direct read, find, and write
calls and validates reported changed files; scope.components is task context,
not a code-level sandbox. A path-scoped child cannot use run or an unknown
workspace-capable plugin/MCP tool because those operations cannot be confined
to the declared paths; web search remains available, and nested delegate
calls inherit or narrow the parent's scope. The runtime rejects an unverifiable scoped patch,
blocks a read-only result that reports changed files, requires a non-empty
change set for a completed patch, and requires passed verification for a
completed test_result. Acceptance criteria are injected into the child's
task prompt, but are not yet independently evaluated as a semantic checklist.
When agent is omitted (or is "auto"), the runtime chooses an eligible
profile from the requested deliverable, declared accepts metadata, workspace
mode, permissions, and the parent's canDelegateTo allowlist. A named profile
is an optional internal orchestration hint, not a user-facing mode. The child
profile fixes its model, prompt, tools, skills, MCP servers, workspace mode, and
budgets. Its effective permission mode is the more restrictive of parent and
child; a read-only workspace lowers it further. Task path scope can narrow this
authority but never widen it.
Context inheritance is selected per spawn:
"none"sends no parent history."summary"sends a compact bounded snapshot.{ "type": "recent", "turns": 4 }sends the requested recent turns."full"sends bounded serialized parent history.
Explicit context strings and artifactRefs are independent of that history
choice. The child always receives its task contract. The internal controller
also supports selected-message context, but that mode is not exposed through
the model-facing tool yet. When contextMode is omitted, delegate applies
multiAgent.defaultContext; a configured "recent" default uses six turns.
Workspace access comes only from the selected profile. shared-readonly and
shared-write use the parent's roots. Read-only children can run concurrently.
The queue serializes shared-write children that use the same workspace roots,
preventing independent writers from editing one shared checkout at the same
time while allowing unrelated work to use the remaining concurrency budget.
git-worktree creates a detached checkout from the parent repository's
committed HEAD and remaps nested workspace roots into it. The child cannot
see uncommitted parent-checkout changes. On completion, Zero CLI collects changed
paths plus a binary-safe Git diff and returns the diff as a patch artifact;
it does not merge or apply that patch to the parent automatically. scratch
creates an empty private temporary directory. Both isolated workspaces are
retained for send follow-ups and cleaned up during runtime shutdown. A
dependency-aware scheduler can run ready read-only and isolated-write tasks in
parallel while the same-root writer lock remains authoritative. Patch results
are returned to the parent; they are not merged automatically.
A foreground spawn waits and returns its completed result. A background spawn
returns childRunId, taskId, and queued run metadata immediately. Use the
same delegate tool to manage that child:
{
"operation": "schedule",
"tasks": [
{
"id": "explore-auth",
"taskName": "explore-auth",
"agent": "explore",
"goal": "Locate the auth flow",
"deliverable": "analysis",
"acceptanceCriteria": ["Return exact paths"],
"dependencies": [],
"access": "read"
},
{
"id": "review-auth",
"taskName": "review-auth",
"agent": "review",
"goal": "Review the investigation",
"deliverable": "review",
"acceptanceCriteria": ["Return a verdict"],
"dependencies": ["explore-auth"],
"access": "read"
}
]
}schedule validates IDs, dependencies, cycles, access declarations, profile
capabilities, depth, and child budgets before executing dependency layers. It
passes compact dependency results and artifact references into each downstream
task. Failed or blocked dependencies produce a structured skipped result rather
than running their dependents.
{ "operation": "wait", "runIds": ["child-run-id"], "timeoutMs": 30000 }{ "operation": "inspect", "runId": "child-run-id" }{ "operation": "cancel", "runId": "child-run-id" }Background completion is event-driven rather than polled. Before the parent's
next model step, the runtime injects a compact completion containing the child
and task IDs, status, summary, findings, changed files, artifact references,
verification, and follow-ups. If the parent turn has already ended, that
completion is carried to the next root run in the same session. An explicit
wait is useful when the parent must synchronize before continuing; consuming
a completion through wait or send prevents a duplicate injected handoff.
wait returns completed envelopes and any still-pending run records; a timeout
does not cancel pending work. Model-facing inspect returns the run, task,
current result, and retained message count without copying the full child
transcript into parent context. With
cancelChildrenWithParent: true (the default), ending or cancelling a parent
also cancels its unfinished children.
Completed children are retained for the lifetime of the current service and
can be continued without rediscovery. send queues another turn on the same
child run ID and reuses its model history:
{
"operation": "send",
"runId": "child-run-id",
"message": "Check whether the same issue affects refresh tokens."
}send currently accepts only a terminal child; it is resume, not a way to
interrupt a running turn. Resuming a failed child consumes one retry and is
limited by multiAgent.maxRetries; successful and cancelled follow-up turns
are continuations rather than failure retries. With storage enabled, child
metadata and bounded binary-safe model-history checkpoints are materialized in
SQLite. Addressing a retained shared-workspace child by run ID rehydrates it
after a Zero CLI restart. Scratch directories and detached worktrees remain
process-local and deliberately cannot be resumed after restart.
The runtime normalizes each completed child into an AgentResult envelope
rather than returning an arbitrary assistant string:
{
"status": "completed",
"summary": "OAuth callbacks enter in src/auth/callback.ts.",
"findings": [
{
"path": "src/auth/callback.ts",
"line": 42,
"description": "The handler exchanges and validates the code.",
"confidence": 0.95
}
],
"changedFiles": [],
"artifacts": [],
"verification": [
{ "command": "bun test auth", "status": "passed" }
],
"followUps": []
}The child is asked for a validated JSON envelope. The runtime accepts typed
findings and follow-ups from that envelope, but derives changed files,
artifacts, patch references, and verification from observed tool evidence.
Plan results add dependency-checked steps, review results add a verdict and
structured issues, and patch results must reference an artifact that was
actually returned. A non-JSON final response still becomes a bounded summary
inside the same base envelope. Large output is kept as an artifact; any agent
with read can load it through its artifact://<sha256> reference.
Per-profile ceilings include maxSteps, maxToolCalls, maxTokens,
timeoutMs, and maxChildren. The runtime intersects maxChildren with
multiAgent.maxChildrenPerAgent and applies the smaller run deadline. Global
controls enforced by delegation are maxDepth, maxConcurrent,
maxChildrenPerAgent, and cancelChildrenWithParent. The selected work-depth
preset adds root-wide step, round, total-run, concurrency, delegation-depth, and
duration bounds; the smaller applicable ceiling always wins. Queued children
run up to the effective concurrency limit, so independent read-only background
tasks can execute in parallel; same-root shared writers are serialized as
described above.
multiAgent.defaultContext supplies an omitted spawn context mode.
multiAgent.maxRetries caps explicit send resumptions after a failed child;
it does not initiate retries automatically. Workflow-step maxRetries remains
a separate setting for application-controlled workflows. Runtime guards also
include per-tool timeouts, cancellation, and a three-identical-call repetition
guard.
Attach a workspace file with a repeatable --attach flag or an inline @path mention:
bun run dev -- run --attach docs/spec.pdf "implement section 4"
bun run dev -- run 'fix @screenshots/desktop.png and compare @screenshots/mobile.png'
bun run dev -- run 'inspect @"design references/home page.png"'In the TUI, typing @ opens a filtered workspace-file menu; use the arrow keys to move and Tab or Enter to insert the selected path. Paths containing spaces are quoted automatically. The same quoted and unquoted syntax works in headless prompts, and selected names appear above the composer. An @mention that is not an existing in-workspace file remains ordinary prompt text. The picker omits common dependency/state directories, secret-looking files, and symlinks that escape the workspace. Every attachment must be a file inside an allowed root and is capped independently by limits.maxAttachmentBytes (10 MiB by default).
Images, MP3/WAV audio, PDFs, and other files remain binary content blocks with MIME types. Before the first model call, Zero CLI compares those blocks with the selected model alias's declared image, audio, and file capabilities:
- If the model declares the required capabilities, it receives the original media.
- Otherwise Zero CLI searches the root agent's allowed delegation targets, preferring an agent named
vision, whose model declares all missing capabilities. This capability route is independent of genericacceptsmetadata. - The specialist receives the media in a bounded child run. Its implementation-relevant text report replaces the unsupported media in the parent model's context.
- If no eligible specialist exists or no child budget remains, the run fails explicitly.
This lets a text-only coding model work with a separate image/document specialist. Routing is driven by configuration, so incorrect capability declarations can still produce provider errors.
Configuration handles model, root-profile, specialist, and workflow changes. Trusted TypeScript plugins can register additional tools and participate in the runtime lifecycle.
A plugin is a default-exported object with this shape:
interface ZeroCliPlugin {
hooks?: {
beforeRun?(context: RunHookContext): void | Promise<void>;
beforeStep?(context: StepHookContext): void | Promise<void>;
beforeModel?(request: ModelStepRequest): ModelStepRequest | void | Promise<ModelStepRequest | void>;
afterModel?(result: ModelStepResult, context: StepHookContext): void | Promise<void>;
beforeTool?(context: ToolHookContext): ToolCall | void | Promise<ToolCall | void>;
afterTool?(result: ToolResult, context: ToolHookContext): void | Promise<void>;
onAgentSpawn?(parent: RunHookContext, child: RunHookContext): void | Promise<void>;
shouldStop?(context: StepHookContext): boolean | Promise<boolean>;
afterRun?(result: RunResult): void | Promise<void>;
onError?(error: unknown, context: RunHookContext): void | Promise<void>;
};
tools?: ToolDefinition[];
}For example, a plugin inside this source checkout can use the included type helper:
// zero-cli.plugin.ts
import { definePlugin } from "./src/plugins/load.ts";
export default definePlugin({
hooks: {
beforeRun(context) {
console.error(`[zero-cli] starting ${context.agent}`);
},
beforeTool(context) {
// Hooks may inspect or transform the proposed call before policy checks.
return context.call;
},
shouldStop(context) {
return context.step > 30;
},
},
tools: [],
});Enable it from the configuration:
{
"plugins": ["./zero-cli.plugin.ts"]
}Paths resolve relative to the configuration file that declares them. Project plugins load only after the workspace is explicitly trusted. Plugins load once at startup in configured order. Extra tools must provide a Zod input schema, risk, protocol-neutral activity kind, and executor, and must also be listed in an agent's tools array before that agent can call them. Duplicate and unavailable tool names are rejected during startup.
Plugins are executable code with the same operating-system privileges as Zero CLI. There is no plugin sandbox or hot reload, and this source-first MVP does not yet expose a published plugin SDK package. Enable only modules you trust.
Start an installed Zero CLI package as an ACP agent with:
zero-cli acpAn ACP client/editor can launch the installed command directly. Adapt the field names to the client:
{
"command": "zero-cli",
"args": ["acp"],
"cwd": "/absolute/path/to/project"
}During source development, point the client at the checkout instead:
{
"command": "bun",
"args": [
"run",
"/absolute/path/to/zero-cli/src/index.ts",
"acp"
],
"cwd": "/absolute/path/to/project"
}Stored /connect credentials and aliases load automatically. Alternatively, ensure the spawned process inherits AI_GATEWAY_API_KEY or any configured provider key variables. Stdout is reserved exclusively for ACP's NDJSON transport; client launchers should treat stderr as diagnostics.
The current adapter uses the pinned @agentclientprotocol/sdk and supports:
- protocol initialization and capability negotiation;
- new durable sessions plus validated list, load, resume, and close operations;
- per-session service isolation, with configuration, models, plugins, state, and tools loaded from the canonical session workspace;
- configured agents exposed as ACP session modes, including mode changes;
- model and workflow configuration selectors, including workflow execution and streaming;
- prompt turns with per-session conversational history;
- text, base64 images/audio, resource links, and embedded text/blob resources;
- additional session directories as explicit workspace roots;
- session-scoped stdio and Streamable HTTP MCP servers supplied by the ACP client;
- streamed message, reasoning, plan, tool-call, tool-progress, and tool-result updates;
- parent/root/task/run metadata for delegated lifecycle and completion updates;
- permission requests with allow-once, allow-always, and deny outcomes;
- cancellation propagated into the runtime and child processes.
ACP exposes one Zero root mode; configured specialists remain child-run
identities and workflows are selected through session configuration options.
Durable restoration validates the canonical workspace,
surface, agent or workflow, and exact model identity before recreating any MCP
processes. session/load replays bounded user, assistant, reasoning, and tool
history, while session/resume restores the same state without replaying it.
Closing a live session releases its resources but retains its durable
checkpoint. ACP-provided stdio and HTTP MCP servers are isolated to that
session and closed with it. Legacy SSE and experimental MCP-over-ACP transports
are rejected because this client implements the current Streamable HTTP
transport.
run --json is for scripts that want Zero CLI's internal event stream without rendering the TUI:
bun run dev -- run --json "map the repository"
bun run dev -- run --json "run the checks" | jq -c 'select(.type == "tool.completed")'Every line on stdout is one complete JSON object. Events include a UUID eventId, monotonically increasing sequence, ISO timestamp, runId, and type-specific fields. The stream covers run/step starts, message and reasoning deltas, live context.updated gauges, plan updates, tool and permission activity, child-agent lifecycle, failures, and the terminal run.completed event with status and usage.
This mode is separate from ACP: run --json emits Zero CLI runtime events, while acp speaks ACP JSON-RPC/NDJSON and must be connected to an ACP client.
By default Zero CLI derives a private state root from the canonical workspace path:
~/.local/state/zero-cli/<workspace-hash>/
zero-cli.db
artifacts/
The directory and artifact files use user-only permissions. SQLite runs in WAL
mode and stores runtime events with a database-owned order that remains stable
across process restarts, session metadata, materialized parent-linked agent-run
records, and bounded, versioned checkpoints. TUI checkpoints retain the
visible timeline and exact root-session model history; child checkpoints retain
binary-safe model history together with lifecycle, task, workspace, permission,
budget, and result metadata. Shared-workspace children can therefore be
inspected and continued by run ID after restart. Agents cannot browse this
state directory through workspace paths. They can reopen only a
content-addressed artifact whose SHA-256 reference is present in their context,
using artifact://<sha256>; session database contents remain outside
model-readable roots.
Persistence can be disabled in configuration:
{
"storage": {
"enabled": false,
"path": "zero-cli.db"
}
}Only the basename of storage.path is used beneath the private state root; project configuration cannot redirect internal writes elsewhere. Set ZERO_CLI_STATE_DIR=/trusted/absolute/directory to choose a different user-owned state base explicitly.
Run /sessions in the TUI to search, resume, rename, pin, or delete TUI sessions
across saved projects. Discovery reads the bounded per-workspace catalog without
migrating unrelated databases, and mutations are routed back to the database
that owns each session. Choosing a session from another project reopens that
project before restoration, so its configuration and workspace boundaries are
active first. TUI snapshots are adapter-owned and validated before restoration.
Older event-only rows replay their validated visible timeline with an explicit
warning, but do not pretend that presentation events reconstruct exact model
context. Headless and ACP sessions are tagged separately and do not appear in
the TUI manager. Use zero-cli sessions to list CLI session IDs and
zero-cli run --resume <id|latest> <task> to restore a validated checkpoint into
a fresh campaign; the canonical workspace, surface, saved mode, and model
identity must still match. To inspect process-interrupted root campaigns, use
zero-cli sessions --interrupted. An eligible agent-mode shared-workspace root can be
continued explicitly with zero-cli run --resume-run <run-id|latest> <task>,
preserving its run ID, authority, history, spent budget, aggregate usage, and
absolute deadline. Zero CLI never auto-resumes, and it rejects exact recovery for
process-local worktree/scratch state, workflow coordinator state, or
incomplete/drifted policy state.
# Strict TypeScript check
bun run check
# Bun test suite
bun run test
# Bundle the Bun-targeted CLI into dist/index.js
bun run build
# Smoke-test the bundle through its hardened executable launcher
./dist/index.js --helpThe tests cover trusted configuration merging, skill discovery/progressive disclosure and read-only package boundaries, native provider registration, provider model-catalog validation, private atomic credential storage and symlink rejection, structured Codex account/driver boundaries, safe launcher behavior in hostile working directories, AI SDK streaming/tool handoff, runtime tool loops and timeout behavior, context compaction and campaign budgets, deterministic model assignment and retries, task-aware work-depth routing, dependency-aware workflows, delegation, multimodal routing, denied writes, workspace/symlink and secret boundaries, process-group cancellation, terminal-control sanitization, core and MCP content tools, the responsive TUI start/connect/skills/session screens, session migrations and binary-safe TUI/headless/ACP resume checkpoints, input/focus/navigation behavior, event reduction, and ACP initialization, lifecycle, configuration, workflow, and prompt exchanges that verify session-workspace isolation.
bun run build applies OpenTUI's Solid transform and creates a Bun-targeted
JavaScript bundle with package dependencies kept external. A normal package
installation installs those runtime dependencies alongside the CLI; a source
checkout must run bun install first. The build is not a standalone
cross-platform executable. Invoke src/index.ts or dist/index.js directly so
their hardened shebang disables cwd .env and bunfig.toml; prefixing them
with bun bypasses that launcher boundary. bun run pack:smoke builds, packs,
installs, and launches the actual tarball in a clean temporary directory.
- File-oriented tools canonicalize paths and resolve symlinks before use. Traversal and symlink escapes outside the allowed workspace roots are rejected, including paths for files that do not exist yet.
- Project-owned provider, permission, prompt-file, skill, MCP, and plugin configuration—and
.agents/skillsdiscovery—is ignored until the canonical workspace is explicitly trusted. - Skill entry points are bounded, regular
SKILL.mdfiles with validated frontmatter. Symlinked entry points, unsafe control characters, ambiguous YAML, oversized packages, and aggregate catalog overflow are not loaded; an invalid explicit entry fails configuration while an invalid auto-discovered package is skipped with a sanitized diagnostic. Validated external package directories are added only toreadroots. - The direct executable launcher disables Bun's automatic cwd
.envandbunfig.tomlloading before Zero CLI starts, so an untrusted checkout cannot inject environment overrides or preload code ahead of the trust check. Do not prefix that launcher withbun, which would start Bun before its shebang flags apply. - Internal SQLite/artifact state is kept outside workspace roots with private filesystem permissions. The
readtool exposes only explicitly referenced, content-verifiedartifact://<sha256>objects, not directory browsing or SQLite access. writecanonicalizes explicit paths and validates all recognizable diff paths before checking or applying a unified patch.- Agents can call only tools in their own allowlist, and delegation targets must be explicitly named in
canDelegateTo. - Permission policy is enforced in code. Child runs inherit a ceiling and cannot broaden parent permissions.
- Processes receive cancellation and timeouts, are terminated as a process group with SIGTERM/SIGKILL on POSIX, and have bounded captured output.
runreceives a reduced environment. Provider keys remain available to Zero CLI itself but are not automatically copied into tool subprocesses.- Text returned by local, URL, and MCP
read, MCP tool results,find, andruncrosses a conservative high-confidence secret-redaction boundary before it reaches the model, progress UI, Git-diff evidence, or a text artifact. It recognizes common provider tokens, private-key blocks, authorization headers, credential-bearing URLs, and secret assignments while preserving lines and reporting redaction metadata.artifact://text reads are scrubbed again. - URL
readrejects credential-bearing URLs, localhost,.local, and private/link-local/reserved literal or DNS-resolved targets. Every redirect is revalidated and each connection is pinned to an approved address before its bounded body is read. Websearchsends only the model's query and configured locale controls to Brave after network approval. /connectAPI keys are stored outside workspaces with user-only permissions, no-follow path checks, and redacted connection summaries. Subscription OAuth stays in dedicated Codex and Kimi CLI homes; their child processes receive filtered environments without unrelated API keys or tokens. Kimi additionally runs from a synthetic home and non-project workspace with official endpoints pinned, an empty executable search path, a validated native-tool deny policy, no injected instruction/MCP/plugin state, and per-step transport-session cleanup.- MCP OAuth tokens and dynamic client registrations use a separate owner-only atomic store; authorization callbacks bind only to loopback, validate exact state, and time out. MCP tool annotations are treated as untrusted hints and cannot lower Zero CLI's network/execute permission checks.
- Attachments are restricted to allowed roots and bounded by size. ACP base64 content is validated and bounded before decoding.
- Human-facing headless output strips terminal control sequences, including ANSI/OSC clipboard operations; JSON and ACP protocol streams remain structured and unmodified.
- Model output, attachment text, repository contents, tool output, and plugin data should all be treated as untrusted input.
These controls are not an operating-system sandbox. In particular, an approved run call launches the user's shell. Although its starting directory and environment are constrained, the command can access anything permitted to the Zero CLI process and can use the network. Run highly autonomous or untrusted tasks inside a container, VM, disposable account, or isolated worktree with appropriate OS-level controls.
- The repository now has a public package definition, clean-install smoke test, completion generator, and tag-driven npm/GitHub release workflow, but a maintainer still has to create the first trusted-publishing release. There is no standalone native binary or updater yet.
- MCP tools, resources, and prompts are supported. MCP sampling, elicitation, and task-based tool execution are not implemented.
- Workflow dependencies, concurrent read-only phases, retries, deterministic handoffs, and shared campaign budgets are implemented. Isolated writer worktrees return patch artifacts, but Zero CLI deliberately does not silently adopt patches or attempt semantic conflict resolution.
- ACP has durable list/load/resume/close lifecycle plus agent, model, and
workflow selection, but the agent transport itself is stdio-only. A listing
without an explicit
cwdsees only the ACP server process workspace because state stores are workspace-sharded. Restored binary attachments are replayed to clients as named placeholders rather than retransmitted binary payloads. - TUI, headless, and ACP sessions have validated durable checkpoints. Older event-only TUI rows can restore their presentation timeline, but cannot recreate exact model context that was never checkpointed.
- Context compaction, aggregate campaign token limits, and cost accounting for configured model prices are implemented. A model without configured pricing can be counted as unpriced, but cannot be used when a campaign cost cap is enabled. Request admission is race-safe; providers whose output ceiling is advisory can still exceed the configured boundary on their final request.
- Secret-like paths remain heuristic. Model-visible output from local/URL/MCP
read, MCP tools,find, andrunreceives conservative high-confidence redaction, but this is not comprehensive DLP and cannot prove ordinary source code is credential-free. - The shell is not sandboxed and network access is not technically blocked.
- Descendant process-group termination is implemented on POSIX; Windows needs a stronger job-object/container boundary for equivalent guarantees.
- TypeScript plugins are trusted and unsandboxed, and there is no stable external plugin package/API promise yet.
/connectstores one API key and selected model per direct provider. Its portable file store relies on filesystem permissions rather than an OS keychain; use environment variables or an encrypted home directory when that boundary is insufficient.- The TUI is a timeline/composer with an attachment picker and syntax-highlighted inline diffs, not a full IDE. It has no full file editor or interactive controls for adopting runtime-managed Git worktrees.
- Unified-diff
writeoperations require the external Git executable; direct create/replace/move/delete operations do not. - There are intentionally no built-in LSP, browser automation, Git-specific, package-manager, or user-question tools beyond the six capability tools. The optional
todoprimitive is only a bounded, run-private progress ledger.
Zero CLI is available under the MIT License.
{ "context": { "fallbackWindowTokens": 128000, "compaction": { "enabled": true, "triggerRatio": 0.8, "targetRatio": 0.45, "recentFraction": 0.15, "minimumRecentTurns": 2, "keepRecentTurns": 6, "maxSummaryTokens": 4096, "reservedOutputTokens": 8192, "safetyBufferTokens": 4096, "rebaseAfter": 4, "rebaseAfterValidationFailures": 2, "pruneToolOutputs": true } }, "limits": { "maxCampaignTokens": 4000000, "maxCostUsd": 25 }, "models": { "coder": { "provider": "openai", "model": "your-model", "contextWindow": 200000, "pricing": { "inputPerMillionUsd": 2, "outputPerMillionUsd": 8, "cachedInputPerMillionUsd": 0.5 } } } }