use async_trait::async_trait;
use bashkit::clap::Parser;
use bashkit::{BashkitContext, ClapBuiltin, ScriptedTool, Tool, ToolArgs, ToolDef};
#[derive(Parser)]
#[command(name = "get_agent", about = "Fetch one agent by id")]
struct GetAgentArgs {
id: String,
#[arg(short, long, default_value = "text")]
format: String,
}
struct GetAgent;
#[async_trait]
impl ClapBuiltin for GetAgent {
type Args = GetAgentArgs;
async fn execute_clap(
&self,
args: Self::Args,
ctx: &mut BashkitContext<'_>,
) -> bashkit::Result<()> {
ctx.write_stdout(format!("agent={} format={}\n", args.id, args.format));
Ok(())
}
}
async fn run(tool: &ScriptedTool, commands: &str) -> anyhow::Result<(String, String, i64)> {
let output = tool
.execution(serde_json::json!({ "commands": commands }))?
.execute()
.await?;
let result = &output.result;
Ok((
result["stdout"].as_str().unwrap_or_default().to_string(),
result["stderr"].as_str().unwrap_or_default().to_string(),
result["exit_code"].as_i64().unwrap_or_default(),
))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let tool = ScriptedTool::builder("control_plane")
.short_description("Control-plane command tree")
.builtin("get_agent", Box::new(GetAgent))
.tool_fn(
ToolDef::new("list_agents", "List agents").with_schema(serde_json::json!({
"type": "object",
"properties": { "limit": { "type": "integer" } },
"additionalProperties": false
})),
|args: &ToolArgs| Ok(format!("limit={}\n", args.param_i64("limit").unwrap_or(10))),
)
.build();
println!("=== clap builtin inside a ScriptedTool ===\n");
let (stdout, _, code) = run(&tool, "get_agent a-42").await?;
println!("$ get_agent a-42\n{stdout}");
assert_eq!(code, 0);
assert_eq!(stdout, "agent=a-42 format=text\n");
let (stdout, _, _) = run(&tool, "get_agent a-42 -f json").await?;
println!("$ get_agent a-42 -f json\n{stdout}");
assert_eq!(stdout, "agent=a-42 format=json\n");
let (_, stderr, code) = run(&tool, "get_agent").await?;
println!("$ get_agent\n{stderr}");
assert_ne!(code, 0);
assert!(stderr.contains("Usage: get_agent"));
assert!(!stderr.contains('\u{1b}'), "no ANSI in tool output");
let (stdout, _, _) = run(
&tool,
r#"for id in a-1 a-2; do get_agent "$id" -f json; done; list_agents --limit 2"#,
)
.await?;
println!("$ <loop over get_agent, then list_agents>\n{stdout}");
assert_eq!(
stdout,
"agent=a-1 format=json\nagent=a-2 format=json\nlimit=2\n"
);
let (_, stderr, code) = run(&tool, "list_agents --limti 2").await?;
println!("$ list_agents --limti 2\n{stderr}");
assert_ne!(code, 0);
assert!(stderr.contains("unknown flag"));
let (_, stderr, code) = run(&tool, "cat /etc/passwd").await?;
println!("$ cat /etc/passwd\n{stderr}");
assert_ne!(code, 0);
println!("All assertions passed.");
Ok(())
}