use std::fs;
use std::process::{Command, Stdio};
use std::time::Duration;
use std::thread;
use std::sync::mpsc;
use anyhow::Result;
pub fn execute_command(command: &str) -> Result<String> {
let timeout_duration = Duration::from_secs(120);
let child = Command::new("sh")
.arg("-c")
.arg(command)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let output = child.wait_with_output();
let _ = tx.send(output);
});
let output = match rx.recv_timeout(timeout_duration) {
Ok(Ok(output)) => output,
Ok(Err(e)) => return Err(anyhow::anyhow!("Command failed: {}", e)),
Err(_) => {
return Err(anyhow::anyhow!("Command timed out after {} seconds. Consider using shorter commands or adding explicit timeouts.", timeout_duration.as_secs()));
}
};
let mut msg = String::new();
if !output.stdout.is_empty() {
msg.push_str("=== STDOUT ===\n");
msg.push_str(&String::from_utf8_lossy(&output.stdout));
msg.push('\n');
}
if !output.stderr.is_empty() {
msg.push_str("=== STDERR ===\n");
msg.push_str(&String::from_utf8_lossy(&output.stderr));
msg.push('\n');
}
msg.push_str(&format!(
"Exit code: {}",
output.status.code().unwrap_or(-1)
));
Ok(msg)
}
pub fn process_file_reads(files: &[String]) -> String {
let mut result = String::new();
for file_path in files {
let trimmed_path = file_path.trim();
match fs::read_to_string(trimmed_path) {
Ok(content) => {
result.push_str(&format!("=== {trimmed_path} ===\n{content}\n"));
}
Err(e) => {
result.push_str(&format!("Error reading {file_path}: {e}\n"));
}
}
}
result
}
pub fn is_exit_command(input: &str) -> bool {
let lower = input.trim().to_lowercase();
matches!(
lower.as_str(),
"exit" | "quit" | "bye" | "goodbye" | "/exit" | "/quit"
)
}