1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//! Lifecycle hooks.
//!
//! Registers hooks for three events: `PreToolUse` (log & optionally block),
//! `PostToolUse` (observe results), and `Stop` (session teardown).
//!
//! ```sh
//! cargo run --example 05_hooks
//! ```
use std::sync::Arc;
use claude_cli_sdk::{ClientConfig, HookEvent, HookMatcher, HookOutput, PermissionMode, query};
#[tokio::main]
async fn main() -> claude_cli_sdk::Result<()> {
let config = ClientConfig::builder()
.prompt("List files in the current directory")
.permission_mode(PermissionMode::AcceptEdits)
.hooks(vec![
// Log every tool invocation before it runs.
HookMatcher::new(
HookEvent::PreToolUse,
Arc::new(|input, _session, _ctx| {
Box::pin(async move {
eprintln!(
"[hook:pre] tool={:?} input={:?}",
input.tool_name, input.tool_input
);
HookOutput::allow()
})
}),
),
// Log Bash results after execution.
HookMatcher::new(
HookEvent::PostToolUse,
Arc::new(|input, _session, _ctx| {
Box::pin(async move {
eprintln!(
"[hook:post] tool={:?} result={:?}",
input.tool_name, input.tool_result
);
HookOutput::allow()
})
}),
)
.for_tool("Bash"),
// Log when the session stops.
HookMatcher::new(
HookEvent::Stop,
Arc::new(|_input, _session, _ctx| {
Box::pin(async move {
eprintln!("[hook:stop] session ended");
HookOutput::allow()
})
}),
),
])
.build();
let messages = query(config).await?;
for msg in &messages {
if let Some(text) = msg.assistant_text() {
println!("{text}");
}
}
Ok(())
}