Enable verbose trace output to stderr during pipeline execution:
cargo run -p magpie-cli -- --trace --pipeline "add health check"
With --trace, you’ll see real-time output like:
[branch-slug] CALL: "Generate a short branch name slug for this task..." (truncated)[branch-slug] TEXT: add[branch-slug] TEXT: -health-check-endpoint[branch-slug] DONE: 1.2s, 0 tool calls[classify] CALL: "Classify this task as either SIMPLE, STANDARD, or BUGFIX..."[classify] TEXT: STANDARD[classify] DONE: 0.8s, 0 tool calls[execute-task] CALL: "You are planning how to implement a task..."[execute-task] TOOL_REQ: read_file { path: "src/main.rs" }[execute-task] TOOL_RES: <file contents>[execute-task] TEXT: I'll add a /health endpoint that returns 200 OK...[execute-task] DONE: 45.3s, 8 tool calls
{ "started_at": 1704067200000, "step_name": "execute-task", "prompt_preview": "You are planning how to implement a task. The file tree of the repository is provided as previous step output.\n\nTask: add health check endpoint\n\nCreate a brief plan:", "events": [ { "kind": "text", "content": "I'll add a /health endpoint", "elapsed_ms": 1200 }, { "kind": "tool_request", "content": "read_file { path: \"src/main.rs\" }", "elapsed_ms": 1250 }, { "kind": "tool_response", "content": "fn main() { ... }", "elapsed_ms": 1350 } ], "response_preview": "I'll add a /health endpoint that returns 200 OK with JSON: { \"status\": \"ok\" }. The endpoint will be added to the existing Axum router in src/main.rs.", "duration_ms": 45320, "tool_call_count": 8}
pub fn write_trace(trace: &AgentCallTrace, trace_dir: &Path) -> Result<()> { use std::io::Write; std::fs::create_dir_all(trace_dir)?; let date_str = date_from_unix_ms(trace.started_at); let file_path = trace_dir.join(format!("magpie-trace-{date_str}.jsonl")); let line = serde_json::to_string(trace)?; let mut file = std::fs::OpenOptions::new() .create(true) .append(true) .open(&file_path)?; writeln!(file, "{line}")?; debug!(path = %file_path.display(), "trace written"); Ok()}
Trace files are named by the date the trace was started, not when it was written. Multi-hour pipeline runs that span midnight will write all traces to the same file.
Verbose mode prints to stderr in the following format:
[step-name] LABEL: content (truncated to 120 chars)
Labels:
CALL — Agent call started (prompt preview)
TEXT — Agent output (buffered by line)
TOOL_REQ — Tool request
TOOL_RES — Tool response
THINK — Thinking block
ERROR — Error
DONE — Call finished (duration + tool count)
Text events are line-buffered to reduce noise from streaming tokens. Only complete lines are printed. Any remaining buffered text is flushed when a non-Text event arrives or when the trace finishes.
async fn claude_call(prompt: &str, step_name: &str, trace_dir: Option<&PathBuf>) -> Result<String> { let mut tb = TraceBuilder::new(step_name, prompt); let output = tokio::process::Command::new("claude") .args(["-p", prompt]) .env_remove("CLAUDECODE") .output() .await .context("failed to run `claude` CLI — is it installed and on PATH?")?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); tb.record_event(EventKind::Error, &stderr); let trace = tb.finish(""); if let Some(dir) = trace_dir { let _ = trace::write_trace(&trace, dir); } anyhow::bail!("claude CLI exited with {}: {}", output.status, stderr.trim()); } let response = String::from_utf8_lossy(&output.stdout).trim().to_string(); tb.record_event(EventKind::Text, &response); let trace = tb.finish(&response); if let Some(dir) = trace_dir { let _ = trace::write_trace(&trace, dir); } Ok(response)}