1use crate::types::CallerKind;
2
3pub fn normalize_event(vendor: &str, caller: &CallerKind) -> String {
7 let canonical = match caller {
8 CallerKind::ClaudeCode => normalize_claude_code_event(vendor),
9 CallerKind::Cursor => normalize_cursor_event(vendor),
10 CallerKind::Windsurf => normalize_windsurf_event(vendor),
11 CallerKind::Cline => normalize_cline_event(vendor),
12 CallerKind::Amp => normalize_amp_event(vendor),
13 CallerKind::GeminiCli => normalize_gemini_cli_event(vendor),
14 CallerKind::Hermes => normalize_hermes_event(vendor),
15 CallerKind::Pi | CallerKind::Codex => normalize_claude_code_event(vendor),
16 CallerKind::Unknown => None,
17 };
18
19 canonical
20 .map(str::to_owned)
21 .unwrap_or_else(|| vendor.to_owned())
22}
23
24fn normalize_claude_code_event(vendor: &str) -> Option<&'static str> {
25 match vendor {
26 "PreToolUse" => Some("tool:before"),
27 "PostToolUse" => Some("tool:after"),
28 "Startup" => Some("session:start"),
29 "Stop" => Some("session:stop"),
30 "SubagentStop" => Some("agent:stop"),
31 "UserPromptSubmit" => Some("prompt:submit"),
32 "Notification" => Some("notification"),
33 _ => None,
34 }
35}
36
37fn normalize_cursor_event(vendor: &str) -> Option<&'static str> {
38 match vendor {
39 "BeforeToolCall" => Some("tool:before"),
40 "AfterToolCall" => Some("tool:after"),
41 "SessionStart" => Some("session:start"),
42 "SessionEnd" => Some("session:stop"),
43 "Notification" => Some("notification"),
44 _ => None,
45 }
46}
47
48fn normalize_windsurf_event(vendor: &str) -> Option<&'static str> {
49 match vendor {
50 "pre_tool" => Some("tool:before"),
51 "post_tool" => Some("tool:after"),
52 "session_start" => Some("session:start"),
53 "session_end" => Some("session:stop"),
54 "notification" => Some("notification"),
55 _ => None,
56 }
57}
58
59fn normalize_cline_event(vendor: &str) -> Option<&'static str> {
60 match vendor {
61 "beforeToolUse" => Some("tool:before"),
62 "afterToolUse" => Some("tool:after"),
63 "onStart" => Some("session:start"),
64 "onStop" => Some("session:stop"),
65 "UserPromptSubmit" => Some("prompt:submit"),
66 _ => None,
67 }
68}
69
70fn normalize_amp_event(vendor: &str) -> Option<&'static str> {
71 match vendor {
72 "tool.before" => Some("tool:before"),
73 "tool.after" => Some("tool:after"),
74 "session.start" => Some("session:start"),
75 "session.stop" => Some("session:stop"),
76 "agent.stop" => Some("agent:stop"),
77 _ => None,
78 }
79}
80
81fn normalize_gemini_cli_event(vendor: &str) -> Option<&'static str> {
82 match vendor {
83 "BeforeTool" => Some("tool:before"),
84 "AfterTool" => Some("tool:after"),
85 "SessionStart" => Some("session:start"),
86 "SessionEnd" => Some("session:stop"),
87 "AfterAgent" => Some("agent:stop"),
88 "BeforeAgent" => Some("prompt:submit"),
89 "Notification" => Some("notification"),
90 _ => None,
91 }
92}
93
94fn normalize_hermes_event(vendor: &str) -> Option<&'static str> {
95 match vendor {
96 "pre_tool_call" => Some("tool:before"),
97 "post_tool_call" => Some("tool:after"),
98 "on_session_start" => Some("session:start"),
99 "on_session_end" | "on_session_finalize" => Some("session:stop"),
100 "subagent_stop" => Some("agent:stop"),
101 "pre_llm_call" => Some("prompt:submit"),
103 _ => None,
104 }
105}
106
107#[cfg(test)]
112#[path = "events_tests.rs"]
113mod tests;