cli-agents
Guide

Streaming Events

Handling real-time streaming events from agent runs

Streaming Events

Every agent run emits a stream of StreamEvent values through the callback you pass to run(). All three providers (Claude, Codex, Gemini) are normalized into the same event types.

Handling events

use cli_agents::{run, RunOptions, StreamEvent};
use std::sync::Arc;

let opts = RunOptions {
    task: "Explain this codebase.".into(),
    ..Default::default()
};

let handle = run(opts, Some(Arc::new(|event: StreamEvent| {
    match &event {
        StreamEvent::TextDelta { text } => {
            print!("{text}");
        }
        StreamEvent::ThinkingDelta { text } => {
            eprint!("[thinking] {text}");
        }
        StreamEvent::ToolStart { tool_name, tool_id, args } => {
            eprintln!("▶ Tool started: {tool_name} ({tool_id})");
        }
        StreamEvent::ToolEnd { tool_id, success, output, error } => {
            if *success {
                eprintln!("✓ Tool {tool_id} succeeded");
            } else {
                eprintln!("✗ Tool {tool_id} failed: {}", error.as_deref().unwrap_or("unknown"));
            }
        }
        StreamEvent::TurnEnd => {
            eprintln!("--- turn complete ---");
        }
        StreamEvent::Error { message, severity } => {
            eprintln!("Error ({severity:?}): {message}");
        }
        StreamEvent::Done { result } => {
            println!("\nSuccess: {}", result.success);
            if let Some(stats) = &result.stats {
                println!("Tokens: in={:?} out={:?}", stats.input_tokens, stats.output_tokens);
            }
        }
        StreamEvent::Raw { provider, event } => {
            // Provider-specific events not covered by the unified types
        }
    }
})));

Event types

VariantWhen it fires
TextDeltaIncremental text output from the agent
ThinkingDeltaReasoning/thinking output (Claude extended thinking, Codex reasoning)
ToolStartA tool call has started — includes tool name, ID, and parsed arguments
ToolEndA tool call has completed — includes success status, output, and error
TurnEndA full agent turn has completed
ErrorAn error or warning — check severity for Warning vs Error
DoneRun completed — contains the final RunResult
RawEscape hatch for provider-specific events not mapped to the unified types

Event flow

A typical run produces events in this order:

TextDelta("I'll read the file.")
ToolStart { tool_name: "Read", tool_id: "t1", ... }
ToolEnd { tool_id: "t1", success: true, ... }
TextDelta("The file contains...")
TurnEnd
Done { result: RunResult { success: true, ... } }

Multi-turn runs repeat the TextDelta → Tool → TurnEnd cycle multiple times before the final Done.

JSON streaming (CLI)

When using the CLI binary, pass --json to get every event as a JSON line on stdout:

cli-agents --json --cwd ./my-project "List all public structs"

Each line is a serialized StreamEvent with a type discriminant:

{"type":"text_delta","text":"Here are the public structs:"}
{"type":"tool_start","toolName":"Bash","toolId":"t1","args":{"command":"grep -r 'pub struct'"}}
{"type":"tool_end","toolId":"t1","success":true,"output":"..."}
{"type":"done","result":{"success":true,"text":"..."}}

This is useful for piping into other tools or building custom frontends.

CLI binary usage

# Auto-discover CLI and run a task
cli-agents --cwd ./my-project "Summarize this project"

# Specify a provider
cli-agents --cli claude --cwd ./my-project "Find all TODO comments"

# With a system prompt
cli-agents --cli codex --cwd ./my-project \
  --system "You are a senior code reviewer." \
  "Review src/lib.rs for potential bugs."

# Verbose mode (show tool calls, thinking, and token stats)
cli-agents --cli gemini -v --cwd ~/projects/my-app "What dependencies does this project have?"

# List available CLIs
cli-agents --discover

CLI flags

FlagDescription
--cli <name>Which CLI to use (claude, codex, gemini) — auto-discovers if omitted
--model <name>Model name (e.g. sonnet, opus, o3)
--system <prompt>System prompt
--append-system-prompt <text>Append to the system prompt
--cwd <path>Working directory
--skip-permissionsRun without permission prompts
--jsonPrint all events as JSON lines
-v, --verboseShow tool calls, thinking, and token stats
--discoverList available CLIs and exit

On this page