agent-commander 0.2.7

A Rust library to control agents enclosed in CLI commands like Anthropic Claude Code CLI
Documentation

agent-commander

crates.io version Rust CI/CD

Rust bindings and CLI tools for controlling AI coding agents through their native command-line interfaces.

This crate provides the same core behavior as the JavaScript package: command building, process control, isolation wrappers, JSON streaming helpers, model aliasing, and read-only planning mode.

Install

cargo add agent-commander

Install the CLI binaries:

cargo install agent-commander

CLI

start-agent --tool claude --working-directory "/tmp/project" --prompt "Inspect this project"
start-agent --tool claude --working-directory "/tmp/project" \
  --prompt "Plan the change" --read-only --model opus --fallback-model sonnet
start-agent --tool codex --working-directory "/tmp/project" \
  --prompt "Run a focused review" --isolation screen --screen-name review-agent --detached

Common options:

  • --tool <name>: claude, codex, opencode, qwen, gemini, or agent
  • --working-directory <path>: directory where the agent command runs
  • --prompt <text> and --system-prompt <text>: user and system prompts
  • --prompt-file <path>: read prompt input from a file for stdin-based tools
  • --model <name>: tool-specific model alias or full model name
  • --read-only or --plan-only: enforce native planning/no-write mode when supported
  • --approve-each (alias --permission-mode ask): approve each command, relaying native permission prompts as normalized NDJSON (supported for agent and claude)
  • --tool-executable <path>: override the native executable for any supported tool
  • --tool-env <KEY=VALUE>: add an environment variable to the native tool process, repeatable
  • --tool-arg <arg>: append a raw native tool argument, repeatable
  • --skip-default-safety-flags: suppress default autonomous safety bypass flags, including Qwen/Gemini --yolo
  • --isolation <mode>: none, screen, or docker
  • --dry-run: print the command without executing it

Claude-specific options include --append-system-prompt, --fallback-model, --session-id, --fork-session, --verbose, and --replay-user-messages.

Library

use agent_commander::{agent, AgentOptions, AgentStartOptions, AgentStopOptions};

#[tokio::main]
async fn main() -> Result<(), String> {
    let mut controller = agent(AgentOptions {
        tool: "claude".to_string(),
        working_directory: "/tmp/project".to_string(),
        prompt: Some("Return a short implementation plan".to_string()),
        model: Some("sonnet".to_string()),
        read_only: true,
        isolation: "none".to_string(),
        ..Default::default()
    })?;

    controller
        .start(AgentStartOptions {
            attached: false,
            ..Default::default()
        })
        .await?;

    let result = controller.stop(AgentStopOptions::default()).await?;
    println!("{}", result.plain_output);
    println!("{:?}", result.metadata);
    Ok(())
}

result.metadata is a normalized summary for claude, codex, opencode, and agent runs. It includes success and error classification, session ID, usage-limit reset details, result summary, cost estimates, stream token usage, optional model usage, and sub-agent call summaries. result.usage exposes the aggregated stream token usage as JSON for parity with the JavaScript package.

For large generated prompts, set prompt_file or let the controller create a temporary prompt file automatically for claude, codex, opencode, agent, qwen, and gemini.

For parity with fast-moving native CLIs, set raw executable, environment, and argument overrides for any supported tool on AgentOptions:

let mut controller = agent(AgentOptions {
    tool: "claude".to_string(),
    working_directory: "/tmp/project".to_string(),
    prompt_file: Some("/tmp/agent-prompt.txt".to_string()),
    executable: Some("/opt/claude-code/bin/claude".to_string()),
    extra_env: vec![("MCP_TIMEOUT".to_string(), "10000".to_string())],
    extra_args: vec![
        "--mcp-config".to_string(),
        "/tmp/mcp.json".to_string(),
        "--permission-mode".to_string(),
        "default".to_string(),
    ],
    skip_default_safety_flags: true,
    ..Default::default()
})?;

Shared Behavior

JavaScript and Rust expose the same core concepts:

  • Tool selection and model alias mapping
  • none, screen, and docker isolation modes
  • Dry-run command preview
  • JSON/NDJSON output parsing for tools that support it
  • Read-only planning mode for tools with enforceable native restrictions
  • Per-command approval (ask mode) with a normalized permission_request/permission_response relay for tools with a drivable native handshake

See shared concepts for behavior that should stay aligned across both packages, including the per-command approval parity table.

Release Flow

Rust releases use changelog fragments in rust/changelog.d/:

---
bump: patch
---

### Fixed

- Describe the user-facing fix.

The GitHub Release workflow publishes language-specific releases with rust_ tags and [Rust] vX.Y.Z release names.

Test

cargo fmt --all -- --check
cargo clippy --all-targets --all-features
cargo test --all-features

Interactive Terminal Capture

Use tui::capture_agent_tui when a test needs the client's real terminal interface rather than its headless JSON mode. The capture drives text, control keys, and resizes through a PTY, then returns an unrolled transcript, normalized message/tool-call events, settled frames, and asciicast-compatible replay data. When artifact_directory is set it also writes a transcript, frame data, a static snapshot, a cast, and an animated SVG suitable for CI artifacts.

use agent_commander::tui::{capture_agent_tui, AgentTuiOptions};

let capture = capture_agent_tui(AgentTuiOptions {
    tool: "codex".into(),
    working_directory: std::env::current_dir()?,
    prompt: Some("Summarize this repository".into()),
    artifact_directory: Some("artifacts/codex".into()),
    ..AgentTuiOptions::default()
})?;
println!("{}", capture.terminal.transcript);
# Ok::<(), Box<dyn std::error::Error>>(())