A Rust port of the Claude Code agent CLI, powered by a local OLLAMA instance instead of the Anthropic API. Single static binary, ~5–10 MB stripped.
Feature-complete MVP. Built incrementally over 13 phases with a signed commit and ≥90% line/function/region coverage at every step.
| Phase | What landed |
|---|---|
| 0 | Project skeleton, rust-toolchain, CI, SSH-signed commits, coverage gate |
| 1 | clap + rustyline REPL with injectable LineReader |
| 2 | OllamaClient over reqwest (/api/chat, non-streaming) |
| 3 | Tool enum + read_file + write_file + SessionContext path-safety |
| 4 | bash tool with tokio::process + timeout |
| 5 | NDJSON streaming + incremental render |
| 6 | glob + grep (ripgrep engine, in-process) |
| 7 | Permission gate (Default / AutoAccept / Plan) with prompt abstraction |
| 8 | Conversation history with token-budgeted compaction |
| 9 | tracing instrumentation + RUST_LOG env filter |
| 10 | AgentRunner parallel tool dispatch via JoinSet + let chains |
| 11 | wiremock end-to-end integration tests + insta snapshots |
| 12 | edit tool, release profile + cross-compile docs |
- Rust 1.95.0 stable (pinned via
rust-toolchain.toml, edition 2024). - A running OLLAMA server reachable from this machine. On WSL the host's OLLAMA is reachable via
http://host.docker.internal:11434. - A model with tool-calling support pulled locally —
ollama pull llama3.2:3b(small, default) orollama pull qwen2.5-coder:7b(recommended for real work).
| Flag | Env var | Default |
|---|---|---|
--ollama-host |
OLLAMA_HOST |
http://host.docker.internal:11434 |
--ollama-model |
OLLAMA_MODEL |
llama3.2:3b |
--log-filter |
RUST_LOG |
info |
--no-banner |
— | (off) |
cargo build --release
./target/release/agent-code --version
./target/release/agent-code # REPL with banner
./target/release/agent-code --no-banner # REPL, quietcargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo llvm-cov --all-features \
--ignore-filename-regex='src/main\.rs|src/tui/rustyline_reader\.rs' \
--fail-under-lines 90 \
--fail-under-functions 90 \
--fail-under-regions 90src/main.rs and src/tui/rustyline_reader.rs are excluded — both are thin wrappers around stdio/rustyline that can't be exercised from unit tests without a real TTY.
INSTA_UPDATE=always cargo test --all-features # regenerate
git diff tests/snapshots/ # review
git add tests/snapshots/ # acceptThe project pins rust-toolchain.toml with x86_64-unknown-linux-musl, x86_64-pc-windows-gnu and aarch64-apple-darwin targets.
cargo install cross --locked
cross build --release --target x86_64-unknown-linux-musl # static linux binary
cross build --release --target x86_64-pc-windows-gnu # windows .exe
cross build --release --target aarch64-apple-darwin # macOS arm64Release profile ([profile.release] in Cargo.toml) sets strip = true, lto = "fat", codegen-units = 1, panic = "abort" so the binary is ~5–10 MB.
src/
├─ main.rs # 3-line shim (excluded from coverage)
├─ lib.rs # banner + run_with_io entry point
├─ cli.rs # clap struct
├─ observability.rs # tracing setup
├─ agent/{mod.rs, history.rs, runner.rs}
├─ client/{mod.rs, ollama.rs, streaming.rs}
├─ tools/{mod.rs, read_file.rs, write_file.rs, edit.rs, bash.rs, glob_tool.rs, grep_tool.rs}
├─ permissions/{mod.rs, gate.rs}
├─ session/{mod.rs, context.rs}
└─ tui/{mod.rs, repl.rs, render.rs, rustyline_reader.rs}
resources/
├─ system_prompt.md
└─ default_settings.toml
tests/
├─ integration_repl.rs # assert_cmd against the binary
├─ integration_agent.rs # AgentRunner against wiremock OLLAMA
├─ common/mod.rs
└─ snapshots/ # insta snapshots
OLLAMA isn't reachable from WSL. Confirm the Windows host is running ollama serve and the WSL nameserver (in /etc/resolv.conf) responds on port 11434:
NS=$(grep nameserver /etc/resolv.conf | awk '{print $2}')
curl -s "http://$NS:11434/api/tags"Then export OLLAMA_HOST=http://$NS:11434 (or use --ollama-host).
Tool calls always come back empty / model never picks tools. The default llama3.2:3b is tiny; agent loops on it are flaky. Try a stronger model:
ollama pull qwen2.5-coder:7b
OLLAMA_MODEL=qwen2.5-coder:7b ./target/release/agent-codeCoverage gate fails locally but not on CI (or vice versa). Coverage threshold is configured per-attribute (lines, functions, regions ≥90% across the project, ignoring main.rs and rustyline_reader.rs). Run with --html and open the report to see which file dropped.
Commits not showing as signed. git config --global gpg.format ssh and pointing user.signingkey at a public key plus enabling commit.gpgsign=true is enough. Verify locally with git log --show-signature.