1pub mod command;
43pub mod error;
44pub mod exec;
45pub mod mcp_config;
46pub mod streaming;
47pub mod types;
48
49use std::collections::HashMap;
50use std::path::{Path, PathBuf};
51use std::time::Duration;
52
53pub use command::ClaudeCommand;
54pub use command::agents::AgentsCommand;
55pub use command::auth::AuthStatusCommand;
56pub use command::doctor::DoctorCommand;
57pub use command::marketplace::{
58 MarketplaceAddCommand, MarketplaceListCommand, MarketplaceRemoveCommand,
59 MarketplaceUpdateCommand,
60};
61pub use command::mcp::{
62 McpAddCommand, McpAddFromDesktopCommand, McpAddJsonCommand, McpGetCommand, McpListCommand,
63 McpRemoveCommand, McpResetProjectChoicesCommand,
64};
65pub use command::plugin::{
66 PluginDisableCommand, PluginEnableCommand, PluginInstallCommand, PluginListCommand,
67 PluginUninstallCommand, PluginUpdateCommand, PluginValidateCommand,
68};
69pub use command::query::QueryCommand;
70pub use command::raw::RawCommand;
71pub use command::version::VersionCommand;
72pub use error::{Error, Result};
73pub use exec::CommandOutput;
74pub use mcp_config::{McpConfigBuilder, McpServerConfig};
75pub use types::*;
76
77#[derive(Debug, Clone)]
81pub struct Claude {
82 pub(crate) binary: PathBuf,
83 pub(crate) working_dir: Option<PathBuf>,
84 pub(crate) env: HashMap<String, String>,
85 pub(crate) global_args: Vec<String>,
86 pub(crate) timeout: Option<Duration>,
87}
88
89impl Claude {
90 #[must_use]
92 pub fn builder() -> ClaudeBuilder {
93 ClaudeBuilder::default()
94 }
95
96 #[must_use]
98 pub fn binary(&self) -> &Path {
99 &self.binary
100 }
101
102 #[must_use]
104 pub fn working_dir(&self) -> Option<&Path> {
105 self.working_dir.as_deref()
106 }
107
108 #[must_use]
110 pub fn with_working_dir(&self, dir: impl Into<PathBuf>) -> Self {
111 let mut clone = self.clone();
112 clone.working_dir = Some(dir.into());
113 clone
114 }
115}
116
117#[derive(Debug, Default)]
133pub struct ClaudeBuilder {
134 binary: Option<PathBuf>,
135 working_dir: Option<PathBuf>,
136 env: HashMap<String, String>,
137 global_args: Vec<String>,
138 timeout: Option<Duration>,
139}
140
141impl ClaudeBuilder {
142 #[must_use]
146 pub fn binary(mut self, path: impl Into<PathBuf>) -> Self {
147 self.binary = Some(path.into());
148 self
149 }
150
151 #[must_use]
155 pub fn working_dir(mut self, path: impl Into<PathBuf>) -> Self {
156 self.working_dir = Some(path.into());
157 self
158 }
159
160 #[must_use]
162 pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
163 self.env.insert(key.into(), value.into());
164 self
165 }
166
167 #[must_use]
169 pub fn envs(
170 mut self,
171 vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
172 ) -> Self {
173 for (k, v) in vars {
174 self.env.insert(k.into(), v.into());
175 }
176 self
177 }
178
179 #[must_use]
181 pub fn timeout_secs(mut self, seconds: u64) -> Self {
182 self.timeout = Some(Duration::from_secs(seconds));
183 self
184 }
185
186 #[must_use]
188 pub fn timeout(mut self, duration: Duration) -> Self {
189 self.timeout = Some(duration);
190 self
191 }
192
193 #[must_use]
197 pub fn arg(mut self, arg: impl Into<String>) -> Self {
198 self.global_args.push(arg.into());
199 self
200 }
201
202 pub fn build(self) -> Result<Claude> {
204 let binary = match self.binary {
205 Some(path) => path,
206 None => which::which("claude").map_err(|_| Error::NotFound)?,
207 };
208
209 Ok(Claude {
210 binary,
211 working_dir: self.working_dir,
212 env: self.env,
213 global_args: self.global_args,
214 timeout: self.timeout,
215 })
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_builder_with_binary() {
225 let claude = Claude::builder()
226 .binary("/usr/local/bin/claude")
227 .env("FOO", "bar")
228 .timeout_secs(60)
229 .build()
230 .unwrap();
231
232 assert_eq!(claude.binary, PathBuf::from("/usr/local/bin/claude"));
233 assert_eq!(claude.env.get("FOO").unwrap(), "bar");
234 assert_eq!(claude.timeout, Some(Duration::from_secs(60)));
235 }
236
237 #[test]
238 fn test_builder_global_args() {
239 let claude = Claude::builder()
240 .binary("/usr/local/bin/claude")
241 .arg("--verbose")
242 .build()
243 .unwrap();
244
245 assert_eq!(claude.global_args, vec!["--verbose"]);
246 }
247}