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::Unknown => None,
14    };
15
16    canonical
17        .map(|s| s.to_owned())
18        .unwrap_or_else(|| vendor.to_owned())
19}
20
21fn normalize_claude_code_event(vendor: &str) -> Option<&'static str> {
22    match vendor {
23        "PreToolUse" => Some("tool:before"),
24        "PostToolUse" => Some("tool:after"),
25        "Startup" => Some("session:start"),
26        "Stop" => Some("session:stop"),
27        "SubagentStop" => Some("agent:stop"),
28        "Notification" => Some("notification"),
29        _ => None,
30    }
31}
32
33fn normalize_cursor_event(vendor: &str) -> Option<&'static str> {
34    match vendor {
35        "BeforeToolCall" => Some("tool:before"),
36        "AfterToolCall" => Some("tool:after"),
37        "SessionStart" => Some("session:start"),
38        "SessionEnd" => Some("session:stop"),
39        "Notification" => Some("notification"),
40        _ => None,
41    }
42}
43
44fn normalize_windsurf_event(vendor: &str) -> Option<&'static str> {
45    match vendor {
46        "pre_tool" => Some("tool:before"),
47        "post_tool" => Some("tool:after"),
48        "session_start" => Some("session:start"),
49        "session_end" => Some("session:stop"),
50        "notification" => Some("notification"),
51        _ => None,
52    }
53}
54
55fn normalize_cline_event(vendor: &str) -> Option<&'static str> {
56    match vendor {
57        "beforeToolUse" => Some("tool:before"),
58        "afterToolUse" => Some("tool:after"),
59        "onStart" => Some("session:start"),
60        "onStop" => Some("session:stop"),
61        _ => None,
62    }
63}
64
65fn normalize_amp_event(vendor: &str) -> Option<&'static str> {
66    match vendor {
67        "tool.before" => Some("tool:before"),
68        "tool.after" => Some("tool:after"),
69        "session.start" => Some("session:start"),
70        "session.stop" => Some("session:stop"),
71        "agent.stop" => Some("agent:stop"),
72        _ => None,
73    }
74}
75
76// ---------------------------------------------------------------------------
77// Tests
78// ---------------------------------------------------------------------------
79
80#[cfg(test)]
81mod tests {
82    use super::normalize_event;
83    use crate::types::CallerKind;
84
85    #[test]
86    fn unknown_caller_returns_original_string() {
87        assert_eq!(normalize_event("SomeVendorEvent", &CallerKind::Unknown), "SomeVendorEvent");
88        assert_eq!(normalize_event("PreToolUse", &CallerKind::Unknown), "PreToolUse");
89        assert_eq!(normalize_event("", &CallerKind::Unknown), "");
90    }
91}