use crate::agent::run_default_agent;
use crate::session::analyze_agent_id;
use crate::tools::Tool;
use crate::{Role, Workspace};
use anyhow::Result;
use async_trait::async_trait;
use serde_json::json;
pub struct ImplementTool;
#[async_trait]
impl Tool for ImplementTool {
fn name(&self) -> &'static str {
"implement"
}
fn parameters_schema(&self) -> serde_json::Value {
super::tool_params_schema(
&json!({
"task": {
"type": "string",
"description": "The implementation task to delegate to the coder sub-agent"
}
}),
&["task"],
)
}
async fn execute(&self, ws: &Workspace, args: serde_json::Value) -> Result<String> {
let task = super::get_str(&args, "task")?;
let agent_id = analyze_agent_id(&ws.name, Role::Coder.as_str());
let parent_key = crate::agent::CURRENT_TOOL_PARENT_KEY
.try_with(std::clone::Clone::clone)
.unwrap_or(None);
let parent_label = crate::agent::CURRENT_TOOL_PARENT_LABEL
.try_with(std::clone::Clone::clone)
.unwrap_or(None);
let (agent, response) = run_default_agent(
&agent_id,
Role::Coder,
ws,
task,
None,
parent_key,
parent_label,
)
.await;
if let Some(response) = response {
Ok(response)
} else if agent.is_cancelled() || crate::shutdown::aborting() {
anyhow::bail!("Sub-agent cancelled");
} else {
anyhow::bail!(
"Sub-agent failed: {}",
agent.failure.as_deref().unwrap_or("unknown error")
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::workspace::test_ws;
use serde_json::json;
#[tokio::test]
async fn test_implement_missing_args() {
let tool = ImplementTool;
let ws = test_ws("/tmp/test_ws");
let result = tool.execute(&ws, json!({})).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Missing required field: task"),
"Should mention missing task"
);
}
}