Skip to main content

claude_code/
lib.rs

1#![forbid(unsafe_code)]
2//! Async helper around the Claude Code CLI (`claude`) focused on the headless `--print` flow.
3//!
4//! This crate intentionally does **not** attempt to wrap interactive default mode (no `--print`)
5//! as a parity target. It shells out to a locally installed/pinned `claude` binary.
6
7use std::{
8    future::Future,
9    pin::Pin,
10    sync::{
11        atomic::{AtomicBool, Ordering},
12        Arc,
13    },
14};
15
16use futures_core::Stream;
17use tokio::sync::Notify;
18
19mod builder;
20mod cli;
21mod client;
22mod commands;
23mod error;
24mod home;
25mod process;
26mod stream_json;
27pub mod wrapper_coverage_manifest;
28
29pub use builder::ClaudeClientBuilder;
30pub use client::ClaudeClient;
31pub use client::ClaudeSetupTokenSession;
32pub use commands::command::ClaudeCommandRequest;
33pub use commands::doctor::ClaudeDoctorRequest;
34pub use commands::mcp::{
35    McpAddFromClaudeDesktopRequest, McpAddJsonRequest, McpAddRequest, McpGetRequest,
36    McpRemoveRequest, McpScope, McpServeRequest, McpTransport,
37};
38pub use commands::plugin::{
39    PluginDisableRequest, PluginEnableRequest, PluginInstallRequest, PluginListRequest,
40    PluginManifestMarketplaceRequest, PluginManifestRequest, PluginMarketplaceAddRequest,
41    PluginMarketplaceListRequest, PluginMarketplaceRemoveRequest, PluginMarketplaceRepoRequest,
42    PluginMarketplaceRequest, PluginMarketplaceUpdateRequest, PluginRequest,
43    PluginUninstallRequest, PluginUpdateRequest, PluginValidateRequest,
44};
45pub use commands::print::{
46    ClaudeChromeMode, ClaudeInputFormat, ClaudeOutputFormat, ClaudePrintRequest,
47};
48pub use commands::setup_token::ClaudeSetupTokenRequest;
49pub use commands::update::ClaudeUpdateRequest;
50pub use error::{ClaudeCodeError, StreamJsonLineError};
51pub use home::{
52    ClaudeHomeLayout, ClaudeHomeSeedLevel, ClaudeHomeSeedOutcome, ClaudeHomeSeedRequest,
53};
54pub use stream_json::{parse_stream_json_lines, StreamJsonLine, StreamJsonLineOutcome};
55pub use stream_json::{
56    ClaudeStreamEvent, ClaudeStreamJsonErrorCode, ClaudeStreamJsonEvent,
57    ClaudeStreamJsonParseError, ClaudeStreamJsonParser,
58};
59
60pub use process::CommandOutput;
61
62pub type DynClaudeStreamJsonEventStream =
63    Pin<Box<dyn Stream<Item = Result<ClaudeStreamJsonEvent, ClaudeStreamJsonParseError>> + Send>>;
64
65pub type DynClaudeStreamJsonCompletion =
66    Pin<Box<dyn Future<Output = Result<std::process::ExitStatus, ClaudeCodeError>> + Send>>;
67
68#[derive(Clone)]
69pub struct ClaudeTerminationHandle {
70    inner: Arc<ClaudeTerminationInner>,
71}
72
73#[derive(Debug)]
74struct ClaudeTerminationInner {
75    requested: AtomicBool,
76    notify: Notify,
77}
78
79impl ClaudeTerminationHandle {
80    fn new() -> Self {
81        Self {
82            inner: Arc::new(ClaudeTerminationInner {
83                requested: AtomicBool::new(false),
84                notify: Notify::new(),
85            }),
86        }
87    }
88
89    pub fn request_termination(&self) {
90        if !self.inner.requested.swap(true, Ordering::SeqCst) {
91            self.inner.notify.notify_waiters();
92        }
93    }
94
95    fn is_requested(&self) -> bool {
96        self.inner.requested.load(Ordering::SeqCst)
97    }
98
99    async fn requested(&self) {
100        if self.is_requested() {
101            return;
102        }
103
104        let notified = self.inner.notify.notified();
105        if self.is_requested() {
106            return;
107        }
108
109        notified.await;
110    }
111}
112
113impl std::fmt::Debug for ClaudeTerminationHandle {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        f.debug_struct("ClaudeTerminationHandle")
116            .field("requested", &self.is_requested())
117            .finish()
118    }
119}
120
121pub struct ClaudePrintStreamJsonHandle {
122    pub events: DynClaudeStreamJsonEventStream,
123    pub completion: DynClaudeStreamJsonCompletion,
124}
125
126impl std::fmt::Debug for ClaudePrintStreamJsonHandle {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        f.debug_struct("ClaudePrintStreamJsonHandle")
129            .field("events", &"<stream>")
130            .field("completion", &"<future>")
131            .finish()
132    }
133}
134
135pub struct ClaudePrintStreamJsonControlHandle {
136    pub events: DynClaudeStreamJsonEventStream,
137    pub completion: DynClaudeStreamJsonCompletion,
138    pub termination: ClaudeTerminationHandle,
139}
140
141impl std::fmt::Debug for ClaudePrintStreamJsonControlHandle {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        f.debug_struct("ClaudePrintStreamJsonControlHandle")
144            .field("events", &"<stream>")
145            .field("completion", &"<future>")
146            .field("termination", &self.termination)
147            .finish()
148    }
149}