run_prompt/run_prompt.rs
1//! Run a prompt against a harness and stream the normalized events.
2//!
3//! `cargo run --example run_prompt`
4//! (requires the `claude` CLI installed + signed in; swap `Claude` for
5//! `Bob` / `Codex` to drive a different agent).
6
7use harness::{Claude, Harness, HarnessError, RunEvent, RunMode, RunRequest, RunTuning};
8
9fn main() -> Result<(), HarnessError> {
10 // Pick a harness. `Claude` drives the `claude` CLI (must be installed +
11 // signed in). Swap for `harness::Bob::new()` or `harness::Codex::new()`.
12 let claude = Claude::new();
13
14 // `run_channel()` starts the run and hands back the events on a channel,
15 // so there's no callback/`Sender` plumbing to write by hand. It returns
16 // immediately; events arrive on background threads. (`run()` is still
17 // there for push semantics — forwarding straight onto a Tauri Channel or
18 // SSE sink from inside a callback.)
19 let (_handle, rx) = claude.run_channel(RunRequest {
20 run_id: "demo".into(),
21 prompt: "In one sentence, what is a Markdown heading?".into(),
22 cwd: None, // working dir for the agent's tool calls
23 mode: RunMode::Ask, // Ask = answer only; Edit = may edit files
24 tuning: RunTuning::default(), // optional: model / effort / max_turns
25 })?; // keep `_handle` to `.cancel()`; dropping it does NOT stop the run
26
27 // ONE normalized event stream, regardless of the backing CLI. `rx` hangs
28 // up on its own when the run ends, so this loop terminates without
29 // touching the handle:
30 for ev in rx {
31 match ev {
32 RunEvent::Text { delta, .. } => print!("{delta}"), // the answer
33 RunEvent::Thinking { delta, .. } => eprint!("{delta}"), // model reasoning
34 RunEvent::ToolStart { name, .. } => eprintln!("\n[tool] {name}"),
35 RunEvent::Error { message, .. } => eprintln!("\n[error] {message}"),
36 RunEvent::Exited { .. } => break,
37 _ => {}
38 }
39 }
40 Ok(())
41}