use std::io::Write as IoWrite;
use std::path::Path;
use std::process::{Command, Stdio};
use serde::{Deserialize, Serialize};
use crate::error::{DotAgentError, Result};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LlmConfig {
#[serde(default)]
pub enabled: bool,
}
pub fn check_claude_cli() -> bool {
Command::new("claude")
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
pub fn execute_claude(working_dir: &Path, prompt: &str) -> Result<String> {
let mut cmd = Command::new("claude");
cmd.arg("--print");
cmd.arg("--dangerously-skip-permissions");
cmd.current_dir(working_dir);
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::piped());
let mut child = cmd
.spawn()
.map_err(|e| DotAgentError::ClaudeExecutionFailed {
message: format!("Failed to spawn claude: {}", e),
})?;
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(prompt.as_bytes())
.map_err(|e| DotAgentError::ClaudeExecutionFailed {
message: format!("Failed to write prompt: {}", e),
})?;
}
let output = child
.wait_with_output()
.map_err(|e| DotAgentError::ClaudeExecutionFailed {
message: format!("Execution failed: {}", e),
})?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(DotAgentError::ClaudeExecutionFailed {
message: format!("Claude exited with error: {}", stderr),
});
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn require_claude_cli() -> Result<()> {
if !check_claude_cli() {
return Err(DotAgentError::ClaudeNotFound);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_llm_config_default() {
let config = LlmConfig::default();
assert!(!config.enabled);
}
#[test]
fn test_llm_config_deserialize() {
let toml_str = r#"
enabled = true
"#;
let config: LlmConfig = toml::from_str(toml_str).unwrap();
assert!(config.enabled);
}
#[test]
fn test_llm_config_deserialize_empty() {
let toml_str = "";
let config: LlmConfig = toml::from_str(toml_str).unwrap();
assert!(!config.enabled);
}
}