1use crate::types::CallerKind;
2
3pub fn normalize_tool(vendor: &str, caller: &CallerKind) -> String {
8 let lower = vendor.to_lowercase();
9
10 let canonical = match caller {
11 CallerKind::ClaudeCode => normalize_claude_code(&lower),
12 CallerKind::Cursor => normalize_cursor(&lower),
13 CallerKind::Windsurf => normalize_windsurf(&lower),
14 CallerKind::Cline => normalize_cline(&lower),
15 CallerKind::Amp => normalize_amp(&lower),
16 CallerKind::GeminiCli => normalize_gemini_cli(&lower),
17 CallerKind::Hermes => normalize_hermes(&lower),
18 CallerKind::Pi | CallerKind::Codex => normalize_claude_code(&lower),
19 CallerKind::Unknown => None,
20 };
21
22 canonical
23 .map(str::to_owned)
24 .unwrap_or_else(|| vendor.to_owned())
25}
26
27fn normalize_claude_code(lower: &str) -> Option<&'static str> {
28 match lower {
29 "bash" => Some("bash"),
30 "read" => Some("read_file"),
31 "write" => Some("write_file"),
32 "edit" => Some("edit_file"),
33 "ls" => Some("list_dir"),
34 "grep" => Some("grep"),
35 "glob" => Some("glob"),
36 "websearch" => Some("web_search"),
37 "webfetch" => Some("web_fetch"),
38 "task" => Some("spawn_agent"),
39 "mcp__ide__getdiagnostics" => Some("diagnostics"),
40 _ => None,
41 }
42}
43
44fn normalize_cursor(lower: &str) -> Option<&'static str> {
45 match lower {
46 "run_terminal_cmd" => Some("bash"),
47 "read_file" => Some("read_file"),
48 "edit_file" => Some("write_file"),
49 "apply_edit" => Some("edit_file"),
50 "list_dir" => Some("list_dir"),
51 "grep_search" => Some("grep"),
52 "file_search" => Some("glob"),
53 "web_search" => Some("web_search"),
54 "fetch_url" => Some("web_fetch"),
55 "spawn_agent" => Some("spawn_agent"),
56 "get_diagnostics" => Some("diagnostics"),
57 "move_file" => Some("move_file"),
58 "delete_file" => Some("delete_file"),
59 "create_dir" => Some("create_dir"),
60 _ => None,
61 }
62}
63
64fn normalize_windsurf(lower: &str) -> Option<&'static str> {
65 match lower {
66 "run_command" => Some("bash"),
67 "read_file" => Some("read_file"),
68 "write_file" => Some("write_file"),
69 "edit_file" => Some("edit_file"),
70 "list_directory" => Some("list_dir"),
71 "search_files" => Some("grep"),
72 "find_files" => Some("glob"),
73 "search_web" => Some("web_search"),
74 "fetch_page" => Some("web_fetch"),
75 "spawn_agent" => Some("spawn_agent"),
76 "get_diagnostics" => Some("diagnostics"),
77 "move_file" => Some("move_file"),
78 "delete_file" => Some("delete_file"),
79 "create_directory" => Some("create_dir"),
80 _ => None,
81 }
82}
83
84fn normalize_cline(lower: &str) -> Option<&'static str> {
85 match lower {
86 "execute_command" => Some("bash"),
87 "read_file" => Some("read_file"),
88 "write_to_file" => Some("write_file"),
89 "apply_diff" => Some("edit_file"),
90 "list_files" => Some("list_dir"),
91 "search_files" => Some("grep"),
92 "search" => Some("web_search"),
93 "fetch" => Some("web_fetch"),
94 "rename_file" => Some("move_file"),
95 "delete_file" => Some("delete_file"),
96 "create_directory" => Some("create_dir"),
97 "get_diagnostics" => Some("diagnostics"),
98 _ => None,
99 }
100}
101
102fn normalize_amp(lower: &str) -> Option<&'static str> {
103 match lower {
104 "shell" => Some("bash"),
105 "file.read" => Some("read_file"),
106 "file.write" => Some("write_file"),
107 "file.edit" => Some("edit_file"),
108 "fs.list" => Some("list_dir"),
109 "search.grep" => Some("grep"),
110 "search.glob" => Some("glob"),
111 "web.search" => Some("web_search"),
112 "web.fetch" => Some("web_fetch"),
113 "agent.spawn" => Some("spawn_agent"),
114 "lsp.diagnostics" => Some("diagnostics"),
115 "fs.move" => Some("move_file"),
116 "fs.delete" => Some("delete_file"),
117 "fs.mkdir" => Some("create_dir"),
118 _ => None,
119 }
120}
121
122fn normalize_gemini_cli(lower: &str) -> Option<&'static str> {
123 match lower {
124 "run_shell_command" => Some("bash"),
125 "read_file" => Some("read_file"),
126 "write_file" => Some("write_file"),
127 "replace" => Some("edit_file"),
128 "list_directory" => Some("list_dir"),
129 "glob" => Some("glob"),
130 "grep" => Some("grep"),
131 "google_web_search" => Some("web_search"),
132 "fetch" => Some("web_fetch"),
133 "move_file" => Some("move_file"),
134 "delete_file" => Some("delete_file"),
135 "make_directory" => Some("create_dir"),
136 _ => None,
137 }
138}
139
140fn normalize_hermes(lower: &str) -> Option<&'static str> {
141 match lower {
142 "terminal" => Some("bash"),
143 "read_file" => Some("read_file"),
144 "write_file" => Some("write_file"),
145 "patch" => Some("edit_file"),
146 "search_files" => Some("grep"),
147 "delegate_task" => Some("spawn_agent"),
148 "browser_back" | "browser_click" | "browser_console" | "browser_get_images"
149 | "browser_navigate" | "browser_press" | "browser_scroll" | "browser_snapshot"
150 | "browser_type" | "browser_vision" => Some("browser"),
151 _ => None,
152 }
153}
154
155#[cfg(test)]
160#[path = "tools_tests.rs"]
161mod tests;