vtcode_core/tools/
names.rs1pub use crate::utils::tool_name_parsing::canonical_tool_name;
3
4fn is_apply_patch_tool_name(name: &str) -> bool {
7 let base = name.rsplit(['/', '\\']).next().unwrap_or(name);
8 let base = base.trim_matches(|c| c == '\'' || c == '"' || c == '`');
9 matches!(base, "apply_patch" | "applypatch")
10}
11
12fn first_shell_token_preserving_backslashes(command: &str) -> &str {
13 let command = command.trim_start();
14 let mut active_quote = None;
15 let mut escaped = false;
16
17 for (index, character) in command.char_indices() {
18 if escaped {
19 escaped = false;
20 continue;
21 }
22 if character == '\\' && active_quote != Some('\'') {
23 escaped = true;
24 continue;
25 }
26
27 if let Some(quote) = active_quote {
28 if character == quote {
29 active_quote = None;
30 }
31 continue;
32 }
33
34 match character {
35 '\'' | '"' | '`' => active_quote = Some(character),
36 character if character.is_whitespace() => return &command[..index],
37 _ => {}
38 }
39 }
40
41 command
42}
43
44pub fn is_apply_patch_shell_collision_command(command: &str) -> bool {
49 let raw_first_token = first_shell_token_preserving_backslashes(command).trim();
52 if is_apply_patch_tool_name(raw_first_token) {
53 return true;
54 }
55
56 if let Ok(parts) = shell_words::split(command)
57 && let Some(first) = parts.into_iter().next()
58 && !first.trim().is_empty()
59 {
60 return is_apply_patch_tool_name(&first);
61 }
62 let first_token = first_shell_token_preserving_backslashes(command).trim();
65 is_apply_patch_tool_name(first_token)
66}
67
68#[test]
69fn test_canonical_tool_name_passes_through() {
70 assert_eq!(canonical_tool_name("list_files"), "list_files");
73
74 assert_eq!(canonical_tool_name("unknown_tool"), "unknown_tool");
75
76 assert_eq!(canonical_tool_name("container.exec"), "container.exec");
77}
78
79#[test]
80fn test_is_apply_patch_shell_collision_command() {
81 assert!(is_apply_patch_shell_collision_command("apply_patch"));
82 assert!(is_apply_patch_shell_collision_command("applypatch"));
83 assert!(is_apply_patch_shell_collision_command("apply_patch --help"));
84 assert!(is_apply_patch_shell_collision_command("/usr/bin/apply_patch"));
85 assert!(is_apply_patch_shell_collision_command("./applypatch foo"));
86 assert!(is_apply_patch_shell_collision_command(r"C:\tools\apply_patch --help"));
87 assert!(is_apply_patch_shell_collision_command(r#""C:\Program Files\apply_patch" --help"#));
88 assert!(is_apply_patch_shell_collision_command(r#""C:\Program Files"\apply_patch --help"#));
89 assert!(is_apply_patch_shell_collision_command("'apply_patch'"));
90 assert!(is_apply_patch_shell_collision_command("apply_patch \"unclosed"));
91 assert!(!is_apply_patch_shell_collision_command("'apply_patch'foo"));
92 assert!(!is_apply_patch_shell_collision_command("sudo apply_patch"));
93 assert!(!is_apply_patch_shell_collision_command("pip install foo"));
94 assert!(!is_apply_patch_shell_collision_command(""));
95 assert!(!is_apply_patch_shell_collision_command("APPLY_PATCH"));
96}