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::Unknown => None,
17 };
18
19 canonical
20 .map(|s| s.to_owned())
21 .unwrap_or_else(|| vendor.to_owned())
22}
23
24fn normalize_claude_code(lower: &str) -> Option<&'static str> {
25 match lower {
26 "bash" => Some("bash"),
27 "read" => Some("read_file"),
28 "write" => Some("write_file"),
29 "edit" => Some("edit_file"),
30 "ls" => Some("list_dir"),
31 "grep" => Some("grep"),
32 "glob" => Some("glob"),
33 "websearch" => Some("web_search"),
34 "webfetch" => Some("web_fetch"),
35 "task" => Some("spawn_agent"),
36 "mcp__ide__getdiagnostics" => Some("diagnostics"),
37 _ => None,
38 }
39}
40
41fn normalize_cursor(lower: &str) -> Option<&'static str> {
42 match lower {
43 "run_terminal_cmd" => Some("bash"),
44 "read_file" => Some("read_file"),
45 "edit_file" => Some("write_file"),
46 "apply_edit" => Some("edit_file"),
47 "list_dir" => Some("list_dir"),
48 "grep_search" => Some("grep"),
49 "file_search" => Some("glob"),
50 "web_search" => Some("web_search"),
51 "fetch_url" => Some("web_fetch"),
52 "spawn_agent" => Some("spawn_agent"),
53 "get_diagnostics" => Some("diagnostics"),
54 "move_file" => Some("move_file"),
55 "delete_file" => Some("delete_file"),
56 "create_dir" => Some("create_dir"),
57 _ => None,
58 }
59}
60
61fn normalize_windsurf(lower: &str) -> Option<&'static str> {
62 match lower {
63 "run_command" => Some("bash"),
64 "read_file" => Some("read_file"),
65 "write_file" => Some("write_file"),
66 "edit_file" => Some("edit_file"),
67 "list_directory" => Some("list_dir"),
68 "search_files" => Some("grep"),
69 "find_files" => Some("glob"),
70 "search_web" => Some("web_search"),
71 "fetch_page" => Some("web_fetch"),
72 "spawn_agent" => Some("spawn_agent"),
73 "get_diagnostics" => Some("diagnostics"),
74 "move_file" => Some("move_file"),
75 "delete_file" => Some("delete_file"),
76 "create_directory" => Some("create_dir"),
77 _ => None,
78 }
79}
80
81fn normalize_cline(lower: &str) -> Option<&'static str> {
82 match lower {
83 "execute_command" => Some("bash"),
84 "read_file" => Some("read_file"),
85 "write_to_file" => Some("write_file"),
86 "apply_diff" => Some("edit_file"),
87 "list_files" => Some("list_dir"),
88 "search_files" => Some("grep"),
89 "search" => Some("web_search"),
90 "fetch" => Some("web_fetch"),
91 "rename_file" => Some("move_file"),
92 "delete_file" => Some("delete_file"),
93 "create_directory" => Some("create_dir"),
94 "get_diagnostics" => Some("diagnostics"),
95 _ => None,
96 }
97}
98
99fn normalize_amp(lower: &str) -> Option<&'static str> {
100 match lower {
101 "shell" => Some("bash"),
102 "file.read" => Some("read_file"),
103 "file.write" => Some("write_file"),
104 "file.edit" => Some("edit_file"),
105 "fs.list" => Some("list_dir"),
106 "search.grep" => Some("grep"),
107 "search.glob" => Some("glob"),
108 "web.search" => Some("web_search"),
109 "web.fetch" => Some("web_fetch"),
110 "agent.spawn" => Some("spawn_agent"),
111 "lsp.diagnostics" => Some("diagnostics"),
112 "fs.move" => Some("move_file"),
113 "fs.delete" => Some("delete_file"),
114 "fs.mkdir" => Some("create_dir"),
115 _ => None,
116 }
117}
118
119#[cfg(test)]
124mod tests {
125 use super::normalize_tool;
126 use crate::CallerKind;
127
128 #[test]
129 fn unknown_caller_returns_original() {
130 assert_eq!(normalize_tool("SomeTool", &CallerKind::Unknown), "SomeTool");
131 assert_eq!(normalize_tool("bash", &CallerKind::Unknown), "bash");
132 assert_eq!(normalize_tool("Bash", &CallerKind::Unknown), "Bash");
133 }
134
135 #[test] fn claude_ls() { assert_eq!(normalize_tool("ls", &CallerKind::ClaudeCode), "list_dir"); }
137 #[test] fn claude_grep() { assert_eq!(normalize_tool("grep", &CallerKind::ClaudeCode), "grep"); }
138 #[test] fn claude_glob() { assert_eq!(normalize_tool("glob", &CallerKind::ClaudeCode), "glob"); }
139 #[test] fn claude_websearch() { assert_eq!(normalize_tool("websearch", &CallerKind::ClaudeCode), "web_search"); }
140 #[test] fn claude_webfetch() { assert_eq!(normalize_tool("webfetch", &CallerKind::ClaudeCode), "web_fetch"); }
141 #[test] fn claude_mcp_diag() { assert_eq!(normalize_tool("mcp__ide__getdiagnostics", &CallerKind::ClaudeCode), "diagnostics"); }
142
143 #[test] fn cursor_apply_edit() { assert_eq!(normalize_tool("apply_edit", &CallerKind::Cursor), "edit_file"); }
145 #[test] fn cursor_list_dir() { assert_eq!(normalize_tool("list_dir", &CallerKind::Cursor), "list_dir"); }
146 #[test] fn cursor_file_search() { assert_eq!(normalize_tool("file_search", &CallerKind::Cursor), "glob"); }
147 #[test] fn cursor_fetch_url() { assert_eq!(normalize_tool("fetch_url", &CallerKind::Cursor), "web_fetch"); }
148 #[test] fn cursor_spawn_agent() { assert_eq!(normalize_tool("spawn_agent", &CallerKind::Cursor), "spawn_agent"); }
149 #[test] fn cursor_get_diag() { assert_eq!(normalize_tool("get_diagnostics",&CallerKind::Cursor), "diagnostics"); }
150 #[test] fn cursor_move_file() { assert_eq!(normalize_tool("move_file", &CallerKind::Cursor), "move_file"); }
151 #[test] fn cursor_delete_file() { assert_eq!(normalize_tool("delete_file", &CallerKind::Cursor), "delete_file"); }
152 #[test] fn cursor_create_dir() { assert_eq!(normalize_tool("create_dir", &CallerKind::Cursor), "create_dir"); }
153
154 #[test] fn windsurf_edit_file() { assert_eq!(normalize_tool("edit_file", &CallerKind::Windsurf), "edit_file"); }
156 #[test] fn windsurf_search_files() { assert_eq!(normalize_tool("search_files", &CallerKind::Windsurf), "grep"); }
157 #[test] fn windsurf_find_files() { assert_eq!(normalize_tool("find_files", &CallerKind::Windsurf), "glob"); }
158 #[test] fn windsurf_fetch_page() { assert_eq!(normalize_tool("fetch_page", &CallerKind::Windsurf), "web_fetch"); }
159 #[test] fn windsurf_spawn_agent() { assert_eq!(normalize_tool("spawn_agent", &CallerKind::Windsurf), "spawn_agent"); }
160 #[test] fn windsurf_get_diag() { assert_eq!(normalize_tool("get_diagnostics", &CallerKind::Windsurf), "diagnostics"); }
161 #[test] fn windsurf_move_file() { assert_eq!(normalize_tool("move_file", &CallerKind::Windsurf), "move_file"); }
162 #[test] fn windsurf_delete_file() { assert_eq!(normalize_tool("delete_file", &CallerKind::Windsurf), "delete_file"); }
163 #[test] fn windsurf_create_dir() { assert_eq!(normalize_tool("create_directory",&CallerKind::Windsurf), "create_dir"); }
164
165 #[test] fn cline_search_files(){ assert_eq!(normalize_tool("search_files", &CallerKind::Cline), "grep"); }
167 #[test] fn cline_search() { assert_eq!(normalize_tool("search", &CallerKind::Cline), "web_search"); }
168 #[test] fn cline_fetch() { assert_eq!(normalize_tool("fetch", &CallerKind::Cline), "web_fetch"); }
169 #[test] fn cline_rename_file() { assert_eq!(normalize_tool("rename_file", &CallerKind::Cline), "move_file"); }
170 #[test] fn cline_delete_file() { assert_eq!(normalize_tool("delete_file", &CallerKind::Cline), "delete_file"); }
171 #[test] fn cline_create_dir() { assert_eq!(normalize_tool("create_directory", &CallerKind::Cline), "create_dir"); }
172 #[test] fn cline_get_diag() { assert_eq!(normalize_tool("get_diagnostics", &CallerKind::Cline), "diagnostics"); }
173
174 #[test] fn amp_search_grep() { assert_eq!(normalize_tool("search.grep", &CallerKind::Amp), "grep"); }
176 #[test] fn amp_search_glob() { assert_eq!(normalize_tool("search.glob", &CallerKind::Amp), "glob"); }
177 #[test] fn amp_web_fetch() { assert_eq!(normalize_tool("web.fetch", &CallerKind::Amp), "web_fetch"); }
178 #[test] fn amp_agent_spawn() { assert_eq!(normalize_tool("agent.spawn", &CallerKind::Amp), "spawn_agent"); }
179 #[test] fn amp_lsp_diag() { assert_eq!(normalize_tool("lsp.diagnostics",&CallerKind::Amp), "diagnostics"); }
180 #[test] fn amp_fs_move() { assert_eq!(normalize_tool("fs.move", &CallerKind::Amp), "move_file"); }
181 #[test] fn amp_fs_delete() { assert_eq!(normalize_tool("fs.delete", &CallerKind::Amp), "delete_file"); }
182 #[test] fn amp_fs_mkdir() { assert_eq!(normalize_tool("fs.mkdir", &CallerKind::Amp), "create_dir"); }
183
184 #[test] fn claude_unknown_falls_through() { assert_eq!(normalize_tool("no_such_tool", &CallerKind::ClaudeCode), "no_such_tool"); }
186 #[test] fn cursor_unknown_falls_through() { assert_eq!(normalize_tool("no_such_tool", &CallerKind::Cursor), "no_such_tool"); }
187 #[test] fn windsurf_unknown_falls_through() { assert_eq!(normalize_tool("no_such_tool", &CallerKind::Windsurf), "no_such_tool"); }
188 #[test] fn cline_unknown_falls_through() { assert_eq!(normalize_tool("no_such_tool", &CallerKind::Cline), "no_such_tool"); }
189 #[test] fn amp_unknown_falls_through() { assert_eq!(normalize_tool("no_such_tool", &CallerKind::Amp), "no_such_tool"); }
190}