use claude_agents_sdk::{
ClaudeAgentOptions, ClaudeClient, ContentBlock, HookCallback, HookEvent, HookInput,
HookMatcher, HookOutput, Message, SyncHookOutput,
};
use std::collections::HashMap;
use std::env;
use std::sync::Arc;
#[allow(dead_code)]
fn display_message(msg: &Message) {
match msg {
Message::Assistant(asst) => {
for block in &asst.content {
if let ContentBlock::Text(text) = block {
println!("Claude: {}", text.text);
}
}
}
Message::Result(_) => {
println!("Result ended");
}
_ => {}
}
}
fn logging_hook() -> HookCallback {
Arc::new(|input, _tool_use_id, _context| {
Box::pin(async move {
match input {
HookInput::PreToolUse(pre) => {
println!("🔧 Hook: PreToolUse for tool: {}", pre.tool_name);
println!(" Tool input: {:?}", pre.tool_input);
}
HookInput::PostToolUse(post) => {
println!("✓ Hook: PostToolUse for tool: {}", post.tool_name);
}
HookInput::UserPromptSubmit(_submit) => {
println!("📝 Hook: UserPromptSubmit");
}
HookInput::Stop(stop) => {
println!("🛑 Hook: Stop - active: {:?}", stop.stop_hook_active);
}
_ => {
println!("📌 Hook: Other event");
}
}
HookOutput::Sync(SyncHookOutput::default())
})
})
}
fn security_hook() -> HookCallback {
Arc::new(|input, _tool_use_id, _context| {
Box::pin(async move {
if let HookInput::PreToolUse(pre) = input {
if pre.tool_name == "Bash" {
if let Some(command) = pre.tool_input.get("command").and_then(|v| v.as_str()) {
let dangerous_patterns = ["rm -rf", "sudo", "chmod 777"];
for pattern in dangerous_patterns {
if command.contains(pattern) {
println!(
"⚠️ Security hook: Blocking dangerous command: {}",
command
);
return HookOutput::Sync(SyncHookOutput {
continue_: Some(false),
decision: Some("block".to_string()),
reason: Some(format!(
"Command contains dangerous pattern: {}",
pattern
)),
..Default::default()
});
}
}
}
}
}
HookOutput::Sync(SyncHookOutput::default())
})
})
}
async fn example_basic_hooks() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Basic Hooks Example ===");
println!("This example shows hooks logging tool usage.\n");
let mut hooks: HashMap<HookEvent, Vec<HookMatcher>> = HashMap::new();
hooks.insert(
HookEvent::PreToolUse,
vec![HookMatcher {
matcher: None, hooks: vec![logging_hook()],
timeout: None,
}],
);
hooks.insert(
HookEvent::PostToolUse,
vec![HookMatcher {
matcher: None,
hooks: vec![logging_hook()],
timeout: None,
}],
);
let mut options = ClaudeAgentOptions::new();
options.allowed_tools = vec!["Bash".to_string()];
options.hooks = Some(hooks);
let mut client = ClaudeClient::new(Some(options));
client.connect().await?;
println!("User: Run echo hello");
client
.query("Run the bash command: echo 'Hello from hooks!'")
.await?;
let (response, _) = client.receive_response().await?;
println!("Claude: {}\n", response);
client.disconnect().await?;
Ok(())
}
async fn example_security_hooks() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Security Hooks Example ===");
println!("This example shows how hooks can block dangerous commands.\n");
let mut hooks: HashMap<HookEvent, Vec<HookMatcher>> = HashMap::new();
hooks.insert(
HookEvent::PreToolUse,
vec![HookMatcher {
matcher: Some("Bash".to_string()),
hooks: vec![security_hook()],
timeout: None,
}],
);
let mut options = ClaudeAgentOptions::new();
options.allowed_tools = vec!["Bash".to_string()];
options.hooks = Some(hooks);
let mut client = ClaudeClient::new(Some(options));
client.connect().await?;
println!("Test 1: Safe command (should work)");
println!("User: Run ls");
client.query("Run: ls -la").await?;
let (response, _) = client.receive_response().await?;
println!("Claude: {}\n", response);
println!("Test 2: Command with dangerous pattern (should be blocked)");
println!("User: Run rm -rf /tmp/test");
client.query("Run: rm -rf /tmp/test").await?;
let (response, _) = client.receive_response().await?;
println!("Claude: {}\n", response);
client.disconnect().await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
println!("Claude SDK Hooks Examples");
println!("{}\n", "=".repeat(50));
if args.len() < 2 {
println!("Usage: cargo run --example hooks <example_name>");
println!("\nAvailable examples:");
println!(" all - Run all examples");
println!(" basic - Basic hook logging");
println!(" security - Security hooks demo");
return Ok(());
}
match args[1].as_str() {
"all" => {
example_basic_hooks().await?;
println!("{}\n", "-".repeat(50));
example_security_hooks().await?;
}
"basic" => {
example_basic_hooks().await?;
}
"security" => {
example_security_hooks().await?;
}
other => {
println!("Unknown example: {}", other);
println!("Available: all, basic, security");
}
}
Ok(())
}