Skip to main content

sqlite_graphrag/spawn/
claude_adapter.rs

1//! Claude Code version adapter (v1.0.75 — G22)
2
3use super::compat_matrix::claude_capabilities;
4use super::executor_version::ExecutorVersion;
5use super::{CompatMode, ParsedOutput, VersionAdapter};
6use crate::errors::AppError;
7use async_trait::async_trait;
8use std::process::Command;
9
10pub struct ClaudeAdapter;
11
12#[async_trait]
13impl VersionAdapter for ClaudeAdapter {
14    fn name(&self) -> &'static str {
15        "claude"
16    }
17
18    async fn detect(&self) -> Result<ExecutorVersion, AppError> {
19        let output = Command::new("claude").arg("--version").output();
20        match output {
21            Ok(out) => {
22                let raw = String::from_utf8_lossy(&out.stdout).trim().to_string();
23                if raw.is_empty() {
24                    let raw = String::from_utf8_lossy(&out.stderr).trim().to_string();
25                    if raw.is_empty() {
26                        return Ok(ExecutorVersion::unknown());
27                    }
28                    return ExecutorVersion::parse(&raw);
29                }
30                ExecutorVersion::parse(&raw)
31            }
32            Err(_) => Ok(ExecutorVersion::unknown()),
33        }
34    }
35
36    fn capabilities_for(&self, version: &ExecutorVersion) -> super::ExecutorCapabilities {
37        claude_capabilities(version)
38    }
39
40    fn build_args(
41        &self,
42        prompt: &str,
43        caps: &super::ExecutorCapabilities,
44        _compat_mode: CompatMode,
45    ) -> Vec<String> {
46        let mut args = vec!["-p".to_string()];
47        args.extend(caps.default_flags.clone());
48        args.push(prompt.to_string());
49        args
50    }
51
52    fn parse_output(&self, raw_stdout: &str, raw_stderr: &str, exit_code: i32) -> ParsedOutput {
53        let mut items = Vec::new();
54        for line in raw_stdout.lines() {
55            let trimmed = line.trim();
56            if trimmed.is_empty() {
57                continue;
58            }
59            if let Ok(v) = serde_json::from_str::<serde_json::Value>(trimmed) {
60                items.push(v);
61            } else if let Ok(v) =
62                serde_json::from_str::<serde_json::Value>(&format!("{{{trimmed}}}"))
63            {
64                items.push(v);
65            }
66        }
67        ParsedOutput {
68            items,
69            raw_stdout: raw_stdout.to_string(),
70            raw_stderr: raw_stderr.to_string(),
71            exit_code,
72        }
73    }
74}