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