Files
g3/crates/g3-core/src/tools/misc.rs
Dhanji R. Prasanna 777191b3cb Remove final_output tool - let summaries stream naturally
- Remove final_output from tool definitions, dispatch, and misc tools
- Update system prompts to request summaries as regular markdown text
- Remove print_final_output from UiWriter trait and all implementations
- Remove final_output handling from agent core logic
- Rename final_output_summary → summary in session continuation
- Delete final_output test files
- Update tool count tests (12→11, 27→26)

This allows LLM summaries to stream through the markdown formatter
for a more natural, responsive user experience instead of buffering
everything into a tool call.
2026-01-09 14:57:24 +11:00

161 lines
5.2 KiB
Rust

//! Miscellaneous tools: take_screenshot, code_coverage, code_search.
use anyhow::Result;
use tracing::debug;
use crate::ui_writer::UiWriter;
use crate::ToolCall;
use super::executor::ToolContext;
/// Execute the `take_screenshot` tool.
pub async fn execute_take_screenshot<W: UiWriter>(
tool_call: &ToolCall,
ctx: &ToolContext<'_, W>,
) -> Result<String> {
debug!("Processing take_screenshot tool call");
let controller = match ctx.computer_controller {
Some(c) => c,
None => {
return Ok(
"❌ Computer control not enabled. Set computer_control.enabled = true in config."
.to_string(),
)
}
};
let path = tool_call
.args
.get("path")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing path argument"))?;
// Extract window_id (app name) - REQUIRED
let window_id = tool_call
.args
.get("window_id")
.and_then(|v| v.as_str())
.ok_or_else(|| {
anyhow::anyhow!(
"Missing window_id argument. You must specify which window to capture \
(e.g., 'Safari', 'Terminal', 'Google Chrome')."
)
})?;
// Extract region if provided
let region = tool_call
.args
.get("region")
.and_then(|v| v.as_object())
.map(|region_obj| g3_computer_control::types::Rect {
x: region_obj.get("x").and_then(|v| v.as_i64()).unwrap_or(0) as i32,
y: region_obj.get("y").and_then(|v| v.as_i64()).unwrap_or(0) as i32,
width: region_obj
.get("width")
.and_then(|v| v.as_i64())
.unwrap_or(0) as i32,
height: region_obj
.get("height")
.and_then(|v| v.as_i64())
.unwrap_or(0) as i32,
});
match controller.take_screenshot(path, region, Some(window_id)).await {
Ok(_) => {
// Get the actual path where the screenshot was saved
let actual_path = if path.starts_with('/') {
path.to_string()
} else {
let temp_dir = std::env::var("TMPDIR")
.or_else(|_| std::env::var("HOME").map(|h| format!("{}/tmp", h)))
.unwrap_or_else(|_| "/tmp".to_string());
format!("{}/{}", temp_dir.trim_end_matches('/'), path)
};
Ok(format!(
"✅ Screenshot of {} saved to: {}",
window_id, actual_path
))
}
Err(e) => Ok(format!("❌ Failed to take screenshot: {}", e)),
}
}
/// Execute the `code_coverage` tool.
pub async fn execute_code_coverage<W: UiWriter>(
tool_call: &ToolCall,
ctx: &ToolContext<'_, W>,
) -> Result<String> {
debug!("Processing code_coverage tool call");
let _ = tool_call; // unused
ctx.ui_writer
.print_context_status("🔍 Generating code coverage report...");
// Ensure coverage tools are installed
match g3_execution::ensure_coverage_tools_installed() {
Ok(already_installed) => {
if !already_installed {
ctx.ui_writer
.print_context_status("✅ Coverage tools installed successfully");
}
}
Err(e) => {
return Ok(format!("❌ Failed to install coverage tools: {}", e));
}
}
// Run cargo llvm-cov --workspace
let output = std::process::Command::new("cargo")
.args(["llvm-cov", "--workspace"])
.current_dir(std::env::current_dir()?)
.output()?;
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let mut result = String::from("✅ Code coverage report generated successfully\n\n");
result.push_str("## Coverage Summary\n");
result.push_str(&stdout);
if !stderr.is_empty() {
result.push_str("\n## Warnings\n");
result.push_str(&stderr);
}
Ok(result)
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
Ok(format!("❌ Failed to generate coverage report:\n{}", stderr))
}
}
/// Execute the `code_search` tool.
pub async fn execute_code_search<W: UiWriter>(
tool_call: &ToolCall,
_ctx: &ToolContext<'_, W>,
) -> Result<String> {
debug!("Processing code_search tool call");
// Parse the request
let request: crate::code_search::CodeSearchRequest =
match serde_json::from_value(tool_call.args.clone()) {
Ok(req) => req,
Err(e) => {
return Ok(format!("❌ Invalid code_search arguments: {}", e));
}
};
// Execute the code search
match crate::code_search::execute_code_search(request).await {
Ok(response) => {
// Serialize the response to JSON
match serde_json::to_string_pretty(&response) {
Ok(json_output) => Ok(format!("✅ Code search completed\n{}", json_output)),
Err(e) => Ok(format!("❌ Failed to serialize response: {}", e)),
}
}
Err(e) => Ok(format!("❌ Code search failed: {}", e)),
}
}