use super::error::Result;
use super::r#trait::{Tool, ToolCapability, ToolExecutionContext, ToolResult};
use crate::brain::SelfUpdater;
use crate::brain::agent::{ProgressCallback, ProgressEvent};
use async_trait::async_trait;
use serde_json::Value;
pub struct RebuildTool {
progress: Option<ProgressCallback>,
}
impl RebuildTool {
pub fn new(progress: Option<ProgressCallback>) -> Self {
Self { progress }
}
}
#[async_trait]
impl Tool for RebuildTool {
fn name(&self) -> &str {
"rebuild"
}
fn description(&self) -> &str {
"Build OpenCrabs from source (cargo build --release) and signal the TUI to hot-restart. \
Call this after editing source code to apply your changes. On success the binary is \
exec()-replaced automatically (no prompt). On failure the compiler output is returned."
}
fn input_schema(&self) -> Value {
serde_json::json!({
"type": "object",
"properties": {},
"required": []
})
}
fn capabilities(&self) -> Vec<ToolCapability> {
vec![ToolCapability::SystemModification]
}
async fn execute(&self, _input: Value, context: &ToolExecutionContext) -> Result<ToolResult> {
let updater = match SelfUpdater::auto_detect() {
Ok(u) => u,
Err(e) => {
return Ok(ToolResult::error(format!(
"Cannot detect project root: {}",
e
)));
}
};
let cb = self.progress.clone();
let sid = context.session_id;
let result = updater
.build_streaming(move |line| {
let trimmed = line.trim();
if (trimmed.starts_with("Compiling")
|| trimmed.starts_with("Finished")
|| trimmed.starts_with("error")
|| trimmed.starts_with("warning[")
|| trimmed.starts_with("-->"))
&& let Some(ref cb) = cb
{
cb(sid, ProgressEvent::BuildLine(line));
}
})
.await;
match result {
Ok(path) => {
if let Some(ref cb) = self.progress {
cb(
sid,
ProgressEvent::RestartReady {
status: format!("Build successful: {}", path.display()),
},
);
}
Ok(ToolResult::success(format!(
"Build successful: {}. Restarting now.",
path.display()
)))
}
Err(output) => Ok(ToolResult::error(format!("Build failed:\n{}", output))),
}
}
}