1pub mod inputs;
17pub mod outputs;
18
19pub use inputs::{
20 BaseHookInput, NotificationInput, PermissionRequestInput, PostToolUseFailureInput,
21 PostToolUseInput, PreCompactInput, PreToolUseInput, StopInput, SubagentContext,
22 SubagentStartInput, SubagentStopInput, UserPromptSubmitInput,
23};
24pub use outputs::{PreToolUseHookSpecificOutput, UserPromptSubmitHookSpecificOutput};
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub enum HookKind {
31 PreToolUse,
33 PostToolUse,
35 PostToolUseFailure,
37 UserPromptSubmit,
39 Stop,
41 SubagentStop,
43 SubagentStart,
45 PreCompact,
47 Notification,
49 PermissionRequest,
51 WorktreeCreate,
53 WorktreeRemove,
55 CwdChanged,
57 FileChanged,
59 SessionStart,
61 SessionEnd,
63 Unknown,
65}
66
67impl HookKind {
68 pub fn as_str(self) -> &'static str {
70 match self {
71 Self::PreToolUse => "PreToolUse",
72 Self::PostToolUse => "PostToolUse",
73 Self::PostToolUseFailure => "PostToolUseFailure",
74 Self::UserPromptSubmit => "UserPromptSubmit",
75 Self::Stop => "Stop",
76 Self::SubagentStop => "SubagentStop",
77 Self::SubagentStart => "SubagentStart",
78 Self::PreCompact => "PreCompact",
79 Self::Notification => "Notification",
80 Self::PermissionRequest => "PermissionRequest",
81 Self::WorktreeCreate => "WorktreeCreate",
82 Self::WorktreeRemove => "WorktreeRemove",
83 Self::CwdChanged => "CwdChanged",
84 Self::FileChanged => "FileChanged",
85 Self::SessionStart => "SessionStart",
86 Self::SessionEnd => "SessionEnd",
87 Self::Unknown => "Unknown",
88 }
89 }
90
91 pub fn from_wire(s: &str) -> Self {
95 match s {
96 "PreToolUse" => Self::PreToolUse,
97 "PostToolUse" => Self::PostToolUse,
98 "PostToolUseFailure" => Self::PostToolUseFailure,
99 "UserPromptSubmit" => Self::UserPromptSubmit,
100 "Stop" => Self::Stop,
101 "SubagentStop" => Self::SubagentStop,
102 "SubagentStart" => Self::SubagentStart,
103 "PreCompact" => Self::PreCompact,
104 "Notification" => Self::Notification,
105 "PermissionRequest" => Self::PermissionRequest,
106 "WorktreeCreate" => Self::WorktreeCreate,
107 "WorktreeRemove" => Self::WorktreeRemove,
108 "CwdChanged" => Self::CwdChanged,
109 "FileChanged" => Self::FileChanged,
110 "SessionStart" => Self::SessionStart,
111 "SessionEnd" => Self::SessionEnd,
112 _ => Self::Unknown,
113 }
114 }
115}
116
117#[derive(Debug, Clone)]
119pub struct HookContext {
120 pub kind: HookKind,
122 pub tool_name: Option<String>,
124 pub session_id: String,
126 pub tool_use_id: Option<String>,
129}
130
131#[cfg(test)]
132mod hook_kind_extension_tests {
133 use super::*;
134
135 #[test]
136 fn worktree_create_round_trips() {
137 assert_eq!(HookKind::from_wire("WorktreeCreate"), HookKind::WorktreeCreate);
138 assert_eq!(HookKind::WorktreeCreate.as_str(), "WorktreeCreate");
139 }
140
141 #[test]
142 fn worktree_remove_round_trips() {
143 assert_eq!(HookKind::from_wire("WorktreeRemove"), HookKind::WorktreeRemove);
144 assert_eq!(HookKind::WorktreeRemove.as_str(), "WorktreeRemove");
145 }
146
147 #[test]
148 fn cwd_changed_round_trips() {
149 assert_eq!(HookKind::from_wire("CwdChanged"), HookKind::CwdChanged);
150 assert_eq!(HookKind::CwdChanged.as_str(), "CwdChanged");
151 }
152
153 #[test]
154 fn file_changed_round_trips() {
155 assert_eq!(HookKind::from_wire("FileChanged"), HookKind::FileChanged);
156 assert_eq!(HookKind::FileChanged.as_str(), "FileChanged");
157 }
158
159 #[test]
160 fn session_start_round_trips() {
161 assert_eq!(HookKind::from_wire("SessionStart"), HookKind::SessionStart);
162 assert_eq!(HookKind::SessionStart.as_str(), "SessionStart");
163 }
164
165 #[test]
166 fn session_end_round_trips() {
167 assert_eq!(HookKind::from_wire("SessionEnd"), HookKind::SessionEnd);
168 assert_eq!(HookKind::SessionEnd.as_str(), "SessionEnd");
169 }
170
171 #[test]
172 fn truly_unknown_still_routes_to_unknown() {
173 assert_eq!(HookKind::from_wire("ThisIsNotARealEvent"), HookKind::Unknown);
174 }
175}