- 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.
161 lines
5.2 KiB
Rust
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)),
|
|
}
|
|
}
|