From effe165f315f23cadb8d362318e62040e4d75a0f Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 2 Mar 2026 12:29:29 -0500 Subject: [PATCH] Simplify `-v` flag from two verbosity levels to a single boolean The `-vv` (full detail) mode was not useful in practice. This collapses the two-tier `-v`/`-vv` into a single `--verbose` boolean and removes the now-dead `format_event_detail` function and its tests. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/arc-api/src/serve.rs | 2 +- crates/arc-workflows/src/cli/backend.rs | 31 +- crates/arc-workflows/src/cli/mod.rs | 410 +--------------------- crates/arc-workflows/src/cli/run.rs | 10 +- crates/arc-workflows/tests/integration.rs | 2 +- 5 files changed, 16 insertions(+), 439 deletions(-) diff --git a/crates/arc-api/src/serve.rs b/crates/arc-api/src/serve.rs index 5f85b816b..bf848faf2 100644 --- a/crates/arc-api/src/serve.rs +++ b/crates/arc-api/src/serve.rs @@ -106,7 +106,7 @@ pub async fn serve_command(args: ServeArgs, styles: &'static Styles) -> anyhow:: Some(Box::new(AgentApiBackend::new( model.clone(), provider_enum, - 0, + false, styles, ))) } diff --git a/crates/arc-workflows/src/cli/backend.rs b/crates/arc-workflows/src/cli/backend.rs index 43f8d0a06..ad8b2e2ae 100644 --- a/crates/arc-workflows/src/cli/backend.rs +++ b/crates/arc-workflows/src/cli/backend.rs @@ -25,14 +25,14 @@ use crate::outcome::StageUsage; pub struct AgentApiBackend { model: String, provider: Provider, - verbose: u8, + verbose: bool, styles: &'static Styles, sessions: Mutex>, } impl AgentApiBackend { #[must_use] - pub fn new(model: String, provider: Provider, verbose: u8, styles: &'static Styles) -> Self { + pub fn new(model: String, provider: Provider, verbose: bool, styles: &'static Styles) -> Self { Self { model, provider, @@ -283,7 +283,7 @@ impl CodergenBackend for AgentApiBackend { } // Verbose stderr printing (gated on verbosity) - if verbose >= 1 { + if verbose { match &event.event { AgentEvent::ToolCallStarted { tool_name, @@ -299,21 +299,6 @@ impl CodergenBackend for AgentApiBackend { args = format_tool_args(arguments), ); } - AgentEvent::ToolCallCompleted { - tool_name, - output, - is_error, - .. - } if verbose >= 2 => { - let label = if *is_error { "error" } else { "result" }; - eprintln!( - "{dim}[{node_id}] [{label}] {tool_name}:{reset}\n{}", - serde_json::to_string_pretty(output) - .unwrap_or_else(|_| output.to_string()), - dim = styles.dim, - reset = styles.reset, - ); - } AgentEvent::Error { error } => { eprintln!( "{dim}[{node_id}]{reset} {red}\u{2717} {error}{reset}", @@ -379,7 +364,7 @@ impl CodergenBackend for AgentApiBackend { stage_usage.cost = super::compute_stage_cost(&stage_usage); // Print session summary to stderr. - if self.verbose >= 1 { + if self.verbose { let total_tokens = total_usage.input_tokens + total_usage.output_tokens; let token_str = if total_tokens >= 1000 { format!("{}k tokens", total_tokens / 1000) @@ -469,10 +454,10 @@ mod tests { #[test] fn agent_backend_stores_config() { let styles = Box::leak(Box::new(Styles::new(false))); - let backend = AgentApiBackend::new("claude-opus-4-6".to_string(), Provider::OpenAi, 2, styles); + let backend = AgentApiBackend::new("claude-opus-4-6".to_string(), Provider::OpenAi, true, styles); assert_eq!(backend.model, "claude-opus-4-6"); assert_eq!(backend.provider, Provider::OpenAi); - assert_eq!(backend.verbose, 2); + assert!(backend.verbose); } #[test] @@ -481,7 +466,7 @@ mod tests { let backend = AgentApiBackend::new( "claude-opus-4-6".to_string(), Provider::Anthropic, - 0, + false, styles, ); assert!(backend.sessions.lock().unwrap().is_empty()); @@ -493,7 +478,7 @@ mod tests { let backend = AgentApiBackend::new( "claude-opus-4-6".to_string(), Provider::Anthropic, - 0, + false, styles, ); let mut profile = backend.build_profile(); diff --git a/crates/arc-workflows/src/cli/mod.rs b/crates/arc-workflows/src/cli/mod.rs index beeec7ecf..6ab54c245 100644 --- a/crates/arc-workflows/src/cli/mod.rs +++ b/crates/arc-workflows/src/cli/mod.rs @@ -106,9 +106,9 @@ pub struct RunArgs { #[arg(long)] pub provider: Option, - /// Verbosity level (-v summary, -vv full details) - #[arg(short, long, action = clap::ArgAction::Count)] - pub verbose: u8, + /// Enable verbose output + #[arg(short, long)] + pub verbose: bool, /// Sandbox for agent tools #[arg(long, value_enum)] @@ -571,353 +571,6 @@ pub fn format_event_summary(event: &WorkflowRunEvent, styles: &Styles) -> String format!("{dim}{body}{reset}", dim = styles.dim, reset = styles.reset) } -/// Multi-line detail view of a workflow run event for `-vv` output. -/// Box-drawing is dimmed; values are normal. -#[must_use] -pub fn format_event_detail(event: &WorkflowRunEvent, styles: &Styles) -> String { - let d = styles.dim; - let r = styles.reset; - - match event { - WorkflowRunEvent::WorkflowRunStarted { name, run_id, .. } => { - format!( - "{d}── WORKFLOW_RUN_STARTED ─────────────────────────{r}\n {d}name:{r} {name}\n {d}id:{r} {run_id}\n" - ) - } - WorkflowRunEvent::WorkflowRunCompleted { - duration_ms, - artifact_count, - total_cost, - .. - } => { - let mut s = format!("{d}── WORKFLOW_RUN_COMPLETED ───────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n {d}artifact_count:{r} {artifact_count}\n"); - if let Some(cost) = total_cost { - s.push_str(&format!(" {d}total_cost:{r} {}\n", format_cost(*cost))); - } - s - } - WorkflowRunEvent::WorkflowRunFailed { error, duration_ms, .. } => { - format!("{d}── WORKFLOW_RUN_FAILED ──────────────────────────{r}\n {d}error:{r} {error}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::StageStarted { name, index, handler_type, attempt, max_attempts } => { - let mut s = format!( - "{d}── STAGE_STARTED ────────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n" - ); - if let Some(ht) = handler_type { - s.push_str(&format!(" {d}handler_type:{r} {ht}\n")); - } - s.push_str(&format!(" {d}attempt:{r} {attempt}/{max_attempts}\n")); - s - } - WorkflowRunEvent::StageCompleted { - name, - index, - duration_ms, - status, - preferred_label, - suggested_next_ids, - usage, - failure_reason, - notes, - files_touched, - attempt, - max_attempts, - failure_class, - } => { - let mut s = format!("{d}── STAGE_COMPLETED ──────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}duration_ms:{r} {duration_ms}\n {d}status:{r} {status}\n"); - if let Some(label) = preferred_label { - s.push_str(&format!(" {d}preferred_label:{r} {label}\n")); - } - if !suggested_next_ids.is_empty() { - s.push_str(&format!(" {d}suggested_next_ids:{r} {}\n", suggested_next_ids.join(", "))); - } - if let Some(u) = usage { - let total = u.input_tokens + u.output_tokens; - s.push_str(&format!(" {d}model:{r} {}\n", u.model)); - s.push_str(&format!(" {d}tokens:{r} {} ({} in / {} out)\n", - format_tokens_human(total), - format_tokens_human(u.input_tokens), - format_tokens_human(u.output_tokens), - )); - if let Some(cache_read) = u.cache_read_tokens { - s.push_str(&format!(" {d}cache_read:{r} {}\n", format_tokens_human(cache_read))); - } - if let Some(cache_write) = u.cache_write_tokens { - s.push_str(&format!(" {d}cache_write:{r} {}\n", format_tokens_human(cache_write))); - } - if let Some(reasoning) = u.reasoning_tokens { - s.push_str(&format!(" {d}reasoning:{r} {}\n", format_tokens_human(reasoning))); - } - if let Some(cost) = compute_stage_cost(u) { - s.push_str(&format!(" {d}cost:{r} {}\n", format_cost(cost))); - } - } - if !files_touched.is_empty() { - s.push_str(&format!(" {d}files_touched:{r} {} files\n", files_touched.len())); - } - if let Some(reason) = failure_reason { - s.push_str(&format!(" {d}failure_reason:{r} {reason}\n")); - } - if let Some(n) = notes { - s.push_str(&format!(" {d}notes:{r} {n}\n")); - } - s.push_str(&format!(" {d}attempt:{r} {attempt}/{max_attempts}\n")); - if let Some(fc) = failure_class { - s.push_str(&format!(" {d}failure_class:{r} {fc}\n")); - } - s - } - WorkflowRunEvent::StageFailed { - name, - index, - error, - will_retry, - failure_reason, - failure_class, - } => { - let mut s = format!("{d}── STAGE_FAILED ─────────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}error:{r} {error}\n {d}will_retry:{r} {will_retry}\n"); - if let Some(reason) = failure_reason { - s.push_str(&format!(" {d}failure_reason:{r} {reason}\n")); - } - if let Some(fc) = failure_class { - s.push_str(&format!(" {d}failure_class:{r} {fc}\n")); - } - s - } - WorkflowRunEvent::StageRetrying { - name, - index, - attempt, - max_attempts, - delay_ms, - } => { - format!("{d}── STAGE_RETRYING ───────────────────────────{r}\n {d}name:{r} {name}\n {d}index:{r} {index}\n {d}attempt:{r} {attempt}/{max_attempts}\n {d}delay_ms:{r} {delay_ms}\n") - } - WorkflowRunEvent::ParallelStarted { branch_count, join_policy, error_policy } => { - format!("{d}── PARALLEL_STARTED ─────────────────────────{r}\n {d}branch_count:{r} {branch_count}\n {d}join_policy:{r} {join_policy}\n {d}error_policy:{r} {error_policy}\n") - } - WorkflowRunEvent::ParallelBranchStarted { branch, index } => { - format!("{d}── PARALLEL_BRANCH_STARTED ──────────────────{r}\n {d}branch:{r} {branch}\n {d}index:{r} {index}\n") - } - WorkflowRunEvent::ParallelBranchCompleted { - branch, - index, - duration_ms, - status, - } => { - format!("{d}── PARALLEL_BRANCH_COMPLETED ────────────────{r}\n {d}branch:{r} {branch}\n {d}index:{r} {index}\n {d}duration_ms:{r} {duration_ms}\n {d}status:{r} {status}\n") - } - WorkflowRunEvent::ParallelCompleted { - duration_ms, - success_count, - failure_count, - } => { - format!("{d}── PARALLEL_COMPLETED ───────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n {d}success_count:{r} {success_count}\n {d}failure_count:{r} {failure_count}\n") - } - WorkflowRunEvent::InterviewStarted { question, stage, question_type } => { - format!("{d}── INTERVIEW_STARTED ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}question:{r} {question}\n {d}question_type:{r} {question_type}\n") - } - WorkflowRunEvent::InterviewCompleted { - question, - answer, - duration_ms, - } => { - format!("{d}── INTERVIEW_COMPLETED ──────────────────────{r}\n {d}question:{r} {question}\n {d}answer:{r} {answer}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::InterviewTimeout { - question, - stage, - duration_ms, - } => { - format!("{d}── INTERVIEW_TIMEOUT ────────────────────────{r}\n {d}question:{r} {question}\n {d}stage:{r} {stage}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::CheckpointSaved { node_id } => { - format!( - "{d}── CHECKPOINT_SAVED ─────────────────────────{r}\n {d}node_id:{r} {node_id}\n" - ) - } - WorkflowRunEvent::GitCheckpoint { run_id, node_id, status, git_commit_sha } => { - format!( - "{d}── GIT_CHECKPOINT ───────────────────────────{r}\n {d}run_id:{r} {run_id}\n {d}node_id:{r} {node_id}\n {d}status:{r} {status}\n {d}sha:{r} {git_commit_sha}\n" - ) - } - WorkflowRunEvent::EdgeSelected { from_node, to_node, label, condition } => { - let mut s = format!("{d}── EDGE_SELECTED ────────────────────────────{r}\n {d}from:{r} {from_node}\n {d}to:{r} {to_node}\n"); - if let Some(l) = label { - s.push_str(&format!(" {d}label:{r} {l}\n")); - } - if let Some(c) = condition { - s.push_str(&format!(" {d}condition:{r} {c}\n")); - } - s - } - WorkflowRunEvent::LoopRestart { from_node, to_node } => { - format!("{d}── LOOP_RESTART ─────────────────────────────{r}\n {d}from:{r} {from_node}\n {d}to:{r} {to_node}\n") - } - WorkflowRunEvent::Prompt { stage, text } => { - format!("{d}── PROMPT ───────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}text:{r}\n{text}\n") - } - WorkflowRunEvent::Agent { stage, event } => match event { - AgentEvent::AssistantMessage { text, model, usage, tool_call_count } => { - let total = usage.input_tokens + usage.output_tokens; - let truncated = if text.len() > 200 { &text[..200] } else { text.as_str() }; - let mut s = format!("{d}── ASSISTANT_MESSAGE ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}model:{r} {model}\n {d}tokens:{r} {} ({} in / {} out)\n {d}tool_calls:{r} {tool_call_count}\n", - format_tokens_human(total), - format_tokens_human(usage.input_tokens), - format_tokens_human(usage.output_tokens), - ); - if let Some(cache_read) = usage.cache_read_tokens { - s.push_str(&format!(" {d}cache_read:{r} {}\n", format_tokens_human(cache_read))); - } - if let Some(cache_write) = usage.cache_write_tokens { - s.push_str(&format!(" {d}cache_write:{r} {}\n", format_tokens_human(cache_write))); - } - if let Some(reasoning) = usage.reasoning_tokens { - s.push_str(&format!(" {d}reasoning:{r} {}\n", format_tokens_human(reasoning))); - } - s.push_str(&format!(" {d}text:{r} {truncated}\n")); - s - } - AgentEvent::ToolCallStarted { tool_name, tool_call_id, arguments } => { - let args_str = serde_json::to_string(arguments).unwrap_or_else(|_| arguments.to_string()); - let truncated = if args_str.len() > 200 { &args_str[..200] } else { &args_str }; - format!("{d}── TOOL_CALL_STARTED ────────────────────────{r}\n {d}stage:{r} {stage}\n {d}tool_name:{r} {tool_name}\n {d}tool_call_id:{r} {tool_call_id}\n {d}arguments:{r} {truncated}\n") - } - AgentEvent::ToolCallCompleted { tool_name, tool_call_id, output, is_error } => { - let output_str = serde_json::to_string(output).unwrap_or_else(|_| output.to_string()); - let truncated = if output_str.len() > 200 { &output_str[..200] } else { &output_str }; - format!("{d}── TOOL_CALL_COMPLETED ──────────────────────{r}\n {d}stage:{r} {stage}\n {d}tool_name:{r} {tool_name}\n {d}tool_call_id:{r} {tool_call_id}\n {d}is_error:{r} {is_error}\n {d}output:{r} {truncated}\n") - } - AgentEvent::Error { error } => { - format!("{d}── SESSION_ERROR ────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}error:{r} {error}\n") - } - AgentEvent::ContextWindowWarning { estimated_tokens, context_window_size, usage_percent } => { - format!("{d}── CONTEXT_WINDOW_WARNING ───────────────────{r}\n {d}stage:{r} {stage}\n {d}estimated_tokens:{r} {estimated_tokens}\n {d}context_window_size:{r} {context_window_size}\n {d}usage_percent:{r} {usage_percent}%\n") - } - AgentEvent::LoopDetected => { - format!("{d}── LOOP_DETECTED ────────────────────────────{r}\n {d}stage:{r} {stage}\n") - } - AgentEvent::TurnLimitReached { max_turns } => { - format!("{d}── TURN_LIMIT_REACHED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}max_turns:{r} {max_turns}\n") - } - AgentEvent::CompactionStarted { estimated_tokens, context_window_size } => { - format!("{d}── COMPACTION_STARTED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}estimated_tokens:{r} {estimated_tokens}\n {d}context_window_size:{r} {context_window_size}\n") - } - AgentEvent::CompactionCompleted { original_turn_count, preserved_turn_count, summary_token_estimate, tracked_file_count } => { - format!("{d}── COMPACTION_COMPLETED ─────────────────────{r}\n {d}stage:{r} {stage}\n {d}original_turn_count:{r} {original_turn_count}\n {d}preserved_turn_count:{r} {preserved_turn_count}\n {d}summary_token_estimate:{r} {summary_token_estimate}\n {d}tracked_file_count:{r} {tracked_file_count}\n") - } - AgentEvent::LlmRetry { provider, model, attempt, delay_secs, error } => { - let delay_ms = (*delay_secs * 1000.0) as u64; - format!("{d}── LLM_RETRY ────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}provider:{r} {provider}\n {d}model:{r} {model}\n {d}attempt:{r} {attempt}\n {d}delay_ms:{r} {delay_ms}\n {d}error:{r} {error}\n") - } - AgentEvent::SubAgentSpawned { agent_id, depth, task } => { - let task_preview = if task.len() > 200 { &task[..200] } else { task.as_str() }; - format!("{d}── SUBAGENT_SPAWNED ─────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}task:{r} {task_preview}\n") - } - AgentEvent::SubAgentCompleted { agent_id, depth, success, turns_used } => { - format!("{d}── SUBAGENT_COMPLETED ───────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}success:{r} {success}\n {d}turns_used:{r} {turns_used}\n") - } - AgentEvent::SubAgentFailed { agent_id, depth, error } => { - format!("{d}── SUBAGENT_FAILED ──────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}error:{r} {error}\n") - } - AgentEvent::SubAgentClosed { agent_id, depth } => { - format!("{d}── SUBAGENT_CLOSED ──────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n") - } - AgentEvent::SubAgentEvent { agent_id, depth, event } => { - format!("{d}── SUBAGENT_EVENT ───────────────────────────{r}\n {d}stage:{r} {stage}\n {d}agent_id:{r} {agent_id}\n {d}depth:{r} {depth}\n {d}event:{r} {event:?}\n") - } - other => format!("{d}── AGENT ────────────────────────────────────{r}\n {d}stage:{r} {stage}\n {d}event:{r} {other:?}\n"), - } - WorkflowRunEvent::ParallelEarlyTermination { - reason, - completed_count, - pending_count, - } => { - format!("{d}── PARALLEL_EARLY_TERMINATION ───────────────{r}\n {d}reason:{r} {reason}\n {d}completed_count:{r} {completed_count}\n {d}pending_count:{r} {pending_count}\n") - } - WorkflowRunEvent::SubgraphStarted { node_id, start_node } => { - format!("{d}── SUBGRAPH_STARTED ─────────────────────────{r}\n {d}node_id:{r} {node_id}\n {d}start_node:{r} {start_node}\n") - } - WorkflowRunEvent::SubgraphCompleted { - node_id, - steps_executed, - status, - duration_ms, - } => { - format!("{d}── SUBGRAPH_COMPLETED ───────────────────────{r}\n {d}node_id:{r} {node_id}\n {d}steps_executed:{r} {steps_executed}\n {d}status:{r} {status}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::Sandbox { event } => { - use arc_agent::SandboxEvent; - match event { - SandboxEvent::Initializing { provider } => { - format!("{d}── SANDBOX_INITIALIZING ────────────────────{r}\n {d}provider:{r} {provider}\n") - } - SandboxEvent::Ready { provider, duration_ms } => { - format!("{d}── SANDBOX_READY ───────────────────────────{r}\n {d}provider:{r} {provider}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::InitializeFailed { provider, error, duration_ms } => { - format!("{d}── SANDBOX_INIT_FAILED ─────────────────────{r}\n {d}provider:{r} {provider}\n {d}error:{r} {error}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::CleanupStarted { provider } => { - format!("{d}── SANDBOX_CLEANUP_STARTED ─────────────────{r}\n {d}provider:{r} {provider}\n") - } - SandboxEvent::CleanupCompleted { provider, duration_ms } => { - format!("{d}── SANDBOX_CLEANUP_COMPLETED ───────────────{r}\n {d}provider:{r} {provider}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::CleanupFailed { provider, error } => { - format!("{d}── SANDBOX_CLEANUP_FAILED ──────────────────{r}\n {d}provider:{r} {provider}\n {d}error:{r} {error}\n") - } - SandboxEvent::SnapshotPulling { name } => { - format!("{d}── SANDBOX_SNAPSHOT_PULLING ───────────────────{r}\n {d}name:{r} {name}\n") - } - SandboxEvent::SnapshotPulled { name, duration_ms } => { - format!("{d}── SANDBOX_SNAPSHOT_PULLED ────────────────────{r}\n {d}name:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::SnapshotEnsuring { name } => { - format!("{d}── SANDBOX_SNAPSHOT_ENSURING ───────────────{r}\n {d}name:{r} {name}\n") - } - SandboxEvent::SnapshotCreating { name } => { - format!("{d}── SANDBOX_SNAPSHOT_CREATING ───────────────{r}\n {d}name:{r} {name}\n") - } - SandboxEvent::SnapshotReady { name, duration_ms } => { - format!("{d}── SANDBOX_SNAPSHOT_READY ──────────────────{r}\n {d}name:{r} {name}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::SnapshotFailed { name, error } => { - format!("{d}── SANDBOX_SNAPSHOT_FAILED ─────────────────{r}\n {d}name:{r} {name}\n {d}error:{r} {error}\n") - } - SandboxEvent::GitCloneStarted { url, branch } => { - let branch_str = branch.as_deref().unwrap_or("(default)"); - format!("{d}── SANDBOX_GIT_CLONE_STARTED ──────────────{r}\n {d}url:{r} {url}\n {d}branch:{r} {branch_str}\n") - } - SandboxEvent::GitCloneCompleted { url, duration_ms } => { - format!("{d}── SANDBOX_GIT_CLONE_COMPLETED ────────────{r}\n {d}url:{r} {url}\n {d}duration_ms:{r} {duration_ms}\n") - } - SandboxEvent::GitCloneFailed { url, error } => { - format!("{d}── SANDBOX_GIT_CLONE_FAILED ───────────────{r}\n {d}url:{r} {url}\n {d}error:{r} {error}\n") - } - } - } - WorkflowRunEvent::SetupStarted { command_count } => { - format!("{d}── SETUP_STARTED ────────────────────────────{r}\n {d}command_count:{r} {command_count}\n") - } - WorkflowRunEvent::SetupCommandStarted { command, index } => { - format!("{d}── SETUP_COMMAND_STARTED ────────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n") - } - WorkflowRunEvent::SetupCommandCompleted { command, index, exit_code, duration_ms } => { - format!("{d}── SETUP_COMMAND_COMPLETED ──────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n {d}exit_code:{r} {exit_code}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::SetupCompleted { duration_ms } => { - format!("{d}── SETUP_COMPLETED ──────────────────────────{r}\n {d}duration_ms:{r} {duration_ms}\n") - } - WorkflowRunEvent::SetupFailed { command, index, exit_code, stderr } => { - format!("{d}── SETUP_FAILED ─────────────────────────────{r}\n {d}index:{r} {index}\n {d}command:{r} {command}\n {d}exit_code:{r} {exit_code}\n {d}stderr:{r} {stderr}\n") - } - WorkflowRunEvent::StallWatchdogTimeout { node, idle_seconds } => { - format!("{d}── STALL_WATCHDOG_TIMEOUT ────────────────────{r}\n {d}node:{r} {node}\n {d}idle_seconds:{r} {idle_seconds}\n") - } - } -} - /// Compute the dollar cost for a stage's token usage, if pricing is available. #[must_use] pub fn compute_stage_cost(usage: &StageUsage) -> Option { @@ -1007,20 +660,6 @@ mod tests { assert!(s.contains("3")); } - #[test] - fn format_detail_sandbox_ready() { - let event = WorkflowRunEvent::Sandbox { - event: arc_agent::SandboxEvent::Ready { - provider: "local".into(), - duration_ms: 42, - }, - }; - let s = format_event_detail(&event, test_styles()); - assert!(s.contains("SANDBOX_READY")); - assert!(s.contains("local")); - assert!(s.contains("42")); - } - #[test] fn format_summary_subagent_spawned() { let event = WorkflowRunEvent::Agent { @@ -1054,36 +693,6 @@ mod tests { assert!(s.contains("turns=5")); } - #[test] - fn format_detail_subagent_failed() { - let event = WorkflowRunEvent::Agent { - stage: "code".into(), - event: AgentEvent::SubAgentFailed { - agent_id: "abcdef12-xxxx".into(), - depth: 2, - error: "timeout".into(), - }, - }; - let s = format_event_detail(&event, test_styles()); - assert!(s.contains("SUBAGENT_FAILED")); - assert!(s.contains("timeout")); - assert!(s.contains("depth")); - } - - #[test] - fn format_detail_subagent_closed() { - let event = WorkflowRunEvent::Agent { - stage: "code".into(), - event: AgentEvent::SubAgentClosed { - agent_id: "abcdef12-xxxx".into(), - depth: 1, - }, - }; - let s = format_event_detail(&event, test_styles()); - assert!(s.contains("SUBAGENT_CLOSED")); - assert!(s.contains("abcdef12-xxxx")); - } - #[test] fn format_summary_subagent_event() { let event = WorkflowRunEvent::Agent { @@ -1099,17 +708,4 @@ mod tests { assert!(s.contains("abcdef12")); } - #[test] - fn format_detail_setup_command_completed() { - let event = WorkflowRunEvent::SetupCommandCompleted { - command: "npm install".into(), - index: 0, - exit_code: 0, - duration_ms: 5000, - }; - let s = format_event_detail(&event, test_styles()); - assert!(s.contains("SETUP_COMMAND_COMPLETED")); - assert!(s.contains("npm install")); - assert!(s.contains("5000")); - } } diff --git a/crates/arc-workflows/src/cli/run.rs b/crates/arc-workflows/src/cli/run.rs index fc75344ff..0731e2875 100644 --- a/crates/arc-workflows/src/cli/run.rs +++ b/crates/arc-workflows/src/cli/run.rs @@ -27,7 +27,7 @@ use super::backend::AgentApiBackend; use super::cli_backend::{BackendRouter, AgentCliBackend}; use super::task_config; use super::{ - compute_stage_cost, format_cost, format_duration_human, format_event_detail, + compute_stage_cost, format_cost, format_duration_human, format_event_summary, format_tokens_human, print_diagnostics, read_dot_file, SandboxProvider, RunArgs, }; @@ -154,7 +154,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu } } - if args.verbose >= 1 { + if args.verbose { eprintln!( "{dim}Logs: {}{reset}", logs_dir.display(), @@ -230,11 +230,7 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu }); } - if args.verbose >= 2 { - emitter.on_event(move |event| { - eprint!("{}", format_event_detail(event, styles)); - }); - } else if args.verbose >= 1 { + if args.verbose { emitter.on_event(move |event| { eprintln!("{}", format_event_summary(event, styles)); }); diff --git a/crates/arc-workflows/tests/integration.rs b/crates/arc-workflows/tests/integration.rs index 9936fe3df..b771699dd 100644 --- a/crates/arc-workflows/tests/integration.rs +++ b/crates/arc-workflows/tests/integration.rs @@ -7208,7 +7208,7 @@ async fn arc_e2e_with_real_llm() { Some(Box::new(AgentApiBackend::new( model.clone(), Provider::Anthropic, - 0, + false, &TEST_STYLES, )) as Box)