feat(cli): add register-agent and list-agents commands #82

Merged
clawdie merged 1 commit from feat/cli-register-agent into main 2026-06-19 21:26:54 +02:00
2 changed files with 64 additions and 0 deletions

View file

@ -48,6 +48,11 @@ enum Command {
description: Option<String>,
category: Option<String>,
},
RegisterAgent {
name: String,
capabilities: Vec<String>,
},
ListAgents,
}
fn default_socket_path() -> PathBuf {
@ -81,6 +86,8 @@ Examples:
colibri list-tasks --status queued
colibri register-skill freebsd-check --description "Live USB startup check" --category freebsd
colibri list-skills
colibri register-agent NAME [--capability CAP]... [--capabilities CSV]
colibri list-agents
"#
}
@ -166,6 +173,7 @@ where
})
}
"list-skills" => expect_arity(&args, 1).map(|()| Command::ListSkills),
"list-agents" => expect_arity(&args, 1).map(|()| Command::ListAgents),
"register-skill" => {
if args.len() < 2 {
Err("register-skill requires NAME\n\n".to_string() + usage())
@ -178,6 +186,17 @@ where
})
}
}
"register-agent" => {
if args.len() < 2 {
Err("register-agent requires NAME\n\n".to_string() + usage())
} else {
let capabilities = parse_capabilities(&args[2..])?;
Ok(Command::RegisterAgent {
name: args[1].clone(),
capabilities,
})
}
}
other => Err(format!("unknown command: {other}\n\n{}", usage())),
}?;
@ -328,6 +347,31 @@ fn parse_spawn_options(args: &[String]) -> Result<(Option<String>, Option<String
Ok((session_id, system_prompt))
}
fn parse_capabilities(args: &[String]) -> Result<Vec<String>, String> {
let mut caps = Vec::new();
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"--capability" => {
let Some(value) = args.get(i + 1) else {
return Err("--capability requires CAP\n\n".to_string() + usage());
};
caps.push(value.clone());
i += 2;
}
"--capabilities" => {
let Some(value) = args.get(i + 1) else {
return Err("--capabilities requires CSV\n\n".to_string() + usage());
};
caps.extend(value.split(',').map(str::trim).filter(|c| !c.is_empty()).map(ToString::to_string));
i += 2;
}
other => return Err(format!("unknown register-agent option: {other}\n\n{}", usage())),
}
}
Ok(caps)
}
fn parse_skill_options(args: &[String]) -> Result<(Option<String>, Option<String>), String> {
let mut description = None;
let mut category = None;
@ -395,6 +439,10 @@ async fn run(options: Options) -> Result<(), ClientError> {
description,
category,
} => print_json(&client.register_skill(name, description, category).await?),
Command::RegisterAgent { name, capabilities } => {
print_json(&client.register_agent(name, capabilities).await?)
}
Command::ListAgents => print_json(&client.list_agents().await?),
}
}

View file

@ -181,6 +181,22 @@ impl DaemonClient {
self.request(&ColibriCommand::ListSkills).await
}
pub async fn register_agent(
&self,
name: impl Into<String>,
capabilities: Vec<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&ColibriCommand::RegisterAgent {
name: name.into(),
capabilities: Some(serde_json::to_value(capabilities).unwrap_or_default()),
})
.await
}
pub async fn list_agents(&self) -> Result<serde_json::Value, ClientError> {
self.request(&ColibriCommand::ListAgents).await
}
pub async fn register_skill(
&self,
name: impl Into<String>,