opi-coding-agent 0.3.0

Interactive coding agent CLI with file editing and shell execution
Documentation

opi-coding-agent

Crates.io Docs.rs

The opi binary — a minimal terminal coding agent. Produced by this crate, built on opi-ai, opi-agent, and opi-tui.

简体中文 · ← opi


Status (v0.2.0)

Phase 1 MVP. The interactive TUI and non-interactive (positional prompt or --non-interactive) modes both work end-to-end against Anthropic. Six built-in tools, TOML configuration, exit codes, and a high-risk-tool safety policy are in place.

Not yet implemented: other providers, persistent sessions, sub-agents, skills, slash commands, /login / OAuth.

Install

cargo install opi-coding-agent
opi --version

Or download the pre-built binary for your platform from a GitHub Release.

Quick start

export ANTHROPIC_API_KEY=sk-ant-...

# Interactive (ratatui TUI)
opi

# Non-interactive — positional prompt to stdout, exit
opi "Find every TODO in this repo."

# Pick a different model
opi -m anthropic:claude-opus-4 "Explain src/main.rs"

# Allow write/edit/bash in non-interactive mode
opi "Add a CHANGELOG entry for the latest commit." --allow-mutating

CLI flags

Flag / arg Description
[PROMPT]... Positional prompt text; non-empty args select non-interactive mode
-m, --model <SPEC> Model spec (e.g. anthropic:claude-sonnet-4)
-c, --config <FILE> Path to a TOML config file (must exist)
-s, --system <FILE> System prompt file (prepended to the built-in prompt)
--non-interactive Force non-interactive mode (prompt text still required)
--allow-mutating Allow write / edit / bash in non-interactive mode
-v, --verbose Enable debug tracing

Configuration

TOML files merge user → project → --config (later layers override earlier keys).

Model resolution (highest → lowest):

  1. --model (CLI)
  2. OPI_MODEL — only when --config was not passed
  3. model in --config <file>
  4. Project — <CWD>/.opi/config.toml
  5. User — %APPDATA%\opi\config.toml (Windows) or ~/.config/opi/config.toml (Unix)
  6. Built-in defaults

.opi/config.toml shape (every field is optional; values shown are defaults):

[defaults]
model = "anthropic:claude-sonnet-4"
max_iterations = 50
tool_timeout_ms = 30000
theme = "default"
allow_mutating_tools = false

[thinking]
enabled = true
budget_tokens = 10000

[providers.anthropic]
api_key_env = "ANTHROPIC_API_KEY"
# base_url = "https://api.anthropic.com"  # override only if needed

Set defaults.allow_mutating_tools = true to skip --allow-mutating on every non-interactive invocation.

Built-in tools

Tools live in src/tool/:

Tool Args Notes
read path, optional offset + limit 1-based line range
glob pattern, optional path gitignore-aware
grep pattern, optional glob / path gitignore-aware, regex
write path, content mutating; needs --allow-mutating non-interactively
edit path, old_string, new_string exact-match replacement; mutating
bash command, optional timeout_secs uses cmd.exe on Windows, sh on Unix

All paths are resolved relative to (and constrained within) the workspace root passed to CodingHarness::new (the CLI uses the current working directory).

Modes

Non-interactive

NonInteractiveRunner::run() captures assistant text to stdout, diagnostics to stderr, and returns one of these exit codes:

Code Meaning
0 Success
1 Runtime failure
2 Config error
3 Auth failure (missing or invalid API key)
4 Provider failure
5 Tool failure
130 Interrupted (Ctrl+C)

Interactive

CodingHarness + InteractiveCodingHooks drives a ratatui TUI built from opi-tui widgets. Streaming text deltas update the live transcript, tool calls render with status, and mutating tools surface confirmation.

Library use

opi-coding-agent is also a regular library crate. The harness and runner modules let you embed the same loop in your own Rust app:

use opi_coding_agent::config::OpiConfig;
use opi_coding_agent::harness::CodingHarness;

# async fn example(provider: Box<dyn opi_ai::Provider>) -> anyhow::Result<()> {
let config = OpiConfig::default();
let mut harness = CodingHarness::new(
    provider,
    config.defaults.model.clone(),
    config,
    std::env::current_dir()?,
);
let _messages = harness.prompt("Hello!").await?;
# Ok(()) }

License

MIT — see workspace LICENSE.