# supercode
A lightweight, fully-customizable AI **coding-agent SDK** in Rust.
`supercode` is a native agent loop — it talks directly to any model through
[OpenRouter](https://openrouter.ai) (or any other OpenAI-compatible endpoint),
drives a configurable set of tools, and is built to be a **superset** of what
tools like Claude Code and Codex do. Three goals shape it:
1. **Highly customizable** — every prompt, every tool description, and every
tool's on/off state is yours to set.
2. **Light and fast** — pure Rust, a thin async core, streaming by default. The
agent loop runs in **~13–19 MB** of memory — measured **~7× lighter than
Codex and ~27× lighter than Claude Code** on identical tasks (see
[Benchmarks & footprint](#benchmarks--footprint)).
3. **A superset of existing agents** — it natively **loads and continues real
Claude Code and Codex sessions** from disk, so you can pick up any prior
session against any model.
> Status: early but real. The core, the OpenRouter/OpenAI provider, the
> built-in tools, the agent loop, and the session loaders are implemented and
> tested. See [Status](#status).
## Benchmarks & footprint
"Light and fast" is measured, not asserted. [`bench/`](bench/) is a task harness
that runs the agent against a live model **and profiles the harness's own
footprint** (peak RSS, CPU, startup, binary size) — because low memory/CPU is the
whole point.
**Footprint vs the incumbents.** Same model (DeepSeek V4 Flash via OpenRouter),
same tasks, same verifier, same per-process sampler — only the harness changes.
Harness **own-process** resident memory:
| **supercode** | **~13–19 MB** | 1× | single 5.5 MB Rust binary |
| codex | ~89 MB | ~7× | Rust core (+ Node launcher) |
| claude code | ~346 MB | ~27× | Node / TypeScript |
All three solved the same tasks; supercode also used the least CPU. The numbers
above are each harness's *own* process — the clean, conservative metric. Codex
and Claude Code additionally launch heavyweight MCP/helper processes (hundreds of
MB) that supercode does not. Full methodology, per-task data, and the honest
tree-sum caveats are in
[`bench/suites/harness-comparison.md`](bench/suites/harness-comparison.md).
**Capability.** On a real subset of the [Aider polyglot
benchmark](https://github.com/Aider-AI/polyglot-benchmark) (12 Exercism exercises
across Python/Rust/Go, real test suites), supercode driving DeepSeek V4 Flash
scores **12/12**, with a profile block in every scorecard
([`bench/suites/polyglot-scorecard.json`](bench/suites/polyglot-scorecard.json)).
It also speaks the [SWE-bench](https://www.swebench.com) instance format
(loader + fixtures; Docker is the citable grading lane — see
[`bench/suites/swebench/`](bench/suites/swebench/)).
```sh
export OPENROUTER_API_KEY=sk-or-...
cargo run -p supercode-bench -- --tasks bench/suites/polyglot --sandbox danger-full-access
python3 bench/tools/compare_harnesses.py # supercode vs claude vs codex
```
## Why OpenRouter
OpenRouter exposes a single OpenAI-compatible API in front of essentially every
frontier model. `supercode` therefore ships **one** provider implementation
([`OpenAiProvider`]) that reaches Claude, GPT, Gemini, Llama, and anything else
OpenRouter routes to. Point `base_url` at any other OpenAI-compatible endpoint
(a local server, a gateway, a mock) and it just works.
## Install
```sh
Or pick your channel:
| Install script | `curl -fsSL .../scripts/install.sh \| sh` (prebuilt binary, checksum-verified) |
| cargo | `cargo install supercode-cli` |
| cargo-binstall | `cargo binstall supercode-cli` (prebuilt, no compile) |
| Homebrew | `brew install volter-ai/tap/supercode` — once the tap is published¹ |
| from source | `git clone … && cargo install --path crates/cli` |
> Prebuilt channels resolve to GitHub Releases; until the first public release is
> cut they fall back to `cargo install`. Requires a Rust toolchain (1.85+) only
> when building from source.
>
> ¹ Each release renders and publishes `supercode.rb` (real checksums) as a
> release asset; the `volter-ai/homebrew-tap` repo is populated automatically
> when a `HOMEBREW_TAP_TOKEN` is configured. Until then, use the install script
> or `cargo install`.
### First run
```sh
supercode login # paste your OpenRouter key — saved to ~/.config/supercode
supercode doctor # ✓ config, key, and live provider reachability
supercode run "hello"
```
No key yet? Any one of these works: `supercode login`, `export OPENROUTER_API_KEY=…`,
or `--api-key`. Shell completions: `supercode completions zsh` (also bash/fish/powershell).
## CLI
```sh
# One-shot
supercode run "List the Rust files and summarize the module layout."
# Interactive
supercode chat
# Inspect a real Claude Code or Codex session — no API key needed
supercode inspect ~/.codex/sessions/2026/05/04/rollout-*.jsonl
# Load a prior session and CONTINUE it against your chosen model
supercode --model openai/gpt-5 resume ~/.claude/projects/<proj>/<id>.jsonl \
"Pick up where we left off and finish the refactor."
# Convert a session between formats (GIMP-style "export as") — no API key needed
supercode convert ~/.codex/sessions/.../rollout-*.jsonl --to claude-code -o out.jsonl
# Audit a corpus: typed-parse every log and report what we model/drop/miss
supercode audit ~/.codex/sessions --format codex
supercode audit ~/.claude/projects --format claude-code
```
Global flags: `--model`, `--base-url`, `--cwd`.
## Library
```rust
use supercode::{Agent, Config};
# async fn run() -> supercode::Result<()> {
let config = Config::builder()
.model("anthropic/claude-opus-4-8")
.system_prompt("You are a terse, expert pair programmer.")
.disable_tool("bash") // toggle tools off…
.tool_description("search", "Grep the repo.") // …or re-describe them
.build();
let mut agent = Agent::new(config)?;
let reply = agent.send("Find every TODO and group them by file.").await?;
println!("{reply}");
# Ok(())
# }
```
### Continue a Claude Code or Codex session
```rust
use supercode::{Agent, Config};
use supercode::session::Session;
# async fn run() -> supercode::Result<()> {
let session = Session::load("~/.codex/sessions/.../rollout-….jsonl")?;
let mut agent = Agent::resume(Config::builder().build(), session)?;
let reply = agent.send("Continue from here.").await?;
# let _ = reply;
# Ok(())
# }
```
## Typed schema & coverage audit
Both formats are modeled as **typed Rust schemas**
([`src/schema/`](crates/core/src/schema)) with two deliberate escape hatches:
every discriminated enum has a `#[serde(other)] Unknown` variant, and every
record struct has a flattened `extra` catch-all. So anything a real log contains
that we don't model is *captured and countable* rather than silently lost.
The `audit` command deserializes a whole corpus through those types and reports,
per discriminant, whether it's **normalized / dropped / unmodeled** — plus the
unmodeled field names and tool frequencies. This is how
[`ROADMAP.md`](ROADMAP.md) is produced: the gaps fall out of the type system
instead of being guessed. On the maintainer's local corpus (opt-in,
`SUPERCODE_CORPUS=1` — **not run in CI**) the audit reports `parse_errors=0`
across up to **1,500 files per corpus** (~1.2M Codex lines); a CI regression
test built on synthetic fixtures fails if a new unmodeled record type appears.
## How session loading works
Both tools persist conversations as JSONL:
| Claude Code | `~/.claude/projects/<encoded-cwd>/<id>.jsonl` | Anthropic message events, linked by `uuid`/`parentUuid` |
| Codex | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | `{timestamp,type,payload}` envelopes; `response_item` lines are canonical |
[`Session::load`] auto-detects the format and **normalizes** either one into a
provider-neutral `Vec<ChatMessage>` in the OpenAI chat-completions shape, so it
can be handed straight back to any model to continue. Provider-internal
artifacts that don't replay across vendors — Anthropic `thinking` blocks, Codex
`reasoning` items — are dropped.
### Saving and converting (GIMP model)
There's one canonical in-memory model plus **importers and exporters** per
format — exactly like an image editor that opens and exports several file types:
```rust
use supercode::{Session, SessionFormat};
# fn run() -> supercode::Result<()> {
let session = Session::load("session.jsonl")?; // import (auto-detect)
session.save("out.jsonl", SessionFormat::ClaudeCode)?; // export as Claude Code
let codex = session.to_jsonl(SessionFormat::Codex); // …or Codex, as a String
# let _ = codex; Ok(())
# }
```
The correctness property is a **semantic round-trip**: load → save → load yields
the same conversation. The opt-in corpus test (`SUPERCODE_CORPUS=1`, **not run
in CI**) asserts **≥97% of sampled files round-trip stably** on the maintainer's
local logs; CI covers the same normalization logic with synthetic fixtures.
Converting *between* formats is an "export" — framing with no slot in the target
(a Codex developer/system turn) may be dropped, but the user/assistant/tool
conversation is preserved.
**Manually verified once (not automated).** A session supercode wrote was
resumed by the stock `claude` and `codex` CLIs in headless mode, and each
correctly recalled a fact established earlier in the conversation — a one-off
check, not part of the test suite:
```sh
# Claude Code: supercode re-exports a session under a new id; claude resumes IT
supercode convert <claude session> --to claude-code --session-id <new> -o <dir>/<new>.jsonl
claude -r <new> -p "…" # → recalls prior context ✔
# Codex: same round-trip; codex resumes supercode's rollout
supercode convert <codex session> --to codex --session-id <new> -o <…>/rollout-…-<new>.jsonl
codex exec resume <new> "…" # → recalls prior context ✔
```
Codex validates the rollout header strictly, so the exporter **replays the
original `session_meta`/`turn_context` records** (preserved on load) and
synthesizes the conversation around them; converting *from* another format
synthesizes a header with the fields Codex requires.
## Customization surface
Everything lives on [`Config`] (built via `Config::builder()`):
- `model`, `base_url`, `api_key` / `api_key_env`
- `system_prompt`
- `temperature`, `max_tokens`, `max_iterations`
- `cwd` — where tools operate
- per-tool **enable/disable** and **description overrides**
- `extra_headers` — e.g. OpenRouter attribution headers
- an `event_sink` for streaming text + tool activity
Register your own tools by implementing [`Tool`] and calling
`agent.register_tool(...)`.
## Built-in tools
`read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `search` (regex,
gitignore-aware), `apply_patch`, `bash`, `shell` (persistent), and
`update_plan`. Each can be disabled or re-described per config.
## Architecture
```
Config ── model / endpoint / key / prompt / tool overrides / event sink
│
Agent ── the loop: stream a turn → run requested tools → feed results back → repeat
├── Provider (OpenAiProvider → OpenRouter / any OpenAI-compatible endpoint)
├── ToolRegistry (built-ins + your tools)
└── Session ── load & normalize Claude Code / Codex logs to continue them
```
## Status
Implemented and tested:
- ✅ OpenRouter / OpenAI-compatible streaming provider with tool calls
- ✅ Built-in tool suite + per-tool customization
- ✅ Agent loop with streaming events and an iteration budget
- ✅ Native Claude Code **and** Codex session loading + continuation
- ✅ Saving / converting sessions back to Claude Code and Codex formats
- ◑ Written files **manually** verified resumable by the stock `claude` and
`codex` binaries (a one-off check, not part of CI)
Exercised against the maintainer's local corpus (up to ~**3,000** logs across
both formats) via **opt-in** tests (`SUPERCODE_CORPUS=1`, **not run in CI**):
the load smoke asserts **0 parse errors** and ≥90% non-empty, and the round-trip
test asserts **≥97% stable**. CI runs synthetic-fixture tests that exercise the
same normalization logic. Run the opt-in sweeps yourself:
```sh
SUPERCODE_CORPUS=1 cargo test -p supercode-core -- --ignored --nocapture
```
Not yet built (natural next steps): a lossless native session format, richer
prompt-cache controls, and Linux (landlock/seccomp) sandboxing — the OS process
sandbox is macOS-only today.
## License
MIT OR Apache-2.0.