Add compact format for remember, take_screenshot, code_coverage, rehydrate
Extend compact single-line output to additional tools: - remember: shows '📝 memory updated (size)' - take_screenshot: shows '📸 path' - code_coverage: shows '📊 report generated' - rehydrate: shows '🔄 restored fragment_id' Tools without file_path argument use simplified format: ● tool_name | summary | tokens ◉ time
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# Project Memory
|
# Project Memory
|
||||||
> Updated: 2026-01-12T05:55:00Z | Size: 10.3k chars
|
> Updated: 2026-01-12T09:14:13Z | Size: 10.5k chars
|
||||||
|
|
||||||
### Remember Tool Wiring
|
### Remember Tool Wiring
|
||||||
- `crates/g3-core/src/tools/memory.rs` [0..5000] - `execute_remember()`, `get_memory_path()`, `merge_memory()`
|
- `crates/g3-core/src/tools/memory.rs` [0..5000] - `execute_remember()`, `get_memory_path()`, `merge_memory()`
|
||||||
@@ -176,3 +176,7 @@ if s.chars().count() <= max_len { ... }
|
|||||||
- Previously was at `.g3/memory.md` (gitignored, ephemeral)
|
- Previously was at `.g3/memory.md` (gitignored, ephemeral)
|
||||||
- `crates/g3-core/src/tools/memory.rs` - `get_memory_path()` returns `analysis/memory.md`
|
- `crates/g3-core/src/tools/memory.rs` - `get_memory_path()` returns `analysis/memory.md`
|
||||||
- `crates/g3-cli/src/project_files.rs` - `read_project_memory()` reads from `analysis/memory.md`
|
- `crates/g3-cli/src/project_files.rs` - `read_project_memory()` reads from `analysis/memory.md`
|
||||||
|
|
||||||
|
### Compact Tool Output
|
||||||
|
- `crates/g3-cli/src/ui_writer_impl.rs` - `print_tool_compact()` handles compact display for file ops and other tools
|
||||||
|
- `crates/g3-core/src/streaming.rs` - `format_*_summary()` functions for each tool type
|
||||||
@@ -266,8 +266,8 @@ impl UiWriter for ConsoleUiWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn print_tool_compact(&self, tool_name: &str, summary: &str, duration_str: &str, tokens_delta: u32, _context_percentage: f32) -> bool {
|
fn print_tool_compact(&self, tool_name: &str, summary: &str, duration_str: &str, tokens_delta: u32, _context_percentage: f32) -> bool {
|
||||||
// Only handle file operation tools in compact format
|
// Handle file operation tools and other compact tools
|
||||||
let is_compact_tool = matches!(tool_name, "read_file" | "write_file" | "str_replace");
|
let is_compact_tool = matches!(tool_name, "read_file" | "write_file" | "str_replace" | "remember" | "take_screenshot" | "code_coverage" | "rehydrate");
|
||||||
if !is_compact_tool {
|
if !is_compact_tool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -275,23 +275,29 @@ impl UiWriter for ConsoleUiWriter {
|
|||||||
let args = self.current_tool_args.lock().unwrap();
|
let args = self.current_tool_args.lock().unwrap();
|
||||||
let is_agent_mode = *self.is_agent_mode.lock().unwrap();
|
let is_agent_mode = *self.is_agent_mode.lock().unwrap();
|
||||||
|
|
||||||
// Get file path
|
// Get file path (for file operation tools)
|
||||||
let file_path = args
|
let file_path = args
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(k, _)| k == "file_path")
|
.find(|(k, _)| k == "file_path")
|
||||||
.map(|(_, v)| v.as_str())
|
.map(|(_, v)| v.as_str())
|
||||||
.unwrap_or("?");
|
.unwrap_or("");
|
||||||
|
|
||||||
// Truncate long paths
|
// For tools without file_path, get other relevant args
|
||||||
let display_path = if file_path.len() > 60 {
|
let display_arg = if file_path.is_empty() {
|
||||||
let truncate_at = file_path
|
// For remember, take_screenshot, etc. - no path to show
|
||||||
.char_indices()
|
String::new()
|
||||||
.nth(57)
|
|
||||||
.map(|(i, _)| i)
|
|
||||||
.unwrap_or(file_path.len());
|
|
||||||
format!("{}...", &file_path[..truncate_at])
|
|
||||||
} else {
|
} else {
|
||||||
file_path.to_string()
|
// Truncate long paths
|
||||||
|
if file_path.len() > 60 {
|
||||||
|
let truncate_at = file_path
|
||||||
|
.char_indices()
|
||||||
|
.nth(57)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap_or(file_path.len());
|
||||||
|
format!("{}", &file_path[..truncate_at])
|
||||||
|
} else {
|
||||||
|
file_path.to_string()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build range suffix for read_file
|
// Build range suffix for read_file
|
||||||
@@ -320,18 +326,30 @@ impl UiWriter for ConsoleUiWriter {
|
|||||||
// Color for tool name
|
// Color for tool name
|
||||||
let tool_color = if is_agent_mode { "\x1b[38;5;250m" } else { "\x1b[32m" };
|
let tool_color = if is_agent_mode { "\x1b[38;5;250m" } else { "\x1b[32m" };
|
||||||
|
|
||||||
// Print compact single line:
|
// Print compact single line - different format for tools with/without path
|
||||||
// " ● read_file | path [range] | summary | tokens ◉ time"
|
if display_arg.is_empty() {
|
||||||
println!(
|
// Tools without file path: " ● tool_name | summary | tokens ◉ time"
|
||||||
" \x1b[2m●\x1b[0m {}{} \x1b[2m|\x1b[0m \x1b[35m{}{}\x1b[0m \x1b[2m| {}\x1b[0m \x1b[2m| {} ◉ {}\x1b[0m",
|
println!(
|
||||||
tool_color,
|
" \x1b[2m●\x1b[0m {}{} \x1b[2m| {}\x1b[0m \x1b[2m| {} ◉ {}\x1b[0m",
|
||||||
tool_name,
|
tool_color,
|
||||||
display_path,
|
tool_name,
|
||||||
range_suffix,
|
summary,
|
||||||
summary,
|
tokens_delta,
|
||||||
tokens_delta,
|
duration_str
|
||||||
duration_str
|
);
|
||||||
);
|
} else {
|
||||||
|
// Tools with file path: " ● tool_name | path [range] | summary | tokens ◉ time"
|
||||||
|
println!(
|
||||||
|
" \x1b[2m●\x1b[0m {}{} \x1b[2m|\x1b[0m \x1b[35m{}{}\x1b[0m \x1b[2m| {}\x1b[0m \x1b[2m| {} ◉ {}\x1b[0m",
|
||||||
|
tool_color,
|
||||||
|
tool_name,
|
||||||
|
display_arg,
|
||||||
|
range_suffix,
|
||||||
|
summary,
|
||||||
|
tokens_delta,
|
||||||
|
duration_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear the stored tool info
|
// Clear the stored tool info
|
||||||
drop(args); // Release the lock before clearing
|
drop(args); // Release the lock before clearing
|
||||||
|
|||||||
@@ -2144,7 +2144,7 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a compact tool (file operations)
|
// Check if this is a compact tool (file operations)
|
||||||
let is_compact_tool = matches!(tool_call.tool.as_str(), "read_file" | "write_file" | "str_replace");
|
let is_compact_tool = matches!(tool_call.tool.as_str(), "read_file" | "write_file" | "str_replace" | "remember" | "take_screenshot" | "code_coverage" | "rehydrate");
|
||||||
|
|
||||||
// Only print output header for non-compact tools
|
// Only print output header for non-compact tools
|
||||||
if !is_compact_tool {
|
if !is_compact_tool {
|
||||||
@@ -2208,7 +2208,23 @@ impl<W: UiWriter> Agent<W> {
|
|||||||
let (ins, del) = parse_diff_stats(&tool_result);
|
let (ins, del) = parse_diff_stats(&tool_result);
|
||||||
Some(streaming::format_str_replace_summary(ins, del))
|
Some(streaming::format_str_replace_summary(ins, del))
|
||||||
}
|
}
|
||||||
_ => Some(streaming::format_read_file_summary(output_len, tool_result.len()))
|
"remember" => {
|
||||||
|
// Extract size from result like "Memory updated. Size: 1.2k"
|
||||||
|
Some(streaming::format_remember_summary(&tool_result))
|
||||||
|
}
|
||||||
|
"take_screenshot" => {
|
||||||
|
// Extract path from result
|
||||||
|
Some(streaming::format_screenshot_summary(&tool_result))
|
||||||
|
}
|
||||||
|
"code_coverage" => {
|
||||||
|
// Show coverage summary
|
||||||
|
Some(streaming::format_coverage_summary(&tool_result))
|
||||||
|
}
|
||||||
|
"rehydrate" => {
|
||||||
|
// Show fragment info
|
||||||
|
Some(streaming::format_rehydrate_summary(&tool_result))
|
||||||
|
}
|
||||||
|
_ => Some(format!("✅ completed"))
|
||||||
}
|
}
|
||||||
} else if is_todo_tool {
|
} else if is_todo_tool {
|
||||||
// Skip - todo tools print their own content
|
// Skip - todo tools print their own content
|
||||||
|
|||||||
@@ -313,6 +313,58 @@ pub fn format_str_replace_summary(insertions: i32, deletions: i32) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format a remember tool result summary.
|
||||||
|
pub fn format_remember_summary(result: &str) -> String {
|
||||||
|
// Result format: "Memory updated. Size: 1.2k" or similar
|
||||||
|
if let Some(size_pos) = result.find("Size: ") {
|
||||||
|
let size_str = &result[size_pos + 6..];
|
||||||
|
let size = size_str.split_whitespace().next().unwrap_or("?");
|
||||||
|
format!("📝 memory updated ({})", size)
|
||||||
|
} else {
|
||||||
|
"📝 memory updated".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a take_screenshot result summary.
|
||||||
|
pub fn format_screenshot_summary(result: &str) -> String {
|
||||||
|
// Result format: "✅ Screenshot of X saved to: /path/to/file.png"
|
||||||
|
if let Some(path_pos) = result.find("saved to: ") {
|
||||||
|
let path = &result[path_pos + 10..].trim();
|
||||||
|
format!("📸 {}", path)
|
||||||
|
} else if result.contains("❌") {
|
||||||
|
"❌ failed".to_string()
|
||||||
|
} else {
|
||||||
|
"📸 saved".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a code_coverage result summary.
|
||||||
|
pub fn format_coverage_summary(result: &str) -> String {
|
||||||
|
// Try to extract coverage percentage from result
|
||||||
|
if result.contains("❌") {
|
||||||
|
"❌ failed".to_string()
|
||||||
|
} else {
|
||||||
|
"📊 report generated".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format a rehydrate result summary.
|
||||||
|
pub fn format_rehydrate_summary(result: &str) -> String {
|
||||||
|
// Result format: "✅ Rehydrated fragment 'abc123' (47 messages, ~18500 tokens)"
|
||||||
|
if let Some(start) = result.find("fragment '") {
|
||||||
|
let after = &result[start + 10..];
|
||||||
|
if let Some(end) = after.find("'") {
|
||||||
|
let fragment_id = &after[..end];
|
||||||
|
return format!("🔄 restored '{}'", fragment_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if result.contains("❌") {
|
||||||
|
"❌ failed".to_string()
|
||||||
|
} else {
|
||||||
|
"🔄 restored".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine if a response is essentially empty (whitespace or timing only)
|
/// Determine if a response is essentially empty (whitespace or timing only)
|
||||||
pub fn is_empty_response(response: &str) -> bool {
|
pub fn is_empty_response(response: &str) -> bool {
|
||||||
response.trim().is_empty()
|
response.trim().is_empty()
|
||||||
|
|||||||
Reference in New Issue
Block a user