use claude_agent_sdk::commands::{CommandError, CommandRegistry, SlashCommand};
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== Slash Commands System Examples ===\n");
basic_commands_example().await?;
command_with_arguments_example().await?;
error_handling_example().await?;
registry_management_example().await?;
Ok(())
}
async fn basic_commands_example() -> anyhow::Result<()> {
println!("=== Basic Commands Example ===\n");
let mut registry = CommandRegistry::new();
println!("Created empty registry: {} commands", registry.len());
let greet_cmd = SlashCommand::new(
"greet",
"Greet the user",
Arc::new(|_name, args| {
Box::pin(async move {
let name = args.first().map(|s| s.as_str()).unwrap_or("World");
Ok(format!("Hello, {}!", name))
})
}),
);
registry.register(greet_cmd)?;
println!("Registered 'greet' command");
let echo_cmd = SlashCommand::new(
"echo",
"Echo back the provided arguments",
Arc::new(|_name, args| {
Box::pin(async move { Ok(args.join(" ")) })
}),
);
registry.register(echo_cmd)?;
println!("Registered 'echo' command");
let time_cmd = SlashCommand::new(
"time",
"Get current time",
Arc::new(|_name, _args| {
Box::pin(async move {
let now = chrono::Utc::now();
Ok(format!("Current time: {}", now.format("%Y-%m-%d %H:%M:%S UTC")))
})
}),
);
registry.register(time_cmd)?;
println!("Registered 'time' command");
println!("\n--- Executing Commands ---");
let result = registry.execute("greet", vec!["Claude".to_string()]).await?;
println!("$ /greet Claude");
println!("> {}", result);
let result = registry.execute("echo", vec!["Hello".to_string(), "World".to_string()]).await?;
println!("$ /echo Hello World");
println!("> {}", result);
let result = registry.execute("time", vec![]).await?;
println!("$ /time");
println!("> {}", result);
println!();
Ok(())
}
async fn command_with_arguments_example() -> anyhow::Result<()> {
println!("=== Commands with Arguments Example ===\n");
let mut registry = CommandRegistry::new();
let sum_cmd = SlashCommand::new(
"sum",
"Calculate the sum of numbers",
Arc::new(|_name, args| {
Box::pin(async move {
let numbers: Vec<i64> = args.iter().filter_map(|s| s.parse().ok()).collect();
let total: i64 = numbers.iter().sum();
Ok(format!(
"Sum of {:?}: {}",
numbers, total
))
})
}),
);
registry.register(sum_cmd)?;
let stats_cmd = SlashCommand::new(
"stats",
"Calculate statistics for numbers",
Arc::new(|_name, args| {
Box::pin(async move {
let numbers: Vec<f64> = args.iter().filter_map(|s| s.parse().ok()).collect();
if numbers.is_empty() {
return Ok("No valid numbers provided".to_string());
}
let count = numbers.len();
let sum: f64 = numbers.iter().sum();
let avg = sum / count as f64;
let min = numbers.iter().cloned().fold(f64::INFINITY, f64::min);
let max = numbers.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
Ok(format!(
"Statistics: count={}, sum={:.2}, avg={:.2}, min={:.2}, max={:.2}",
count, sum, avg, min, max
))
})
}),
);
registry.register(stats_cmd)?;
let repeat_cmd = SlashCommand::new(
"repeat",
"Repeat text N times (usage: /repeat <count> <text>)",
Arc::new(|_name, args| {
Box::pin(async move {
if args.len() < 2 {
return Err(CommandError::ExecutionFailed(
"Usage: /repeat <count> <text>".to_string(),
));
}
let count: usize = args[0].parse().map_err(|_| {
CommandError::ExecutionFailed("Invalid count".to_string())
})?;
let text = args[1..].join(" ");
let result = vec![text.as_str(); count].join("\n");
Ok(result)
})
}),
);
registry.register(repeat_cmd)?;
println!("--- Executing Commands with Arguments ---");
let result = registry.execute("sum", vec!["10".to_string(), "20".to_string(), "30".to_string()]).await?;
println!("$ /sum 10 20 30");
println!("> {}", result);
let result = registry.execute("stats", vec!["1".to_string(), "2".to_string(), "3".to_string(), "4".to_string(), "5".to_string()]).await?;
println!("$ /stats 1 2 3 4 5");
println!("> {}", result);
let result = registry.execute("repeat", vec!["3".to_string(), "Hello!".to_string()]).await?;
println!("$ /repeat 3 Hello!");
println!("> {}", result);
println!();
Ok(())
}
async fn error_handling_example() -> anyhow::Result<()> {
println!("=== Error Handling Example ===\n");
let mut registry = CommandRegistry::new();
let fail_cmd = SlashCommand::new(
"fail",
"A command that always fails",
Arc::new(|_name, _args| {
Box::pin(async move {
Err(CommandError::ExecutionFailed(
"This command always fails intentionally".to_string(),
))
})
}),
);
registry.register(fail_cmd)?;
match registry.execute("nonexistent", vec![]).await {
Err(CommandError::NotFound(name)) => {
println!("✓ Expected error: Command '{}' not found", name);
}
Err(e) => println!("Unexpected error: {}", e),
Ok(_) => println!("Unexpected success"),
}
match registry.execute("fail", vec![]).await {
Err(CommandError::ExecutionFailed(msg)) => {
println!("✓ Expected execution error: {}", msg);
}
Err(e) => println!("Unexpected error: {}", e),
Ok(_) => println!("Unexpected success"),
}
let dupe_cmd = SlashCommand::new(
"fail",
"Duplicate command",
Arc::new(|_name, _args| Box::pin(async { Ok(String::new()) })),
);
match registry.register(dupe_cmd) {
Err(CommandError::AlreadyRegistered(name)) => {
println!("✓ Expected error: Command '{}' already registered", name);
}
Err(e) => println!("Unexpected error: {}", e),
Ok(_) => println!("Unexpected success"),
}
let invalid_names = vec!["", "has space", "123starts", "test-cmd", "test_cmd"];
for name in invalid_names {
let cmd = SlashCommand::new(
name,
"Test",
Arc::new(|_name, _args| Box::pin(async { Ok(String::new()) })),
);
let result = registry.register(cmd);
if result.is_err() {
println!("✓ '{}' correctly rejected as invalid name", if name.is_empty() { "<empty>" } else { name });
} else {
println!("✓ '{}' accepted as valid name", name);
}
}
println!();
Ok(())
}
async fn registry_management_example() -> anyhow::Result<()> {
println!("=== Registry Management Example ===\n");
let mut registry = CommandRegistry::new();
let commands = vec![
("help", "Show available commands"),
("status", "Show system status"),
("version", "Show version info"),
("config", "Show configuration"),
("debug", "Toggle debug mode"),
];
for (name, desc) in commands {
let cmd = SlashCommand::new(
name,
desc,
Arc::new(move |_name, _args| {
let desc = desc.to_string();
Box::pin(async move { Ok(desc.to_string()) })
}),
);
registry.register(cmd)?;
}
println!("Registered {} commands", registry.len());
println!("Available commands: {:?}", registry.list_names());
println!("\nCommand existence check:");
println!(" 'help' exists: {}", registry.exists("help"));
println!(" 'unknown' exists: {}", registry.exists("unknown"));
if let Some(cmd) = registry.get("status") {
println!("\nCommand 'status':");
println!(" Name: {}", cmd.name);
println!(" Description: {}", cmd.description);
}
registry.unregister("debug")?;
println!("\nAfter unregistering 'debug':");
println!(" Commands: {:?}", registry.list_names());
registry.clear();
println!("\nAfter clearing:");
println!(" Commands: {:?}", registry.list_names());
println!(" Is empty: {}", registry.is_empty());
println!();
Ok(())
}