Skip to main content

microclaw_engine/internal/tool_runtime/
command_runner.rs

1use std::path::Path;
2
3pub struct CommandSpec {
4    pub program: String,
5    pub args: Vec<String>,
6}
7
8pub fn shell_command(command: &str) -> CommandSpec {
9    if cfg!(target_os = "windows") {
10        CommandSpec {
11            program: "powershell".to_string(),
12            args: vec![
13                "-NoProfile".to_string(),
14                "-NonInteractive".to_string(),
15                "-Command".to_string(),
16                command.to_string(),
17            ],
18        }
19    } else {
20        let shell = std::env::var("SHELL")
21            .ok()
22            .filter(|s| !s.trim().is_empty())
23            .unwrap_or_else(|| "/bin/sh".to_string());
24        CommandSpec {
25            program: shell,
26            args: vec!["-c".to_string(), command.to_string()],
27        }
28    }
29}
30
31pub fn agent_browser_program() -> String {
32    if cfg!(target_os = "windows") {
33        "agent-browser.cmd".to_string()
34    } else {
35        "agent-browser".to_string()
36    }
37}
38
39pub fn build_command(spec: &CommandSpec, working_dir: Option<&Path>) -> tokio::process::Command {
40    let mut cmd = tokio::process::Command::new(&spec.program);
41    cmd.args(&spec.args);
42    if let Some(dir) = working_dir {
43        cmd.current_dir(dir);
44    }
45    cmd
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn test_shell_command_shape() {
54        let spec = shell_command("echo hello");
55        assert!(!spec.program.is_empty());
56        assert!(!spec.args.is_empty());
57    }
58
59    #[test]
60    fn test_agent_browser_program_not_empty() {
61        let p = agent_browser_program();
62        assert!(!p.trim().is_empty());
63    }
64}