better formatting cli
This commit is contained in:
@@ -236,7 +236,7 @@ pub async fn run() -> Result<()> {
|
||||
let result = agent
|
||||
.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true)
|
||||
.await?;
|
||||
output.print_markdown(&result.response);
|
||||
output.print_smart(&result.response);
|
||||
} else {
|
||||
// Interactive mode (default)
|
||||
if !cli.retro {
|
||||
@@ -809,7 +809,7 @@ async fn execute_task<W: UiWriter>(
|
||||
if attempt > 1 {
|
||||
output.print(&format!("✅ Request succeeded after {} attempts", attempt));
|
||||
}
|
||||
output.print_markdown(&result.response);
|
||||
output.print_smart(&result.response);
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -1087,7 +1087,7 @@ async fn run_autonomous(
|
||||
Ok(result) => {
|
||||
// Display player's implementation result
|
||||
output.print("📝 Player implementation completed:");
|
||||
output.print_markdown(&result.response);
|
||||
output.print_smart(&result.response);
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -1332,7 +1332,7 @@ Remember: Be thorough in your review but concise in your feedback. APPROVE if th
|
||||
continue;
|
||||
}
|
||||
|
||||
output.print(&format!("Coach feedback:\n{}", coach_feedback_text));
|
||||
output.print_smart(&format!("Coach feedback:\n{}", coach_feedback_text));
|
||||
|
||||
// Check if coach approved the implementation
|
||||
if coach_result.is_approved() {
|
||||
|
||||
@@ -27,10 +27,40 @@ impl SimpleOutput {
|
||||
Self { mad_skin }
|
||||
}
|
||||
|
||||
/// Detect if text contains markdown formatting
|
||||
fn has_markdown(&self, text: &str) -> bool {
|
||||
// Check for common markdown patterns
|
||||
text.contains("**") ||
|
||||
text.contains("```") ||
|
||||
text.contains("`") ||
|
||||
text.lines().any(|line| {
|
||||
let trimmed = line.trim();
|
||||
trimmed.starts_with('#') ||
|
||||
trimmed.starts_with("- ") ||
|
||||
trimmed.starts_with("* ") ||
|
||||
trimmed.starts_with("+ ") ||
|
||||
(trimmed.len() > 2 &&
|
||||
trimmed.chars().next().map_or(false, |c| c.is_ascii_digit()) &&
|
||||
trimmed.chars().nth(1) == Some('.') &&
|
||||
trimmed.chars().nth(2) == Some(' ')) ||
|
||||
(trimmed.contains('[') && trimmed.contains("]("))
|
||||
}) ||
|
||||
(text.matches('*').count() >= 2 && !text.contains("/*") && !text.contains("*/"))
|
||||
}
|
||||
|
||||
pub fn print(&self, text: &str) {
|
||||
println!("{}", text);
|
||||
}
|
||||
|
||||
/// Smart print that automatically detects and renders markdown
|
||||
pub fn print_smart(&self, text: &str) {
|
||||
if self.has_markdown(text) {
|
||||
self.print_markdown(text);
|
||||
} else {
|
||||
self.print(text);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_markdown(&self, markdown: &str) {
|
||||
self.mad_skin.print_text(markdown);
|
||||
}
|
||||
@@ -64,3 +94,33 @@ impl SimpleOutput {
|
||||
println!(" {:.1}% | {}/{} tokens", percentage, used, total);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_markdown_detection() {
|
||||
let output = SimpleOutput::new();
|
||||
|
||||
// Should detect markdown
|
||||
assert!(output.has_markdown("**bold text**"));
|
||||
assert!(output.has_markdown("`code`"));
|
||||
assert!(output.has_markdown("```\ncode block\n```"));
|
||||
assert!(output.has_markdown("# Header"));
|
||||
assert!(output.has_markdown("- list item"));
|
||||
assert!(output.has_markdown("* list item"));
|
||||
assert!(output.has_markdown("+ list item"));
|
||||
assert!(output.has_markdown("1. numbered item"));
|
||||
assert!(output.has_markdown("[link](url)"));
|
||||
assert!(output.has_markdown("*italic* text"));
|
||||
|
||||
// Should NOT detect markdown
|
||||
assert!(!output.has_markdown("plain text"));
|
||||
assert!(!output.has_markdown("file.txt"));
|
||||
assert!(!output.has_markdown("/* comment */"));
|
||||
assert!(!output.has_markdown("just one * asterisk"));
|
||||
assert!(!output.has_markdown("📁 Workspace: /path/to/dir"));
|
||||
assert!(!output.has_markdown("✅ Success message"));
|
||||
}
|
||||
}
|
||||
@@ -98,8 +98,25 @@ impl UiWriter for ConsoleUiWriter {
|
||||
first_line.to_string()
|
||||
};
|
||||
|
||||
// Add range information for read_file tool calls
|
||||
let header_suffix = if tool_name == "read_file" {
|
||||
// Check if start or end parameters are present
|
||||
let has_start = args.iter().any(|(k, _)| k == "start");
|
||||
let has_end = args.iter().any(|(k, _)| k == "end");
|
||||
|
||||
if has_start || has_end {
|
||||
let start_val = args.iter().find(|(k, _)| k == "start").map(|(_, v)| v.as_str()).unwrap_or("0");
|
||||
let end_val = args.iter().find(|(k, _)| k == "end").map(|(_, v)| v.as_str()).unwrap_or("end");
|
||||
format!(" [{}..{}]", start_val, end_val)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
// Print with bold green formatting using ANSI escape codes
|
||||
println!("┌─\x1b[1;32m {} | {}\x1b[0m", tool_name, display_value);
|
||||
println!("┌─\x1b[1;32m {} | {}{}\x1b[0m", tool_name, display_value, header_suffix);
|
||||
} else {
|
||||
// Print with bold green formatting using ANSI escape codes
|
||||
println!("┌─\x1b[1;32m {}\x1b[0m", tool_name);
|
||||
@@ -255,7 +272,18 @@ impl UiWriter for RetroTuiWriter {
|
||||
} else {
|
||||
value.to_string()
|
||||
};
|
||||
*caption = truncated;
|
||||
|
||||
// Add range information for read_file tool calls
|
||||
let tool_name = self.current_tool_name.lock().unwrap();
|
||||
let range_suffix = if tool_name.as_ref().map_or(false, |name| name == "read_file") {
|
||||
// We need to check if start/end args will be provided - for now just check if this is a partial read
|
||||
// This is a simplified approach since we're building the caption incrementally
|
||||
String::new() // We'll handle this in print_tool_output_header instead
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
*caption = format!("{}{}", truncated, range_suffix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +291,21 @@ impl UiWriter for RetroTuiWriter {
|
||||
// This is called right before tool execution starts
|
||||
// Send the initial tool header to the TUI now
|
||||
if let Some(tool_name) = self.current_tool_name.lock().unwrap().as_ref() {
|
||||
let caption = self.current_tool_caption.lock().unwrap().clone();
|
||||
let mut caption = self.current_tool_caption.lock().unwrap().clone();
|
||||
|
||||
// Add range information for read_file tool calls
|
||||
if tool_name == "read_file" {
|
||||
// Check the tool output for start/end parameters
|
||||
let output = self.current_tool_output.lock().unwrap();
|
||||
let has_start = output.iter().any(|line| line.starts_with("start:"));
|
||||
let has_end = output.iter().any(|line| line.starts_with("end:"));
|
||||
|
||||
if has_start || has_end {
|
||||
let start_val = output.iter().find(|line| line.starts_with("start:")).map(|line| line.split(':').nth(1).unwrap_or("0").trim()).unwrap_or("0");
|
||||
let end_val = output.iter().find(|line| line.starts_with("end:")).map(|line| line.split(':').nth(1).unwrap_or("end").trim()).unwrap_or("end");
|
||||
caption = format!("{} [{}..{}]", caption, start_val, end_val);
|
||||
}
|
||||
}
|
||||
|
||||
// Send the tool output with initial header
|
||||
self.tui.tool_output(tool_name, &caption, "");
|
||||
|
||||
Reference in New Issue
Block a user