sqlite_graphrag/spawn/
opencode_adapter.rs1use super::compat_matrix::opencode_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 OpencodeAdapter;
12
13#[async_trait]
14impl VersionAdapter for OpencodeAdapter {
15 fn name(&self) -> &'static str {
16 "opencode"
17 }
18
19 async fn detect(&self) -> Result<ExecutorVersion, AppError> {
20 let output = Command::new("opencode").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 opencode_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 vec![
48 "run".to_string(),
49 "--format".to_string(),
50 "json".to_string(),
51 "--dangerously-skip-permissions".to_string(),
52 prompt.to_string(),
53 ]
54 }
55
56 fn parse_output(&self, raw_stdout: &str, raw_stderr: &str, exit_code: i32) -> ParsedOutput {
57 let mut items = Vec::new();
58 for line in raw_stdout.lines() {
59 let trimmed = line.trim();
60 if trimmed.is_empty() {
61 continue;
62 }
63 if let Ok(v) = serde_json::from_str::<serde_json::Value>(trimmed) {
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}