-
Notifications
You must be signed in to change notification settings - Fork 4.6k
fix(cli): improve terminal output formatting and visual clarity #8025
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f7f6b96
578f902
22fc4eb
bfe88e8
bf8b619
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Comment on lines
+540
to
+543
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This truncates long tool responses whenever Useful? React with 👍 / 👎. |
||
| }; | ||
| let lines: Vec<&str> = text.lines().collect(); | ||
| if lines.len() <= max_lines { | ||
| for line in &lines { | ||
| println!(" {}", style(line).dim()); | ||
| } | ||
| } else { | ||
|
michaelneale marked this conversation as resolved.
|
||
| 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 | ||
|
Comment on lines
+558
to
+560
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The truncation banner tells users to run Useful? React with 👍 / 👎. |
||
| )) | ||
| .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); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"); | ||
|
Comment on lines
83
to
85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The header is now emitted only while Useful? React with 👍 / 👎. |
||
| } 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,48 +146,41 @@ 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)); | ||
| } | ||
| } | ||
|
|
||
| if matches!(task.status, TaskStatus::Completed) { | ||
| 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)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| 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 | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.