diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index 8ad2b0422352..9f711aeb3356 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -187,8 +187,7 @@ async fn offer_extension_debugging_help( // Create a debugging prompt with context about the extension failure let debug_prompt = format!( "I'm having trouble starting an extension called '{}'. Here's the error I encountered:\n\n{}\n\nCan you help me diagnose what might be wrong and suggest how to fix it? Please consider common issues like:\n- Missing dependencies or tools\n- Configuration problems\n- Network connectivity (for remote extensions)\n- Permission issues\n- Path or environment variable problems", - extension_name, - error_message + extension_name, error_message ); // Create a minimal agent for debugging diff --git a/crates/goose-cli/src/session/mod.rs b/crates/goose-cli/src/session/mod.rs index bf655a698483..10c5f3a8072d 100644 --- a/crates/goose-cli/src/session/mod.rs +++ b/crates/goose-cli/src/session/mod.rs @@ -207,7 +207,9 @@ pub async fn classify_planner_response( message_text: String, provider: Arc, ) -> Result { - let prompt = format!("The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}"); + let prompt = format!( + "The text below is the output from an AI model which can either provide a plan or list of clarifying questions. Based on the text below, decide if the output is a \"plan\" or \"clarifying questions\".\n---\n{message_text}" + ); let message = Message::user().with_text(&prompt); let model_config = provider.get_model_config(); @@ -628,6 +630,7 @@ impl CliSession { let _provider = self.agent.provider().await?; + println!(); output::run_status_hook("thinking"); output::show_thinking(); let start_time = Instant::now(); @@ -1751,7 +1754,9 @@ fn display_log_notification( let _ = progress_bars.hide(); } if !is_json_mode { - print!("{}", formatted_message); + for line in formatted_message.lines() { + println!(" {}", console::style(line).dim()); + } std::io::stdout().flush().unwrap(); } } else if ntype == "shell_output" { @@ -1766,7 +1771,7 @@ fn display_log_notification( let _ = progress_bars.hide(); } if !is_json_mode { - println!("{}", formatted_message); + println!(" {}", console::style(formatted_message).dim()); } } } diff --git a/crates/goose-cli/src/session/output.rs b/crates/goose-cli/src/session/output.rs index 950cd021fdd8..221891b675ee 100644 --- a/crates/goose-cli/src/session/output.rs +++ b/crates/goose-cli/src/session/output.rs @@ -235,7 +235,7 @@ pub fn render_message(message: &Message, debug: bool) { }, MessageContent::Text(text) => print_markdown(&text.text, theme), MessageContent::ToolRequest(req) => render_tool_request(req, theme, debug), - MessageContent::ToolResponse(resp) => render_tool_response(resp, theme, debug), + MessageContent::ToolResponse(resp) => render_tool_response(resp, debug), MessageContent::Image(image) => { println!("Image: [data: {}, type: {}]", image.data, image.mime_type); } @@ -295,7 +295,7 @@ pub fn render_message_streaming( } MessageContent::ToolResponse(resp) => { flush_markdown_buffer(buffer, theme); - render_tool_response(resp, theme, debug); + render_tool_response(resp, debug); } MessageContent::ActionRequired(action) => { flush_markdown_buffer(buffer, theme); @@ -491,7 +491,7 @@ fn render_tool_request(req: &ToolRequest, theme: Theme, debug: bool) { } } -fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) { +fn render_tool_response(resp: &ToolResponse, debug: bool) { let config = Config::global(); match &resp.tool_result { @@ -519,11 +519,52 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) { if debug { println!("{:#?}", content); } else if let Some(text) = content.as_text() { - print_markdown(&text.text, theme); + print_tool_output(&text.text); } } } - Err(e) => print_markdown(&e.to_string(), theme), + Err(e) => { + println!(" {}", style(e.to_string()).red().dim()); + } + } +} + +fn print_tool_output(text: &str) { + if text.is_empty() { + return; + } + if !std::io::stdout().is_terminal() { + print!("{}", text); + return; + } + let max_lines = if get_show_full_tool_output() { + usize::MAX + } else { + 20 + }; + let lines: Vec<&str> = text.lines().collect(); + if lines.len() <= max_lines { + for line in &lines { + println!(" {}", style(line).dim()); + } + } else { + let head = max_lines / 2; + let tail = max_lines - head; + for line in &lines[..head] { + println!(" {}", style(line).dim()); + } + println!( + " {}", + style(format!( + "... ({} lines hidden, /toggle to show all)", + lines.len() - head - tail + )) + .dim() + .italic() + ); + for line in &lines[lines.len() - tail..] { + println!(" {}", style(line).dim()); + } } } @@ -904,6 +945,7 @@ fn print_tool_header(call: &CallToolRequestParams) { ) }; println!(); + println!(" {}", style("─".repeat(40)).dim()); println!("{}", tool_header); } diff --git a/crates/goose-cli/src/session/task_execution_display/mod.rs b/crates/goose-cli/src/session/task_execution_display/mod.rs index 434c777271a2..8c9d3a7e3dbd 100644 --- a/crates/goose-cli/src/session/task_execution_display/mod.rs +++ b/crates/goose-cli/src/session/task_execution_display/mod.rs @@ -9,10 +9,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(test)] mod tests; -const CLEAR_SCREEN: &str = "\x1b[2J\x1b[H"; -const MOVE_TO_PROGRESS_LINE: &str = "\x1b[4;1H"; -const CLEAR_TO_EOL: &str = "\x1b[K"; -const CLEAR_BELOW: &str = "\x1b[J"; pub const TASK_EXECUTION_NOTIFICATION_TYPE: &str = "task_execution"; static INITIAL_SHOWN: AtomicBool = AtomicBool::new(false); @@ -85,18 +81,14 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str let mut display = String::new(); if !INITIAL_SHOWN.swap(true, Ordering::SeqCst) { - display.push_str(CLEAR_SCREEN); display.push_str("🎯 Task Execution Dashboard\n"); display.push_str("═══════════════════════════\n\n"); - } else { - display.push_str(MOVE_TO_PROGRESS_LINE); } display.push_str(&format!( - "📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed", + "📊 Progress: {} total | ⏳ {} pending | 🏃 {} running | ✅ {} completed | ❌ {} failed\n\n", stats.total, stats.pending, stats.running, stats.completed, stats.failed )); - display.push_str(&format!("{}\n\n", CLEAR_TO_EOL)); let mut sorted_tasks = tasks.clone(); sorted_tasks.sort_by(|a, b| a.id.cmp(&b.id)); @@ -105,7 +97,6 @@ fn format_tasks_update_from_event(event: &TaskExecutionNotificationEvent) -> Str display.push_str(&format_task_display(&task)); } - display.push_str(CLEAR_BELOW); display } else { String::new() @@ -155,25 +146,22 @@ fn format_task_display(task: &TaskInfo) -> String { }; task_display.push_str(&format!( - "{} {} ({}){}\n", - status_icon, task.task_name, task.task_type, CLEAR_TO_EOL + "{} {} ({})\n", + status_icon, task.task_name, task.task_type )); if !task.task_metadata.is_empty() { - task_display.push_str(&format!( - " 📋 Parameters: {}{}\n", - task.task_metadata, CLEAR_TO_EOL - )); + task_display.push_str(&format!(" 📋 Parameters: {}\n", task.task_metadata)); } if let Some(duration_secs) = task.duration_secs { - task_display.push_str(&format!(" ⏱️ {:.1}s{}\n", duration_secs, CLEAR_TO_EOL)); + task_display.push_str(&format!(" ⏱️ {:.1}s\n", duration_secs)); } if matches!(task.status, TaskStatus::Running) && !task.current_output.trim().is_empty() { let processed_output = process_output_for_display(&task.current_output); if !processed_output.is_empty() { - task_display.push_str(&format!(" 💬 {}{}\n", processed_output, CLEAR_TO_EOL)); + task_display.push_str(&format!(" 💬 {}\n", processed_output)); } } @@ -181,7 +169,7 @@ fn format_task_display(task: &TaskInfo) -> String { if let Some(result_data) = &task.result_data { let result_preview = format_result_data_for_display(result_data); if !result_preview.is_empty() { - task_display.push_str(&format!(" 📄 {}{}\n", result_preview, CLEAR_TO_EOL)); + task_display.push_str(&format!(" 📄 {}\n", result_preview)); } } } @@ -189,14 +177,10 @@ fn format_task_display(task: &TaskInfo) -> String { if matches!(task.status, TaskStatus::Failed) { if let Some(error) = &task.error { let error_preview = safe_truncate(error, 80); - task_display.push_str(&format!( - " ⚠️ {}{}\n", - error_preview.replace('\n', " "), - CLEAR_TO_EOL - )); + task_display.push_str(&format!(" ⚠️ {}\n", error_preview.replace('\n', " "))); } } - task_display.push_str(&format!("{}\n", CLEAR_TO_EOL)); + task_display.push('\n'); task_display } diff --git a/crates/goose-cli/src/session/task_execution_display/tests.rs b/crates/goose-cli/src/session/task_execution_display/tests.rs index c86b62258ecd..df8cf14a6ec3 100644 --- a/crates/goose-cli/src/session/task_execution_display/tests.rs +++ b/crates/goose-cli/src/session/task_execution_display/tests.rs @@ -145,7 +145,7 @@ fn test_format_tasks_update_from_event() { let result2 = format_tasks_update_from_event(&event); assert!(!result2.contains("🎯 Task Execution Dashboard")); - assert!(result2.contains(MOVE_TO_PROGRESS_LINE)); + assert!(result2.contains("📊 Progress:")); } #[test]