ringzero
v0.6.0
Published
Minimal token-efficient agent harness (zero-dep kernel, Ink-based TUI)
Maintainers
Readme
RingZero
Minimal, token-efficient agent harness. The kernel is zero-dependency (agent loop, providers, tools, sessions all run on Node builtins); the TUI is built on Ink (React — the same engine as Claude Code / Gemini CLI / Copilot CLI). Aggressive token-saving: minimal system prompt, cache-aware ordering, compaction, output truncation, streaming, progressive disclosure (skills), and ephemeral sub-agents.
Features
- Agent loop — stream → tool calls → permission gate → execute → repeat (maxSteps)
- Reasoning —
thinkingevents from DeepSeek/OpenAI-compatreasoning_contentand Anthropic extended thinking, shown collapsed in the TUI, never persisted. - Providers — OpenAI-compatible (packyapi / Ollama / LM Studio / OpenRouter /
MiniMax…), Anthropic Messages (with
cache_control), and Gemini (streamGenerateContent, incl. vision viainline_data). Chosen from env. - CJK-aware tokenizer — ASCII ≈ 4 chars/token, CJK ≈ 1 char/token, used for
compaction thresholds & budgeting; provider
usageis the source of truth. - Compaction 2.0 — auto-summarizes old messages into a structured brief
(goals / decisions / files / errors / unfinished), keeps the tail verbatim
(
RINGZERO_PRESERVE_RECENT), folds prior summaries forward, and re-compacts incrementally until the budget fits. - Mid-run injection — while the agent is running, just type and press Enter:
the current stream aborts, your message is queued, and the run continues with
it (TUI, REPL, and RPC
prompt {interrupt:true}). - Tools — read (full / range / auto-outline for large files) / write / edit,
list_dir/tree/ grep (incl.files_only) / glob,related_files(importers + same-symbol files), bash,web_fetch,web_search(opt-in),http_request(SSRF-guarded),git_status/git_diff/git_log/git_commit,plan,todo,task(sub-agent),ask_user, MCP. - Tool toggles —
/tools(TUI menu) //tools [name|reset](REPL) lets you hide tools from the agent;disabledToolsand/permissionoverrides persist toconfig.json(~/.ringzero/config.jsonglobal, merged with.ringzero/config.jsonper project). - Plan mode —
/plangates the agent: only read-only tools run until it presents a plan via theplantool and you approve it; approved plans run without further permission prompts. - Todos — agent-maintained checklist (
todotool), persisted per session, shown as a collapsible strip in the TUI (Ctrl+T,/todos). - Security — secret values redacted from tool output/logs,
web_fetchblocks private/loopback addresses (SSRF guard), bash children get a sanitized env and a capped timeout. - Tool efficiency — per-run result cache for pure tools (deduped identical reads), capped parallel tool execution, tool definitions ordered by usage to stabilize the provider prompt cache.
- Checkpoints — auto-snapshot of the worktree before each run;
/checkpoint/rollbackrestore it (index + worktree, HEAD untouched).
- Verify loop — after the automatic post-edit check (
RINGZERO_VERIFY), the agent also gets averifytool to re-run build/tests after each fix (capped at 3 calls per run), with failing exit codes fed back. - Sub-agent —
tasktool spawns ephemeral Agents (same model as the main loop — no multi-model routing); only their summaries enter context. Batch mode: passtasks: [...]to fan out N independent subtasks in parallel (capped at 4), merged into one numbered report; a failing task is isolated. - MCP — stdio and streamable-HTTP transports; config via
RINGZERO_MCPenv or.ringzero/mcp.json. - Skills — on-demand SKILL.md injection appended after the stable system prefix (doesn't bust prompt cache).
- Sessions — append-only JSONL under
~/.ringzero/sessions/, resumable via--resume/--continue; auto titles./exportwrites a Markdown transcript; excess/old sessions auto-archive (RINGZERO_SESSION_LIMIT,RINGZERO_SESSION_KEEP_DAYS). - Plugins — single-file ESM plugins add tools, slash commands, and tool hooks.
- RPC/SDK —
--rpcJSON-RPC over stdin/stdout for embedding, with streamedprompt/eventnotifications and mid-runinterrupt. - Notifications — terminal bell + desktop bubble when a long run finishes or a permission prompt waits (
RINGZERO_NOTIFY,RINGZERO_NOTIFY_MIN). - Watch mode —
--watch "prompt"re-runs the prompt whenever the project changes (auto-fix loops). - Permission gate — allow / ask / deny per tool, per-session overrides.
- Yolo mode —
/yolo(TUI/REPL),YOLO=1env, or--yoloflag: auto-allow every tool with zero prompts (overrides plan mode anddenyrules; the step-cap continuation also auto-runs, capped at 3 per turn). Persists toconfig.jsonlike/permissionoverrides. - Token/cost dashboard — per-turn + session input/output/cache breakdown
with cache hit rate and an estimated cost from a built-in zero-dep price
table (right sidebar + StatusBar,
/usage, per-turn status); tunesrc/kernel/cost.ts. - Cost/token caps —
RINGZERO_COST_CAP(USD) andRINGZERO_TOKEN_CAPabort a run with a clear status when hit (checked per turn against cumulative usage) and warn once at 80%. - Symbol index +
related_files— zero-dep ctags-style index (src/tools/indexer.ts, cached with mtime invalidation);related_filesfinds importers and files defining the same symbols before you edit. - Vision — attach images to any turn:
--image <path>(CLI),/image(TUI/REPL), orprompt {images: [...]}(RPC). Images are one-shot — sent once, never persisted to the session store. - Benchmark —
npm run benchmeasures tokens per task, compaction savings, and sub-agent savings against recorded offline fixtures.
Install / build
Quick install from npm (published package — works on any platform with Node ≥ 20.3, including macOS x86):
npm i -g ringzero
ringzeroUpgrade: npm i -g ringzero@latest (npm) · ringzero --update
(exe / portable-zip installs) · or re-run the one-liner.
Build from source:
npm install
npm run build # tsc → dist/
npm test # build + node --testBun is the recommended dev toolchain (faster tests + the cross-platform shell + single-file binaries). Install Bun, then:
bun install # bun.lock
bun test # runs the TS tests directly (no build step)
bun run scripts/build-bun.mjs # single-file binary → build/bun/Install without npm
Windows: one-line installer (PowerShell)
irm https://ringzero.abby.md/install.ps1 | iex
ringzeroDownloads the latest single-file ringzero-win-x64.exe (a Bun-compiled
standalone binary — no Node, no unpacking) from GitHub Releases into
%LOCALAPPDATA%\Programs\RingZero, adds it to your user PATH, and runs it
once so ringzero works immediately in that shell and in new ones. The
script is reviewable in the repo: install.ps1.
Windows: winget
Once the package is accepted into the community repo, Windows users install with:
winget install --id Abbychau.RingZero -eThis installs the single-file ringzero.exe (a Bun-compiled standalone
binary) as a portable app and puts the ringzero command on your PATH.
Status: submitted to microsoft/winget-pkgs (PR #413332); once merged, every tagged release updates the package automatically (needs a
WINGET_TOKENPAT secret, see.github/workflows/winget.yml).
Single-file binary (all platforms)
No Node, no npm needed. Download the standalone binary for your OS from the
latest release:
ringzero-win-x64.exe, ringzero-darwin-arm64, or ringzero-linux-x64.
Make it executable and run:
chmod +x ringzero-darwin-arm64 && ./ringzero-darwin-arm64 --versionThe binary is compiled with Bun (npm run build:bun — the Bun runtime and
the whole app are embedded), so it runs anywhere with no dependencies.
Portable zip (all platforms)
Prefer a folder you can inspect? The zip bundles the app + a Node runtime
with ringzero / ringzero.cmd launchers:
unzip ringzero-linux-x64.zip # or ringzero-darwin-arm64.zip
./ringzero/ringzero --versionBoth are produced from tagged releases (v* tags) by the Portable builds
GitHub Actions workflow. To build locally:
npm run build && npm run build:portable # → build/portable/ringzero-<platform>-<arch>.zip
bun run scripts/build-bun.mjs # → build/bun/ringzero-<platform>-<arch>[.exe]Only the build machine needs Node ≥ 20 and npm (and Bun for the binary); end users need neither.
Update
Already installed? Get the latest release with the built-in self-updater:
ringzero --update # checks the latest GitHub release and replaces
# the installed binary in placeIt detects how you installed (Windows SFX exe or the macOS/Linux portable dir), downloads the matching asset, and swaps it in — no admin needed. If it can't self-update (dev checkout, ad-hoc copy), it prints the one-line installer for your OS instead.
You can also just re-run the installer — it always fetches the latest release:
# Windows
irm https://ringzero.abby.md/install.ps1 | iex# macOS / Linux
curl -fsSL https://ringzero.abby.md/install.sh | shFor a dev checkout, pull and rebuild instead:
git pull && npm install && npm run build.
Uninstall
One-command uninstallers remove the installed binary / app dir and the PATH
entry the installer added — your data (~/.ringzero — sessions, config,
skills) is always kept:
# Windows
irm https://ringzero.abby.md/uninstall.ps1 | iex# macOS / Linux
curl -fsSL https://ringzero.abby.md/uninstall.sh | shInstalled via npm instead? npm uninstall -g ringzero. Via winget?
winget uninstall --id Abbychau.RingZero -e.
To also delete your sessions, config, and skills:
rm -rf ~/.ringzero # macOS / Linux
Remove-Item -Recurse -Force "$env:USERPROFILE\.ringzero" # WindowsHow to run & test
# 1. Build (required before running)
npm run build
# 2. Run tests (offline — no API calls; includes CJK tokenizer, SSE, compaction,
# permission, glob/fsutil, MCP client + real spawned stdio server)
npm test
# 3. Smoke test — real round-trip vs the endpoint in .env (needs network + API key)
npm run smoke
# 4. Run the CLI. Either use node directly:
node dist/src/cli/index.js "列出 package.json 的 name"
# …or link it once so `ringzero` works anywhere:
npm link # then: ringzero "prompt"
# Useful invocations
ringzero # interactive TUI (fallback: --repl line mode)
ringzero "prompt" # one-shot
ringzero --json "prompt" # NDJSON event stream (scriptable)
ringzero --watch "prompt" # re-run the prompt on file changes (--yes for writes)
ringzero --image shot.png "…" # attach an image to the prompt (repeatable)
ringzero --sessions # list saved sessions (then --resume <id>)
ringzero --resume <id> "prompt" # continue a session
ringzero --version
ringzero --doctor # environment self-check (exit 1 on problems)Sessions are stored as JSONL under ~/.ringzero/sessions/ (or RINGZERO_HOME).
Usage
# ~/.ringzero/.env (or real env vars; --env <path> loads an explicit .env file)
# Working-directory .env files are NOT loaded automatically — use --env .env
# to opt in, so a foreign project's .env can't hijack your config.
# API_URL=https://www.packyapi.ai/v1
# API_KEY=sk-...
# MODEL=deepseek-v4-flash
ringzero # interactive TUI (line REPL if no TTY, or --repl)
ringzero "prompt" # one-shot
ringzero --json "prompt" # NDJSON event stream (scriptable)
ringzero --resume <id> "..." # continue a session
ringzero --continue "..." # resume the most recent session
ringzero --rpc # JSON-RPC mode over stdin/stdout
ringzero --yes "prompt" # auto-allow all tools (scripted)
ringzero --model <id> "..." # override model
ringzero --verbose "..." # verbose logging
ringzero --image shot.png "..." # attach an image (vision models)
ringzero --watch "..." # re-run on file changes (use --yes for writes)
ringzero --doctor # environment self-check (exit 1 on problems)TUI keys
The layout is opencode-style: conversation on the left, a one-column gap, and
a right sidebar (terminals ≥ 90 columns) holding the header
(RingZero · <working-dir name>), model, session id, mode badges
([plan], [yolo], [img]), a live context-budget bar, token/cost totals,
and the status line. The sidebar has rounded corners and its text is
selectable. On narrower terminals the sidebar hides and the header/status bar
return to full width.
Enter submit · ↑/↓ input history · PgUp/PgDn or mouse wheel scroll ·
Ctrl+P/L model dialog / cycle favorites (RINGZERO_MODELS) · Ctrl+K command palette ·
Ctrl+O or mouse click expand/collapse tool output · Ctrl+T toggle the todo list ·
Ctrl+A/E line start/end · Ctrl+←/→ jump by word ·
Ctrl+U clear line · Ctrl+W delete word · Ctrl+C abort run / exit.
Drag with the mouse to select text (or Shift+↑/↓, Shift+PgUp/PgDn after
clicking the transcript) and press Ctrl+C or Ctrl+Y to copy the selection
to the system clipboard (with a selection active, Ctrl+C copies instead of
aborting/exiting); Esc clears the selection. Selection works in both the
transcript and the sidebar. Mouse-driven selection is CJK-safe: a click on a
wide character selects the whole character.
While the agent is running, typing + Enter injects your message mid-run
(the current stream aborts and the run continues with your input).
Permission prompts appear as an inline modal: y yes · n no · a always · v never.
Paste (incl. CJK) is bracketed-paste safe; IME composition works.
Slash commands (REPL & TUI)
/help /usage /context /model [id] /effort [level] /retry /compact /copy [n|all] /permission <tool> <allow|ask|deny> /yolo [on|off] /skills [name] /sessions /resume <id> /diff /status /commit <msg> /checkpoint /rollback /plan [on|off] /todos /tools /image <path> /export [path] /new /exit
/image <path> attaches an image to your next message (shown as [img] in the
sidebar); /image clear removes it. /export [path] writes the current session
as a Markdown transcript (default: transcript-<id>.md in the cwd).
/copy [n|all] copies the last assistant message (or the last n, or the full
transcript) to the OS clipboard — zero-dep: clip / pbcopy / xclip /
wl-copy / xsel — handy when terminal selection is unavailable (mouse mode +
alternate screen).
/effort [low|medium|high|max] sets the reasoning effort (persisted to
config.json, applied to the next run); with no argument it opens a picker
and always reports the current value. /retry re-runs the last submitted
prompt as a new turn in the same session.
/tools opens a toggle menu (TUI) — Enter flips a tool on/off, Esc closes — or
in the REPL lists every tool ([on]/[off]), toggles one by name, and reset
re-enables all. Disabled tools are hidden from the agent until re-enabled.
/usage shows the session token totals with cache hit rate and estimated cost
(per-turn breakdown too); the StatusBar keeps a live cost estimate for the
session, and each finished turn reports its own usage.
Yolo mode
/yolo (or YOLO=1 in .env, or ringzero --yolo): every permission
check auto-allows — no modals, no [denied] events, deny rules and plan
mode are overridden. Useful for unattended/trusted tasks. The status bar
shows a red YOLO badge while it's on; /yolo off restores prompts. The
toggle persists to config.json (precedence: CLI --yolo > YOLO env >
persisted toggle). When the step cap is hit, yolo auto-continues up to 3
times per user turn instead of prompting.
Plan mode
/plan (or RINGZERO_PLAN_MODE=1) puts the agent in plan mode: only read-only
tools (read_file, grep, glob, git_status, git_diff, web_fetch) are
allowed until the agent presents a plan with the plan tool and you approve it.
Once approved, the rest of the turn runs without further permission prompts.
Rejected plans keep the gate closed — the agent must revise and re-present.
Persistent config
/permission overrides (and always/never answers) plus /tools toggles
persist to ~/.ringzero/config.json (global). A project-level
.ringzero/config.json merges on top and wins per key — handy for
repo-specific defaults (.ringzero/ is gitignored by default). Disabled tools
are a union across both files: a repo can disable more, but only the global
file can re-enable. The config file is JSON with disabledTools (array),
permissionOverrides (tool → allow|ask|deny), and yolo (boolean,
project file wins).
Checkpoints & rollback
At the start of each run, RingZero snapshots the worktree (tracked + untracked
files) into a shadow git ref — nothing is committed to your branch. While the
agent works you can inspect the damage with /diff or /status, or undo it:
/checkpoint— take an explicit snapshot (returns the sha)/rollback— restore the most recent snapshot (index + worktree; HEAD untouched)
The git_status / git_diff tools let the agent see the same view and adapt
(e.g. noticing it left stray files).
MCP config
.ringzero/mcp.json (repo or home) or RINGZERO_MCP env (JSON):
{
"my-server": { "command": "npx", "args": ["-y", "some-mcp-server"] },
"http-server": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer x" } }
}Skills
Create <cwd>/.ringzero/skills/<name>/SKILL.md (or ~/.ringzero/skills/), then
/skills <name> to enable. A skill may also ship tools.mjs (default export =
array of Tool objects) that gets registered when the skill is enabled.
See examples/skills/.
Plugins
Drop a single-file ESM/CJS plugin into <cwd>/.ringzero/plugins/ or
~/.ringzero/plugins/ (file name = plugin name). The default export is
async (api) => {} where api provides:
registerTool(tool)— add a custom toolregisterCommand(name, fn)— add a/nameslash commandonToolBefore(fn)— deny or rewrite tool calls before executiononToolAfter(fn)— inspect or rewrite tool results before they reach the modelsay(text)— push a line into the active UI
See examples/plugins/hello.mjs.
RPC mode
ringzero --rpc speaks JSON-RPC 2.0 over stdin/stdout (one object per line):
initialize, ping, model/get, model/set, sessions/list, sessions/resume,
sessions/export, prompt. prompt accepts notify: true (streams
prompt/event notifications for every agent event) and
interrupt: true (injects a message into the running prompt, bypassing the
serial queue). prompt also accepts images: [{ mime, data }] for vision.
echo '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | ringzero --rpc
echo '{"jsonrpc":"2.0","id":2,"method":"prompt","params":{"text":"列出 cwd"}}' | ringzero --rpcDevelopment
- CONTRIBUTING.md — setup, conventions, testing, commit style.
- docs/EXTENDING.md — how to add providers, tools, plugins, skills, slash commands, and use the RPC/SDK.
Env knobs
| Var | Default | Meaning |
| --------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API_URL / API_KEY / MODEL | — | OpenAI-compatible endpoint |
| ANTHROPIC_API_KEY / ANTHROPIC_MODEL | — | used when API_URL is empty |
| GEMINI_API_KEY / GEMINI_MODEL | — | used when API_URL is empty (after Anthropic); MODEL wins over GEMINI_MODEL |
| EFFORT | — | short alias for RINGZERO_EFFORT (handy in .env): low / medium / high |
| RINGZERO_EFFORT | — | reasoning effort. OpenAI-compat sends reasoning_effort; Anthropic/Gemini enable thinking with a budget (low 2048 / medium 8192 / high 16384 tokens) |
| YOLO | 0 | yolo mode: auto-allow every tool, no permission prompts (1/true/yes/on). CLI --yolo wins, then this, then the persisted /yolo toggle |
| RINGZERO_YOLO | 0 | long alias for YOLO |
| CONTEXT_BUDGET | — | short alias for RINGZERO_CONTEXT_BUDGET (handy in .env) |
| RINGZERO_CONTEXT_BUDGET | 32000 | compaction trigger (estimated tokens) |
| RINGZERO_PRESERVE_RECENT | 8000 | tail tokens kept verbatim on compaction |
| MAX_STEPS | 24 | agent loop step cap (-1 = unlimited) |
| RINGZERO_MAX_STEPS | 24 | long alias for MAX_STEPS |
| RINGZERO_COST_CAP | — | hard per-run cost cap in USD (fractional allowed, e.g. 0.5); the run aborts at the cap and warns at 80% |
| RINGZERO_TOKEN_CAP | — | hard per-run cumulative-token cap (input + output + cache); aborts at the cap, warns at 80% |
| RINGZERO_MODELS | — | comma-separated favorite models for Ctrl+L cycling |
| RINGZERO_RETRIES | 2 | transient-failure retries (429/5xx/network) |
| RINGZERO_HOME | ~/.ringzero | data dir (skills, plugins) |
| RINGZERO_SESSIONS | <home>/sessions | session store dir |
| RINGZERO_WORKSPACE | git root | lock fs tools (read/write/edit/grep/glob) to this root; paths outside are rejected. Unset = auto-detect the git work-tree root; off/none disables the sandbox |
| RINGZERO_VERIFY | — | shell command run after the first write/edit of a run; output fed back to the model (e.g. npm test) |
| RINGZERO_PLAN_MODE | 0 | start with plan mode on (1/true) |
| RINGZERO_ALLOW_PRIVATE_NET | 0 | 1 disables the web_fetch SSRF guard (not recommended) |
| RINGZERO_BASH_FULL_ENV | 0 | 1 passes the full environment to bash children (secrets are stripped by default) |
| RINGZERO_NOTIFY | 1 (TTY only) | 0 disables bell/desktop notifications |
| RINGZERO_NOTIFY_MIN | 30 | minimum run length (seconds) before a completion notification fires |
| RINGZERO_SESSION_LIMIT | 50 | max sessions kept; older ones archive to <sessions>/archive |
| RINGZERO_SESSION_KEEP_DAYS | 0 | archive sessions older than N days (0 = off) |
| RINGZERO_SEARCH_KEY | — | API key enabling the web_search tool (Tavily-compatible) |
| RINGZERO_SEARCH_ENDPOINT | Tavily API | search endpoint for web_search (POST {api_key, query, max_results}) |
| RINGZERO_OS_ENCODING | system locale | force the legacy console codepage used to decode cmd/PowerShell output (e.g. gbk, big5, shift-jis) |
Workspace sandbox
Set RINGZERO_WORKSPACE=/path/to/project to restrict the file tools
(read_file, write_file, edit_file, grep, glob) to that directory —
attempts to touch anything outside it are rejected instead of executed. When
unset, the sandbox is auto-detected as the git work-tree root (so a prompt
never writes outside the project by accident); set it to off (or none) to
disable the sandbox entirely. ringzero --doctor shows which root is active.
Layout
src/
kernel/ types, tokenizer, agent loop, context/compaction, truncate, redact
providers/ provider interface, openai-compat, anthropic, gemini, SSE, retry, registry
tools/ fs, search (grep/glob), indexer + related_files, bash, web, plan, todo, task (sub-agent), verify
mcp/ client, stdio+http transports, config, tool bridge
session/ JSONL store, markdown export
permission/ gate
skills/ loader
cli/ index (args), repl, one-shot/json, rpc, watch, notify, runner
config/ env + app config