1use serde::{Deserialize, Serialize};
6use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum HookEvent {
12 SessionStart,
14 SessionEnd,
16 UserPrompt,
18 PreToolCall,
20 PostToolCall,
22 ToolError,
24 PreApproval,
26 PostApproval,
28 Notification,
30 PreCompact,
32 PostCompact,
34 PromptBuild,
36 MessageSend,
38 SessionReset,
40 ModelResolve,
42 MessageReceived,
44 MessageSent,
46 AgentLoopEnd,
48 ToolResultPersist,
50 SubagentStart,
52 SubagentStop,
54 KernelStart,
56 KernelStop,
58}
59
60impl fmt::Display for HookEvent {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 Self::SessionStart => write!(f, "session_start"),
64 Self::SessionEnd => write!(f, "session_end"),
65 Self::UserPrompt => write!(f, "user_prompt"),
66 Self::PreToolCall => write!(f, "pre_tool_call"),
67 Self::PostToolCall => write!(f, "post_tool_call"),
68 Self::ToolError => write!(f, "tool_error"),
69 Self::PreApproval => write!(f, "pre_approval"),
70 Self::PostApproval => write!(f, "post_approval"),
71 Self::Notification => write!(f, "notification"),
72 Self::PreCompact => write!(f, "pre_compact"),
73 Self::PostCompact => write!(f, "post_compact"),
74 Self::PromptBuild => write!(f, "prompt_build"),
75 Self::MessageSend => write!(f, "message_send"),
76 Self::SessionReset => write!(f, "session_reset"),
77 Self::ModelResolve => write!(f, "model_resolve"),
78 Self::MessageReceived => write!(f, "message_received"),
79 Self::MessageSent => write!(f, "message_sent"),
80 Self::AgentLoopEnd => write!(f, "agent_loop_end"),
81 Self::ToolResultPersist => write!(f, "tool_result_persist"),
82 Self::SubagentStart => write!(f, "subagent_start"),
83 Self::SubagentStop => write!(f, "subagent_stop"),
84 Self::KernelStart => write!(f, "kernel_start"),
85 Self::KernelStop => write!(f, "kernel_stop"),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn all_hook_events_have_display_and_serde_roundtrip() {
96 let hooks: Vec<HookEvent> = vec![
97 HookEvent::SessionStart,
98 HookEvent::SessionEnd,
99 HookEvent::UserPrompt,
100 HookEvent::PreToolCall,
101 HookEvent::PostToolCall,
102 HookEvent::ToolError,
103 HookEvent::PreApproval,
104 HookEvent::PostApproval,
105 HookEvent::Notification,
106 HookEvent::PreCompact,
107 HookEvent::PostCompact,
108 HookEvent::PromptBuild,
109 HookEvent::MessageSend,
110 HookEvent::SessionReset,
111 HookEvent::ModelResolve,
112 HookEvent::MessageReceived,
113 HookEvent::MessageSent,
114 HookEvent::AgentLoopEnd,
115 HookEvent::ToolResultPersist,
116 HookEvent::SubagentStart,
117 HookEvent::SubagentStop,
118 HookEvent::KernelStart,
119 HookEvent::KernelStop,
120 ];
121
122 for hook in &hooks {
123 let display = hook.to_string();
124 assert!(
125 !display.is_empty(),
126 "Display must not be empty for {hook:?}"
127 );
128
129 let json = serde_json::to_string(hook).unwrap();
130 let parsed: HookEvent = serde_json::from_str(&json).unwrap();
131 assert_eq!(*hook, parsed, "serde round-trip failed for {hook:?}");
132 }
133 }
134}