atomr_agents_coding_cli_core/
vendor.rs1use std::collections::BTreeMap;
9use std::ffi::OsString;
10use std::path::{Path, PathBuf};
11
12use async_trait::async_trait;
13use serde::{Deserialize, Serialize};
14
15use crate::error::{MapperError, ParseError};
16use crate::event::CodingCliEvent;
17use crate::projection::ConceptProjection;
18use crate::request::CliRequest;
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum CliVendorKind {
25 Claude,
26 Codex,
27 Antigravity,
28 Cursor,
29 Aider,
30 Other(String),
31}
32
33impl CliVendorKind {
34 pub fn as_str(&self) -> &str {
35 match self {
36 Self::Claude => "claude",
37 Self::Codex => "codex",
38 Self::Antigravity => "antigravity",
39 Self::Cursor => "cursor",
40 Self::Aider => "aider",
41 Self::Other(s) => s.as_str(),
42 }
43 }
44}
45
46impl std::fmt::Display for CliVendorKind {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.write_str(self.as_str())
49 }
50}
51
52#[derive(Debug, Clone)]
57pub struct CliCommand {
58 pub program: PathBuf,
59 pub args: Vec<OsString>,
60 pub env: BTreeMap<String, String>,
61 pub workdir: PathBuf,
63 pub allocate_pty: bool,
65}
66
67impl CliCommand {
68 pub fn new(program: impl Into<PathBuf>, workdir: impl Into<PathBuf>) -> Self {
69 Self {
70 program: program.into(),
71 args: Vec::new(),
72 env: BTreeMap::new(),
73 workdir: workdir.into(),
74 allocate_pty: false,
75 }
76 }
77
78 pub fn arg(mut self, a: impl Into<OsString>) -> Self {
79 self.args.push(a.into());
80 self
81 }
82
83 pub fn arg_pair(mut self, flag: impl Into<OsString>, value: impl Into<OsString>) -> Self {
84 self.args.push(flag.into());
85 self.args.push(value.into());
86 self
87 }
88
89 pub fn envv(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
90 self.env.insert(k.into(), v.into());
91 self
92 }
93
94 pub fn with_pty(mut self) -> Self {
95 self.allocate_pty = true;
96 self
97 }
98}
99
100pub trait CliEventParser: Send {
106 fn parse_line(&mut self, line: &str) -> Result<Vec<CodingCliEvent>, ParseError>;
107 fn flush(&mut self) -> Result<Vec<CodingCliEvent>, ParseError>;
108}
109
110#[async_trait]
112pub trait CliVendor: Send + Sync {
113 fn kind(&self) -> CliVendorKind;
115
116 fn label(&self) -> &str;
118
119 fn build_headless_command(&self, req: &CliRequest, workdir: &Path) -> CliCommand;
121
122 fn build_interactive_command(&self, req: &CliRequest, workdir: &Path) -> CliCommand;
124
125 fn new_parser(&self) -> Box<dyn CliEventParser>;
127
128 async fn materialize_config(
133 &self,
134 projection: &ConceptProjection,
135 workdir: &Path,
136 ) -> Result<(), MapperError>;
137
138 async fn is_available(&self) -> bool {
142 true
143 }
144}