Skip to main content

polyhook_core/
events.rs

1use crate::types::CallerKind;
2
3/// Normalize a vendor-specific event name to the canonical polyhook event name.
4///
5/// If no mapping exists the original name is returned unchanged.
6pub 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::Unknown => None,
15    };
16
17    canonical
18        .map(|s| s.to_owned())
19        .unwrap_or_else(|| vendor.to_owned())
20}
21
22fn normalize_claude_code_event(vendor: &str) -> Option<&'static str> {
23    match vendor {
24        "PreToolUse" => Some("tool:before"),
25        "PostToolUse" => Some("tool:after"),
26        "Startup" => Some("session:start"),
27        "Stop" => Some("session:stop"),
28        "SubagentStop" => Some("agent:stop"),
29        "Notification" => Some("notification"),
30        _ => None,
31    }
32}
33
34fn normalize_cursor_event(vendor: &str) -> Option<&'static str> {
35    match vendor {
36        "BeforeToolCall" => Some("tool:before"),
37        "AfterToolCall" => Some("tool:after"),
38        "SessionStart" => Some("session:start"),
39        "SessionEnd" => Some("session:stop"),
40        "Notification" => Some("notification"),
41        _ => None,
42    }
43}
44
45fn normalize_windsurf_event(vendor: &str) -> Option<&'static str> {
46    match vendor {
47        "pre_tool" => Some("tool:before"),
48        "post_tool" => Some("tool:after"),
49        "session_start" => Some("session:start"),
50        "session_end" => Some("session:stop"),
51        "notification" => Some("notification"),
52        _ => None,
53    }
54}
55
56fn normalize_cline_event(vendor: &str) -> Option<&'static str> {
57    match vendor {
58        "beforeToolUse" => Some("tool:before"),
59        "afterToolUse" => Some("tool:after"),
60        "onStart" => Some("session:start"),
61        "onStop" => Some("session:stop"),
62        _ => None,
63    }
64}
65
66fn normalize_amp_event(vendor: &str) -> Option<&'static str> {
67    match vendor {
68        "tool.before" => Some("tool:before"),
69        "tool.after" => Some("tool:after"),
70        "session.start" => Some("session:start"),
71        "session.stop" => Some("session:stop"),
72        "agent.stop" => Some("agent:stop"),
73        _ => None,
74    }
75}
76
77fn normalize_gemini_cli_event(vendor: &str) -> Option<&'static str> {
78    match vendor {
79        "BeforeTool" => Some("tool:before"),
80        "AfterTool" => Some("tool:after"),
81        "SessionStart" => Some("session:start"),
82        "SessionEnd" => Some("session:stop"),
83        "AfterAgent" => Some("agent:stop"),
84        "Notification" => Some("notification"),
85        _ => None,
86    }
87}
88
89// ---------------------------------------------------------------------------
90// Tests
91// ---------------------------------------------------------------------------
92
93#[cfg(test)]
94#[path = "events_tests.rs"]
95mod tests;