todo list formatting

This commit is contained in:
Dhanji Prasanna
2025-10-20 14:27:45 +11:00
parent 2008a81193
commit 2ad0c9a3fd
2 changed files with 131 additions and 33 deletions

View File

@@ -10,6 +10,7 @@ pub struct ConsoleUiWriter {
current_tool_args: Mutex<Vec<(String, String)>>, current_tool_args: Mutex<Vec<(String, String)>>,
current_output_line: Mutex<Option<String>>, current_output_line: Mutex<Option<String>>,
output_line_printed: Mutex<bool>, output_line_printed: Mutex<bool>,
in_todo_tool: Mutex<bool>,
} }
impl ConsoleUiWriter { impl ConsoleUiWriter {
@@ -19,6 +20,60 @@ impl ConsoleUiWriter {
current_tool_args: Mutex::new(Vec::new()), current_tool_args: Mutex::new(Vec::new()),
current_output_line: Mutex::new(None), current_output_line: Mutex::new(None),
output_line_printed: Mutex::new(false), output_line_printed: Mutex::new(false),
in_todo_tool: Mutex::new(false),
}
}
fn print_todo_line(&self, line: &str) {
// Transform and print todo list lines elegantly
let trimmed = line.trim();
// Skip the "📝 TODO list:" prefix line
if trimmed.starts_with("📝 TODO list:") || trimmed == "📝 TODO list is empty" {
return;
}
// Handle empty lines
if trimmed.is_empty() {
println!();
return;
}
// Detect indentation level
let indent_count = line.chars().take_while(|c| c.is_whitespace()).count();
let indent = " ".repeat(indent_count / 2); // Convert spaces to visual indent
// Format based on line type
if trimmed.starts_with("- [ ]") {
// Incomplete task
let task = trimmed.strip_prefix("- [ ]").unwrap_or(trimmed).trim();
println!("{}{}", indent, task);
} else if trimmed.starts_with("- [x]") || trimmed.starts_with("- [X]") {
// Completed task
let task = trimmed.strip_prefix("- [x]")
.or_else(|| trimmed.strip_prefix("- [X]"))
.unwrap_or(trimmed)
.trim();
println!("{}\x1b[2m☑ {}\x1b[0m", indent, task);
} else if trimmed.starts_with("- ") {
// Regular bullet point
let item = trimmed.strip_prefix("- ").unwrap_or(trimmed).trim();
println!("{}{}", indent, item);
} else if trimmed.starts_with("# ") {
// Heading
let heading = trimmed.strip_prefix("# ").unwrap_or(trimmed).trim();
println!("\n\x1b[1m{}\x1b[0m", heading);
} else if trimmed.starts_with("## ") {
// Subheading
let subheading = trimmed.strip_prefix("## ").unwrap_or(trimmed).trim();
println!("\n\x1b[1m{}\x1b[0m", subheading);
} else if trimmed.starts_with("**") && trimmed.ends_with("**") {
// Bold text (section marker)
let text = trimmed.trim_start_matches("**").trim_end_matches("**");
println!("{}\x1b[1m{}\x1b[0m", indent, text);
} else {
// Regular text or note
println!("{}{}", indent, trimmed);
} }
} }
} }
@@ -53,6 +108,15 @@ impl UiWriter for ConsoleUiWriter {
// Store the tool name and clear args for collection // Store the tool name and clear args for collection
*self.current_tool_name.lock().unwrap() = Some(tool_name.to_string()); *self.current_tool_name.lock().unwrap() = Some(tool_name.to_string());
self.current_tool_args.lock().unwrap().clear(); self.current_tool_args.lock().unwrap().clear();
// Check if this is a todo tool call
let is_todo = tool_name == "todo_read" || tool_name == "todo_write";
*self.in_todo_tool.lock().unwrap() = is_todo;
// For todo tools, we'll skip the normal header and print a custom one later
if is_todo {
return;
}
} }
fn print_tool_arg(&self, key: &str, value: &str) { fn print_tool_arg(&self, key: &str, value: &str) {
@@ -75,6 +139,12 @@ impl UiWriter for ConsoleUiWriter {
} }
fn print_tool_output_header(&self) { fn print_tool_output_header(&self) {
// Skip normal header for todo tools
if *self.in_todo_tool.lock().unwrap() {
println!(); // Just add a newline
return;
}
println!(); println!();
// Now print the tool header with the most important arg in bold green // Now print the tool header with the most important arg in bold green
if let Some(tool_name) = self.current_tool_name.lock().unwrap().as_ref() { if let Some(tool_name) = self.current_tool_name.lock().unwrap().as_ref() {
@@ -144,10 +214,21 @@ impl UiWriter for ConsoleUiWriter {
} }
fn print_tool_output_line(&self, line: &str) { fn print_tool_output_line(&self, line: &str) {
// Special handling for todo tools
if *self.in_todo_tool.lock().unwrap() {
self.print_todo_line(line);
return;
}
println!("\x1b[2m{}\x1b[0m", line); println!("\x1b[2m{}\x1b[0m", line);
} }
fn print_tool_output_summary(&self, count: usize) { fn print_tool_output_summary(&self, count: usize) {
// Skip for todo tools
if *self.in_todo_tool.lock().unwrap() {
return;
}
println!( println!(
"\x1b[2m({} line{})\x1b[0m", "\x1b[2m({} line{})\x1b[0m",
count, count,
@@ -156,6 +237,13 @@ impl UiWriter for ConsoleUiWriter {
} }
fn print_tool_timing(&self, duration_str: &str) { fn print_tool_timing(&self, duration_str: &str) {
// For todo tools, just print a simple completion message
if *self.in_todo_tool.lock().unwrap() {
println!();
*self.in_todo_tool.lock().unwrap() = false;
return;
}
// Parse the duration string to determine color // Parse the duration string to determine color
// Format is like "1.5s", "500ms", "2m 30.0s" // Format is like "1.5s", "500ms", "2m 30.0s"
let color_code = if duration_str.ends_with("ms") { let color_code = if duration_str.ends_with("ms") {

View File

@@ -776,27 +776,6 @@ impl<W: UiWriter> Agent<W> {
// For native tool calling providers, use a more explicit system prompt // For native tool calling providers, use a more explicit system prompt
"You are G3, an AI programming agent of the same skill level as a seasoned engineer at a major technology company. You analyze given tasks and write code to achieve goals. "You are G3, an AI programming agent of the same skill level as a seasoned engineer at a major technology company. You analyze given tasks and write code to achieve goals.
# Task Management
Use todo_read and todo_write for tasks with 3+ steps, multiple files/components, or uncertain scope.
Workflow:
- Start: read → write checklist
- During: read → update progress
- End: verify all complete
Warning: todo_write overwrites entirely; always todo_read first (skipping is an error)
Keep items short, specific, action-oriented. Not using the todo tools for complex tasks is an error.
Template:
- [ ] Implement feature X
- [ ] Update API
- [ ] Write tests
- [ ] Run tests
- [ ] Run lint
- [ ] Blocked: waiting on credentials
You have access to tools. When you need to accomplish a task, you MUST use the appropriate tool. Do not just describe what you would do - actually use the tools. You have access to tools. When you need to accomplish a task, you MUST use the appropriate tool. Do not just describe what you would do - actually use the tools.
IMPORTANT: You must call tools to achieve goals. When you receive a request: IMPORTANT: You must call tools to achieve goals. When you receive a request:
@@ -815,18 +794,9 @@ When taking screenshots of specific windows (like \"my Safari window\" or \"my t
Do not explain what you're going to do - just do it by calling the tools. Do not explain what you're going to do - just do it by calling the tools.
# Response Guidelines
- Use Markdown formatting for all responses except tool calls.
- Whenever taking actions, use the pronoun 'I'
".to_string()
} else {
// For non-native providers (embedded models), use JSON format instructions
"You are G3, a general-purpose AI agent. Your goal is to analyze and solve problems by writing code.
# Task Management # Task Management
Use todo_read and todo_write for tasks with 3+ steps, multiple files/components, or uncertain scope. Use todo_read and todo_write for tasks with 2+ steps, multiple files/components, or uncertain scope.
Workflow: Workflow:
- Start: read → write checklist - Start: read → write checklist
@@ -841,6 +811,21 @@ Template:
- [ ] Implement feature X - [ ] Implement feature X
- [ ] Update API - [ ] Update API
- [ ] Write tests - [ ] Write tests
- [ ] Run tests
- [ ] Run lint
- [ ] Blocked: waiting on credentials
# Response Guidelines
- Use Markdown formatting for all responses except tool calls.
- Whenever taking actions, use the pronoun 'I'
".to_string()
} else {
// For non-native providers (embedded models), use JSON format instructions
"You are G3, a general-purpose AI agent. Your goal is to analyze and solve problems by writing code.
You have access to tools. When you need to accomplish a task, you MUST use the appropriate tool. Do not just describe what you would do - actually use the tools.
# Tool Call Format # Tool Call Format
@@ -887,6 +872,24 @@ The tool will execute immediately and you'll receive the result (success or erro
3. STOP when the original request was satisfied 3. STOP when the original request was satisfied
4. Call the final_output tool when done 4. Call the final_output tool when done
# Task Management
Use todo_read and todo_write for tasks with 3+ steps, multiple files/components, or uncertain scope.
Workflow:
- Start: read → write checklist
- During: read → update progress
- End: verify all complete
Warning: todo_write overwrites entirely; always todo_read first (skipping is an error)
Keep items short, specific, action-oriented. Not using the todo tools for complex tasks is an error.
Template:
- [ ] Implement feature X
- [ ] Update API
- [ ] Write tests
# Response Guidelines # Response Guidelines
- Use Markdown formatting for all responses except tool calls. - Use Markdown formatting for all responses except tool calls.
@@ -1813,14 +1816,21 @@ The tool will execute immediately and you'll receive the result (success or erro
const MAX_LINES: usize = 5; const MAX_LINES: usize = 5;
const MAX_LINE_WIDTH: usize = 80; const MAX_LINE_WIDTH: usize = 80;
let output_len = output_lines.len(); let output_len = output_lines.len();
// For todo tools, show all lines without truncation
let is_todo_tool = tool_call.tool == "todo_read" || tool_call.tool == "todo_write";
let max_lines_to_show = if is_todo_tool { output_len } else { MAX_LINES };
for line in output_lines { for (idx, line) in output_lines.iter().enumerate() {
if !is_todo_tool && idx >= max_lines_to_show {
break;
}
// Clip line to max width // Clip line to max width
let clipped_line = truncate_line(line, MAX_LINE_WIDTH); let clipped_line = truncate_line(line, MAX_LINE_WIDTH);
self.ui_writer.update_tool_output_line(&clipped_line); self.ui_writer.update_tool_output_line(&clipped_line);
} }
if output_len > MAX_LINES { if !is_todo_tool && output_len > MAX_LINES {
self.ui_writer.print_tool_output_summary(output_len); self.ui_writer.print_tool_output_summary(output_len);
} }
} }