better formatting cli

This commit is contained in:
Dhanji Prasanna
2025-10-15 22:04:39 +11:00
parent beccc8fa15
commit 662748ed23
3 changed files with 109 additions and 7 deletions

View File

@@ -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() {

View File

@@ -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"));
}
}

View File

@@ -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, "");