pub struct ClaudeBuilder { /* private fields */ }Expand description
Implementations§
Source§impl ClaudeBuilder
impl ClaudeBuilder
Sourcepub fn binary(self, path: impl Into<PathBuf>) -> Self
pub fn binary(self, path: impl Into<PathBuf>) -> Self
Set the path to the claude binary.
If not set, the binary is resolved from PATH using which.
Sourcepub fn working_dir(self, path: impl Into<PathBuf>) -> Self
pub fn working_dir(self, path: impl Into<PathBuf>) -> Self
Set the working directory for all commands.
The spawned process will use this as its current directory.
Sourcepub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
pub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
Add an environment variable to pass to all commands.
Sourcepub fn envs(
self,
vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self
pub fn envs( self, vars: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>, ) -> Self
Add multiple environment variables.
Sourcepub fn clear_env(self) -> Self
pub fn clear_env(self) -> Self
Clear the inherited environment of every spawned Claude CLI child.
By default, children inherit the parent process environment and
env / envs add or replace entries. With
this option enabled, the inherited environment is cleared first and
only explicitly configured entries are applied. The order in which
clear_env, env, and envs are called does not affect the result.
Callers normally need to rebuild a minimal environment including
PATH, locale settings, the Claude config directory, and the intended
authentication selector.
This controls only the direct child process environment. It is not an operating-system sandbox and does not prevent a same-UID child from reading accessible files or inspecting other processes where the OS permits it.
§Example
use claude_wrapper::Claude;
let claude = Claude::builder()
.clear_env()
.env("PATH", "/usr/local/bin:/usr/bin:/bin")
.env("CLAUDE_CONFIG_DIR", "/srv/claude/config")
.build()?;Sourcepub fn timeout_secs(self, seconds: u64) -> Self
pub fn timeout_secs(self, seconds: u64) -> Self
Set a default timeout for all commands (in seconds).
Sourcepub fn arg(self, arg: impl Into<String>) -> Self
pub fn arg(self, arg: impl Into<String>) -> Self
Add a global argument applied to all commands.
This is an escape hatch for flags not yet covered by the API.
Sourcepub fn retry(self, policy: RetryPolicy) -> Self
pub fn retry(self, policy: RetryPolicy) -> Self
Set a default retry policy for all commands.
Individual commands can override this via their own retry settings.
§Example
use claude_wrapper::{Claude, RetryPolicy};
use std::time::Duration;
let claude = Claude::builder()
.retry(RetryPolicy::new()
.max_attempts(3)
.initial_backoff(Duration::from_secs(2))
.exponential()
.retry_on_timeout(true))
.build()?;Sourcepub fn tested_cli_version_range(self, min: CliVersion, max: CliVersion) -> Self
pub fn tested_cli_version_range(self, min: CliVersion, max: CliVersion) -> Self
Declare the inclusive [min, max] range of claude CLI
versions this client has been tested against.
The wrapper does not enforce the range – nothing errors when
it’s set wrong. Use Claude::cli_version_status (or its
sync mirror) at startup to classify the actually-installed CLI
against this declaration; that call returns a typed
CliVersionStatus AND emits a tracing::warn! when
outside the range. Hosts (claude-server, application code)
can additionally surface the status to operators.
§Why this exists
CLI semantics drift across minor / patch releases (e.g.
claude agents was repurposed in 2.1.143). The min floor
lets us say “we know it’s broken below this”; the max ceiling
lets us say “we haven’t verified above this – proceed but
expect surprises.”
§You usually do not need this
The crate declares its own range in
TESTED_CLI_VERSION_MIN / TESTED_CLI_VERSION_MAX, and
version checks use it by default, because only the crate knows
what it was built and tested against. Set this only when a host
has verified a different range itself.
§Example
use claude_wrapper::{Claude, CliVersion};
let claude = Claude::builder()
.tested_cli_version_range(CliVersion::new(2, 1, 0), CliVersion::new(2, 1, 999))
.build()?;
// Run once at startup to log a warning if the CLI is out of range.
let _status = claude.cli_version_status().await?;Sourcepub fn process_group(self, enabled: bool) -> Self
pub fn process_group(self, enabled: bool) -> Self
Control whether spawned claude children are placed in their
own process group on Unix. Defaults to true.
Own group (the default): cancellation and timeouts kill the child’s owned process group. Descendants that create another group require cooperative cleanup by the CLI. The child no longer shares the host terminal’s process group, so terminal-generated signals (Ctrl-C) do not reach it; terminating a run is the wrapper’s job via drop, timeout, or explicit kill. This is the right contract for supervisors (daemons, MCP servers, worker queues).
Shared group (false): the child stays in the host’s process
group, so a terminal Ctrl-C reaches the whole run directly, but
a wrapper-side kill only reaches the direct child and any
subprocesses it spawned for tool use survive it. This is the
right contract for terminal-attached hosts that shell out
synchronously and rely on the terminal as the supervisor.
No effect on non-Unix targets.
Sourcepub fn kill_grace(self, grace: Duration) -> Self
pub fn kill_grace(self, grace: Duration) -> Self
Grace period before SIGKILL on timeout, explicit cancellation, or a duplex shutdown overrun (Unix). Default: none; kills are immediate.
With nonzero grace, the owned process group first receives
termination_signal, SIGTERM by default.
Output is drained and discarded while the CLI gets a chance to clean
up, then SIGKILL follows after the full grace. Keep the grace short
(500ms to 2s). Detached descendants require cooperation from the CLI;
group signals cannot guarantee their termination.
Dropping a future cannot wait and always uses immediate SIGKILL.
With process_group(false), grace and the soft
signal are skipped and only the direct child is killed.
Sourcepub fn termination_signal(self, signal: TerminationSignal) -> Self
pub fn termination_signal(self, signal: TerminationSignal) -> Self
Select the first signal for graceful shutdown. Defaults to SIGTERM.
Requires nonzero kill_grace and an owned Unix
process group. SIGINT lets a cooperating CLI cancel its detached tools
before the final group SIGKILL. Other platforms and drop cleanup are
unchanged. Cancellation and timeout still return their original error
even when the CLI exits successfully during this grace.
use claude_wrapper::{Claude, TerminationSignal};
use std::time::Duration;
let claude = Claude::builder()
.termination_signal(TerminationSignal::Interrupt)
.kill_grace(Duration::from_secs(2))
.build()?;Sourcepub fn output_limit(self, bytes: usize) -> Self
pub fn output_limit(self, bytes: usize) -> Self
Ceiling, in bytes, on stdout and stderr captured from a buffered run. Default: none; capture grows to whatever the child prints.
The limit applies to each stream independently, so a client with
a 1 MiB ceiling holds at most 2 MiB across the pair. Reaching it
on either stream terminates the child’s process group (honoring
kill_grace), reaps the direct child, and
returns Error::OutputLimitExceeded rather than a truncated
success: continuing to read past the ceiling would defeat the
bound, and presenting a partial answer as a complete one is worse
than failing. Output captured before the ceiling tripped is
logged at warn.
This bounds the buffered paths (execute, execute_json, and
their cancellable and blocking forms). Streaming runs deliver
events as they arrive and are not affected.
Sourcepub fn on_spawn(self, observer: SpawnObserver) -> Self
pub fn on_spawn(self, observer: SpawnObserver) -> Self
Observe every child this client spawns, at spawn time.
The observer receives a SpawnInfo before the run produces output,
which is what makes it useful to a supervisor: recording the pid only
once a run finishes leaves nothing to reconcile after a crash
mid-run. Fires for one-shot runs, streaming runs, and duplex sessions
alike, and on retry it fires once per attempt, since each attempt is a
distinct process.
The observer runs inline on the spawning thread, so it must not block. Write a pidfile or push to a channel; do not do I/O that can stall.
§Why a callback rather than a return value
The crash case needs the pid to be durable before the run can be orphaned. A pid on the result type arrives too late for exactly the scenario that motivates recording it.
§Example
use std::sync::Arc;
use claude_wrapper::Claude;
let claude = Claude::builder()
.on_spawn(Arc::new(|info| {
eprintln!("spawned pid {} (group {:?})", info.pid, info.pgid);
}))
.build()?;Sourcepub fn die_with_parent(self, enabled: bool) -> Self
pub fn die_with_parent(self, enabled: bool) -> Self
Ask the kernel to kill spawned children when this process dies.
Linux only. Check die_with_parent_supported
rather than assuming; elsewhere this is accepted and does nothing.
§The problem it addresses
Every other cleanup path in this crate is a destructor: kill_on_drop,
and the process-group kill on drop, timeout, or stream error. None of
them run when this process is SIGKILLed. The child is then reparented
to init, and because it leads its own process group (see
process_group) terminal signals cannot reach it
either. It keeps running, keeps billing, and keeps appending to the
session transcript, so a restarted supervisor that resumes the same
session id can find itself interleaving with an orphan still writing.
On Linux PR_SET_PDEATHSIG closes that: the kernel delivers SIGKILL to
the child the moment its parent dies, with no cooperation from either
side.
§What it does not cover
- Non-Linux targets. macOS has no equivalent. A supervisor that
needs the guarantee there has to poll and kill by pid; recording the
pid is what
on_spawnis for. - Re-parenting. The signal fires when the immediate parent dies. If the crate’s caller is itself an intermediate process that exits normally, the child dies then, which is usually what you want but is worth knowing if you daemonize between building the client and spawning.
- The fork/prctl window. Handled: the hook re-checks
getppid()after arming and exits if the parent already changed. Without that check a parent dying in that window leaves exactly the orphan this option exists to prevent.
Off by default, because killing children on parent exit is the right default for a supervisor and the wrong one for a CLI that deliberately backgrounds work.