diff --git a/crates/g3-core/src/lib.rs b/crates/g3-core/src/lib.rs index bc467b0..2d0cc9b 100644 --- a/crates/g3-core/src/lib.rs +++ b/crates/g3-core/src/lib.rs @@ -2158,6 +2158,15 @@ impl Agent { "required": ["content"] }), }, + Tool { + name: "code_coverage".to_string(), + description: "Generate a code coverage report for the entire workspace using cargo llvm-cov. This runs all tests with coverage instrumentation and returns a summary of coverage statistics. Requires llvm-tools-preview and cargo-llvm-cov to be installed (they will be auto-installed if missing).".to_string(), + input_schema: json!({ + "type": "object", + "properties": {}, + "required": [] + }), + }, ]; // Add code_search tool @@ -4352,6 +4361,46 @@ impl Agent { Ok("❌ Missing content argument".to_string()) } } + "code_coverage" => { + debug!("Processing code_coverage tool call"); + self.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 { + self.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); + + // Combine output + 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)) + } + } "webdriver_start" => { debug!("Processing webdriver_start tool call"); diff --git a/crates/g3-execution/examples/setup_coverage_tools.rs b/crates/g3-execution/examples/setup_coverage_tools.rs new file mode 100644 index 0000000..9495800 --- /dev/null +++ b/crates/g3-execution/examples/setup_coverage_tools.rs @@ -0,0 +1,13 @@ +use g3_execution::ensure_coverage_tools_installed; + +fn main() -> anyhow::Result<()> { + // Ensure coverage tools are installed + let already_installed = ensure_coverage_tools_installed()?; + + if already_installed { + println!("All coverage tools are already installed!"); + } else { + println!("Coverage tools have been installed successfully!"); + } + Ok(()) +} diff --git a/crates/g3-execution/src/lib.rs b/crates/g3-execution/src/lib.rs index 2a2e871..38e03d9 100644 --- a/crates/g3-execution/src/lib.rs +++ b/crates/g3-execution/src/lib.rs @@ -330,3 +330,87 @@ impl CodeExecutor { }) } } + +/// Check if rustup component llvm-tools-preview is installed +pub fn is_llvm_tools_installed() -> Result { + let output = Command::new("rustup") + .args(&["component", "list", "--installed"]) + .output()?; + + let installed = String::from_utf8_lossy(&output.stdout) + .lines() + .any(|line| line.trim() == "llvm-tools-preview" || line.starts_with("llvm-tools")); + + Ok(installed) +} + +/// Check if cargo-llvm-cov is installed +pub fn is_cargo_llvm_cov_installed() -> Result { + let output = Command::new("cargo") + .args(&["--list"]) + .output()?; + + let installed = String::from_utf8_lossy(&output.stdout) + .lines() + .any(|line| line.trim().starts_with("llvm-cov")); + + Ok(installed) +} + +/// Install llvm-tools-preview via rustup +pub fn install_llvm_tools() -> Result<()> { + info!("Installing llvm-tools-preview..."); + let output = Command::new("rustup") + .args(&["component", "add", "llvm-tools-preview"]) + .output()?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Failed to install llvm-tools-preview: {}", stderr); + } + + info!("✅ llvm-tools-preview installed successfully"); + Ok(()) +} + +/// Install cargo-llvm-cov via cargo install +pub fn install_cargo_llvm_cov() -> Result<()> { + info!("Installing cargo-llvm-cov... (this may take a few minutes)"); + let output = Command::new("cargo") + .args(&["install", "cargo-llvm-cov"]) + .output()?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Failed to install cargo-llvm-cov: {}", stderr); + } + + info!("✅ cargo-llvm-cov installed successfully"); + Ok(()) +} + +/// Ensure both llvm-tools-preview and cargo-llvm-cov are installed +/// Returns Ok(true) if tools were already installed, Ok(false) if they were installed by this function +pub fn ensure_coverage_tools_installed() -> Result { + let mut already_installed = true; + + // Check and install llvm-tools-preview + if !is_llvm_tools_installed()? { + info!("llvm-tools-preview not found, installing..."); + install_llvm_tools()?; + already_installed = false; + } else { + info!("✅ llvm-tools-preview is already installed"); + } + + // Check and install cargo-llvm-cov + if !is_cargo_llvm_cov_installed()? { + info!("cargo-llvm-cov not found, installing..."); + install_cargo_llvm_cov()?; + already_installed = false; + } else { + info!("✅ cargo-llvm-cov is already installed"); + } + + Ok(already_installed) +}