1use clau_core::{Error, Result, Config, StreamFormat};
2use tokio::process::Command;
3use tokio::time::{timeout, Duration};
4use tracing::debug;
5
6pub async fn execute_claude(config: &Config, query: &str) -> Result<String> {
8
9 let claude_binary = which::which("claude").map_err(|_| Error::BinaryNotFound)?;
10
11 let mut cmd = Command::new(claude_binary);
12
13 cmd.arg("-p");
15
16 match config.stream_format {
18 StreamFormat::Json => {
19 cmd.arg("--output-format").arg("json");
20 }
21 StreamFormat::StreamJson => {
22 cmd.arg("--output-format").arg("stream-json");
23 cmd.arg("--verbose");
25 }
26 StreamFormat::Text => {
27 }
29 }
30
31 if config.verbose && config.stream_format != StreamFormat::StreamJson {
33 cmd.arg("--verbose");
34 }
35
36 if let Some(system_prompt) = &config.system_prompt {
38 cmd.arg("--system-prompt").arg(system_prompt);
39 }
40
41 if let Some(model) = &config.model {
42 cmd.arg("--model").arg(model);
43 }
44
45 if let Some(mcp_config_path) = &config.mcp_config_path {
46 cmd.arg("--mcp-config").arg(mcp_config_path);
47 }
48
49 if let Some(allowed_tools) = &config.allowed_tools {
50 for tool in allowed_tools {
51 cmd.arg("--allowedTools").arg(tool);
52 }
53 }
54
55 if let Some(max_tokens) = &config.max_tokens {
56 cmd.arg("--max-tokens").arg(max_tokens.to_string());
57 }
58
59 cmd.arg(query);
61
62 debug!("Executing Claude command: {:?}", cmd);
63
64 let timeout_duration = Duration::from_secs(config.timeout_secs.unwrap_or(30));
66 let output = timeout(timeout_duration, cmd.output())
67 .await
68 .map_err(|_| Error::Timeout(config.timeout_secs.unwrap_or(30)))??;
69
70 if !output.status.success() {
71 let stderr = String::from_utf8_lossy(&output.stderr);
72 return Err(Error::ProcessError(format!("Claude command failed: {}", stderr)));
73 }
74
75 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
76 Ok(stdout)
77}