vtcode_core/tools/pty/
command_utils.rs1use std::path::Path;
2
3pub fn is_cargo_command(program: &str) -> bool {
5 let name = Path::new(program)
6 .file_name()
7 .and_then(|value| value.to_str())
8 .unwrap_or(program)
9 .to_ascii_lowercase();
10 name == "cargo"
11}
12
13pub fn is_cargo_command_string(command: &str) -> bool {
15 let trimmed = command.trim();
16 trimmed.starts_with("cargo ") || trimmed == "cargo"
17}
18
19const LOCKFILE_COMMANDS: &[&str] = &[
23 "cargo", "npm", "pnpm", "yarn", "bun", "go", "gradle", "mvn", "pip", "pip3", "poetry", "composer", "bundler", "bundle", ];
38
39fn normalize_program_name(program: &str) -> String {
40 Path::new(program)
41 .file_name()
42 .and_then(|value| value.to_str())
43 .unwrap_or(program)
44 .to_ascii_lowercase()
45}
46
47pub fn is_lockfile_command(program: &str) -> bool {
51 let name = normalize_program_name(program);
52 LOCKFILE_COMMANDS.contains(&name.as_str())
53}
54
55pub fn is_long_running_command(program: &str) -> bool {
57 let name = normalize_program_name(program);
58 is_development_toolchain_command(&name) || is_lockfile_command(&name)
59}
60
61pub fn is_lockfile_command_string(command: &str) -> bool {
63 let trimmed = command.trim();
64 LOCKFILE_COMMANDS
65 .iter()
66 .any(|&cmd| trimmed.starts_with(&format!("{cmd} ")) || trimmed == cmd)
67}
68
69pub fn is_long_running_command_string(command: &str) -> bool {
71 let trimmed = command.trim();
72 if trimmed.is_empty() {
73 return false;
74 }
75 if is_lockfile_command_string(trimmed) {
76 return true;
77 }
78 let first = trimmed.split_whitespace().next().unwrap_or_default();
79 is_development_toolchain_command(first)
80}
81
82pub(super) fn is_shell_program(program: &str) -> bool {
83 let name = Path::new(program)
84 .file_name()
85 .and_then(|value| value.to_str())
86 .unwrap_or(program)
87 .to_ascii_lowercase();
88 matches!(
89 name.as_str(),
90 "bash"
91 | "sh"
92 | "zsh"
93 | "fish"
94 | "dash"
95 | "ash"
96 | "busybox"
97 | "ksh"
98 | "ksh93"
99 | "mksh"
100 | "tcsh"
101 | "csh"
102 | "posh"
103 | "yash"
104 | "oksh"
105 | "loksh"
106 | "oil"
107 | "osh"
108 | "nu"
109 | "nushell"
110 | "elvish"
111 | "pwsh"
112 )
113}
114
115pub(super) fn is_sandbox_wrapper_program(program: &str, args: &[String]) -> bool {
116 let name = Path::new(program)
117 .file_name()
118 .and_then(|value| value.to_str())
119 .unwrap_or(program)
120 .to_ascii_lowercase();
121 if name == "sandbox-exec" {
122 return true;
123 }
124
125 args.iter().any(|arg| {
126 matches!(
127 arg.as_str(),
128 "--sandbox-policy" | "--sandbox-policy-cwd" | "--seccomp-profile" | "--resource-limits"
129 )
130 })
131}
132
133pub fn is_development_toolchain_command(program: &str) -> bool {
138 let name = Path::new(program)
139 .file_name()
140 .and_then(|value| value.to_str())
141 .unwrap_or(program)
142 .to_ascii_lowercase();
143 matches!(
144 name.as_str(),
145 "cargo"
146 | "rustc"
147 | "rustup"
148 | "rustfmt"
149 | "clippy"
150 | "npm"
151 | "node"
152 | "yarn"
153 | "pnpm"
154 | "bun"
155 | "go"
156 | "python"
157 | "python3"
158 | "pip"
159 | "pip3"
160 | "java"
161 | "javac"
162 | "mvn"
163 | "gradle"
164 | "make"
165 | "cmake"
166 | "gcc"
167 | "g++"
168 | "clang"
169 | "clang++"
170 | "which"
171 )
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_is_cargo_command() {
180 assert!(is_cargo_command("cargo"));
181 assert!(is_cargo_command("Cargo"));
182 assert!(is_cargo_command("/usr/bin/cargo"));
183 assert!(is_cargo_command("~/.cargo/bin/cargo"));
184 assert!(!is_cargo_command("rustc"));
185 assert!(!is_cargo_command("npm"));
186 assert!(!is_cargo_command("cargo-watch"));
187 }
188
189 #[test]
190 fn test_is_cargo_command_string() {
191 assert!(is_cargo_command_string("cargo check"));
192 assert!(is_cargo_command_string("cargo test --lib"));
193 assert!(is_cargo_command_string("cargo build --release"));
194 assert!(is_cargo_command_string(" cargo clippy "));
195 assert!(is_cargo_command_string("cargo"));
196 assert!(!is_cargo_command_string("rustc --version"));
197 assert!(!is_cargo_command_string("npm install"));
198 assert!(!is_cargo_command_string("echo cargo"));
199 }
200
201 #[test]
202 fn test_is_long_running_command() {
203 assert!(is_long_running_command("cargo"));
204 assert!(is_long_running_command("cmake"));
205 assert!(is_long_running_command("composer"));
206 assert!(is_long_running_command("bundle"));
207 assert!(is_long_running_command("poetry"));
208 assert!(!is_long_running_command("rg"));
209 }
210
211 #[test]
212 fn test_is_long_running_command_string() {
213 assert!(is_long_running_command_string("cargo test"));
214 assert!(is_long_running_command_string(" cmake --build ."));
215 assert!(is_long_running_command_string("composer install"));
216 assert!(is_long_running_command_string("bundle exec rake"));
217 assert!(is_long_running_command_string("poetry install"));
218 assert!(!is_long_running_command_string("rg \"fn main\" ."));
219 }
220
221 #[test]
222 fn test_is_sandbox_wrapper_program() {
223 assert!(is_sandbox_wrapper_program("sandbox-exec", &[]));
224 assert!(is_sandbox_wrapper_program("vtcode-linux-sandbox", &["--sandbox-policy".to_string()]));
225 assert!(!is_sandbox_wrapper_program("bash", &["-lc".to_string(), "ls".to_string()]));
226 }
227}