claude-agent-sdk 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec4f48758138c2a833ba6adc419753f8b8c1b7b2555824c5b2a497d6ab191c8a
4
- data.tar.gz: 48787d59ffcbd3fa6c90b953234064065aad8cfc87ddc85d77f042d87a9bb250
3
+ metadata.gz: c5c70ede77de755091c7587cb3bed3ea95075ed6db34b7a2cc4e3214db9adcb5
4
+ data.tar.gz: b4d77a1e0888dc0ef3e436e933ee2334d6912b087e440fb88af0765e34703e10
5
5
  SHA512:
6
- metadata.gz: dce3bf872a4bfe12c72248a9f036d2c706600679e9724200eef80da47a093c4e1d7be3e9ec7b895105a35f56f07dce26d8516b03c26c040fd60f8a53b594a785
7
- data.tar.gz: 4a51034acf467515e897a8739b3f0dc0c85392bd88d88fe90b21251af98f78b29a7fc5877305e7136c7fa5cf9a0e5b64edd718d20794e90fbb14614619ff6246
6
+ metadata.gz: '0658614c047b36d2cd7c8724c01e31b1bdcb4aac81246caf6e56c7afb9e8203bf2e7cedfd112c59c317fb0cd5359785737e8a4e33efa67f448ee47792a81fc17'
7
+ data.tar.gz: 2938e8804ee6396fdadb60be8345da7317628f853ec35947aba5f8542c732346d4a360aa0222f57977d18578e3689e0307d55337802f908ce4eed85543518122
data/CHANGELOG.md CHANGED
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.33.0] - 2026-09-21
11
+
12
+ Subagent capabilities for UI builders: metadata reads, background snapshots, cooperative callback cancellation, and the task/background/permission signals the CLI already emits — all raw data and controls, no status model. Ruby-ahead of the Python SDK (0.2.153).
13
+
14
+ ### Added
15
+ - `get_subagent_metadata` / `get_subagent_metadata_from_store`: read optional subagent metadata with original string keys, including type, spawning tool ID, parent agent, depth, and future CLI fields. Disk reads reuse transcript scoping without parsing the transcript; store reads select the latest metadata entry even before messages arrive. These are historical reads, not live-status queries.
16
+ - `background_tasks` and `session_crons` on `StopHookInput` / `SubagentStopHookInput`. Raw snapshots preserve unavailable (`nil`) versus explicitly empty (`[]`) and describe the parent session, not all foreground/background agents.
17
+ - Cooperative permission cancellation through `ToolPermissionContext#signal` (`CancellationSignal#cancelled?` / `#wait`) and the associated `request_id`. CLI cancellation, disconnect, EOF, and failed dispatch invalidate pending requests, including callbacks running on worker threads; normal decisions do not. User threads are not forcibly stopped, and late decisions after observed cancellation cannot become allow responses.
18
+ - `HookContext#signal` / `#request_id` use the same per-invocation cancellation contract, including hook timeouts. Thread callbacks can cooperate with cancellation; late hook output is discarded.
19
+ - A minimal subagent event subscription example and capability reference covering lifecycle events, metadata, background snapshots, and permission cancellation. UI and application status aggregation remain outside the SDK.
20
+ - Opt-in real-CLI subagent contract tests for ID/metadata/text correlation, permission cancellation on interrupt, and background completion/stop after a parent result. They self-skip without CLI credentials.
21
+ - Subagent UI signals the CLI already emits, read from the schema embedded in Claude Code CLI 2.1.278 (the Python SDK has none of these as of Python SDK 0.2.153; only partly verified live — a smoke run against CLI 2.1.278 confirmed the `task_started` fields and the targeted-miss `background_tasks` response; the rest is schema-derived). The SDK exposes raw data and controls only — no status aggregation.
22
+ - `TaskStartedMessage#subagent_type` / `#is_backgrounded` / `#spawn_depth`, `TaskProgressMessage#subagent_type`, `TaskNotificationMessage#reason` / `#resource_links` (raw Array, symbol keys with the wire spelling), the `#skip_transcript` / `#ambient` display flags on both `TaskStartedMessage` and `TaskNotificationMessage` (hints for the host — the SDK never filters frames or computes activity), and `TaskUpdatedMessage#is_backgrounded` / `#error` / `#end_time` / `#total_paused_ms` / `#description` derived from `patch` like `status`. `is_backgrounded` keeps `nil` (not reported) distinct from an explicit `false` (foreground, spawning tool call blocking). `TaskUpdatedMessage` now also reads a string-keyed `patch` on hand-built messages.
23
+ - `BackgroundTasksChangedMessage` (`background_tasks_changed`): the full set of live background tasks, a level signal with REPLACE semantics. The SDK's own stdin-close bookkeeping still deliberately ignores this frame.
24
+ - `PermissionDeniedMessage` (`permission_denied`): a tool call auto-denied without an interactive prompt, with `agent_id` for subagent routing (not a permission `request_id`). A best-effort advisory, not a complete denial feed — `ResultMessage#permission_denials` stays authoritative.
25
+ - `Client#background_tasks(tool_use_id: nil)` / `Query#background_tasks`: send in-flight foreground tasks to the background (Ctrl+B). Keyed by the spawning `tool_use_id`, not `task_id` / `agent_id`. The targeted form returns `{ backgrounded: true }` or `{ backgrounded: false }` — a definitive miss, after which no event is coming; `nil` is the explicit all-tasks form and returns `{}`. Because the CLI treats `''` as "all tasks", anything other than `nil` or a non-empty String raises `ArgumentError` before a request is written. `TaskUpdatedMessage#is_backgrounded` / `BackgroundTasksChangedMessage` report the lifecycle state that follows.
26
+ - `ClaudeAgentOptions#agent_progress_summaries`: request model-generated progress summaries for subagents; `TaskProgressMessage#summary` may then be present, and stays optional. Tri-state; `nil` omits `agentProgressSummaries` from the `initialize` request and `true` / `false` are forwarded verbatim. An enable switch, not a live toggle: CLI 2.1.278 only acts on a truthy value, so `false` is equivalent to unset.
27
+ - **Compatibility:** `background_tasks_changed` and `permission_denied` frames previously parsed as a generic `SystemMessage`. Both new classes subclass it, so `when SystemMessage` and `#data` consumers are unaffected; code matching on `message.class == SystemMessage` will no longer see them.
28
+
29
+ ### Fixed
30
+ - Best-effort control error/cancellation replies no longer leak a secondary connection error when the CLI has already exited. Original transport read errors still propagate to the consumer.
31
+ - Control-request tracking is identity-guarded: if the CLI ever reused an in-flight request ID, the first handler finishing no longer untracks the later request's cancellation signal or task, so EOF, disconnect, and `control_cancel_request` still invalidate it and its late decision cannot be sent as a success.
32
+ - `ClaudeAgentSDK.configure` defaults now merge by option, not by literal key spelling. `ClaudeAgentOptions` accepts symbol/string and snake_case/camelCase names, but the defaults merge compared raw keys, so a differently spelled per-call key rode along as a second entry: `'permissionMode' => nil` wiped a configured `permission_mode:` instead of inheriting it, and a Hash option such as `'env' => {...}` replaced the configured `env:` instead of merging into it. Unknown option names are still reported with the caller's own spelling.
33
+
34
+ ## [0.32.0] - 2026-09-17
35
+
36
+ Syncs the gem with Python SDK **0.2.153** (previously 0.2.147). The intervening Python releases 0.2.148–0.2.152 only bump the CLI binary Python bundles; this gem does not vendor a CLI, so they carry no Ruby-side change.
37
+
38
+ ### Added
39
+ - **`snapshot` on `SystemPromptPreset`, and a new `SystemPromptCustom` type** (port of Python [#1268](https://github.com/anthropics/claude-agent-sdk-python/pull/1268), v0.2.153). By default the CLI records the system prompt on a session's first request and reuses it on every later request, including after resume, so a changed `append` or custom prompt has no effect until the session is compacted or a new one starts. `snapshot: false` makes the CLI rebuild the prompt on every request instead — useful while iterating on prompt text across calls that resume the same session. `SystemPromptCustom` (`prompt:`, `snapshot:`) is the object form of a String prompt, so `snapshot` can travel with it; the `{ type: 'custom', prompt: '...', snapshot: false }` and `{ type: 'preset', ..., snapshot: false }` Hash forms are accepted too. The value rides on the control-protocol `initialize` request as `systemPromptSnapshot` (never as a CLI flag), so it applies to both `query()` and `Client`; `false` is sent explicitly and only an unset value is omitted. `SystemPromptFile` has no `snapshot`, and one given on a file Hash is ignored, as in Python. Requires Claude Code CLI 2.1.257 or later; before 2.1.265 a session with an `append` or custom prompt recorded it only when `snapshot` was `true`. Older CLIs silently ignore the field.
40
+ - **Compatibility:** a `{ type: 'custom', ... }` Hash previously fell through the command builder unrecognised — pushing no flag at all — and so silently activated the *default* Claude Code system prompt. It now forwards `--system-prompt <prompt>` exactly like a String, and a custom prompt without a String `prompt` raises `ArgumentError` at command-build time rather than falling through (Python raises `KeyError` on the same input).
41
+
10
42
  ## [0.31.0] - 2026-08-28
11
43
 
12
44
  Syncs the gem with Python SDK **0.2.147** (previously 0.2.134). The intervening Python releases 0.2.135/136/138/139/141–147 only bump the CLI binary Python bundles; this gem does not vendor a CLI, so they carry no Ruby-side change.
data/README.md CHANGED
@@ -1,143 +1,48 @@
1
- # Claude Agent SDK for Ruby
2
-
3
- ![Claude Agent SDK for Ruby banner](https://raw.githubusercontent.com/ya-luotao/claude-agent-sdk-ruby/main/assets/claude-agent-sdk-ruby-banner.png)
4
-
5
- [![Gem Version](https://badge.fury.io/rb/claude-agent-sdk.svg?icon=si%3Arubygems)](https://badge.fury.io/rb/claude-agent-sdk)
6
-
7
- An **unofficial, community-maintained** Ruby SDK for the [Claude Code](https://docs.claude.com/en/docs/claude-code-overview) agent runtime. Not affiliated with or supported by Anthropic.
8
-
9
- Official SDKs: [TypeScript](https://github.com/anthropics/claude-agent-sdk-typescript) · [Python](https://github.com/anthropics/claude-agent-sdk-python).
10
-
11
- ## Why a Ruby SDK?
12
-
13
- Ruby powers a massive ecosystem — Rails, Sidekiq, Kamal, countless production web apps — but has no official Claude Agent SDK. This gem fills that gap so Ruby and Rails developers can build AI agents, automate coding workflows, and integrate Claude into existing applications without switching languages or shelling out to Python/Node.
1
+ ![Claude Agent SDK for Ruby — a ruby connecting code to terminal, tools, and messages](assets/readme-banner.webp)
14
2
 
15
- All three SDKs share the same underlying mechanism: they spawn the `claude` CLI as a subprocess and communicate over stream-JSON on stdin/stdout. The wire protocol is identical, so Ruby gets the same capabilities as the official SDKs.
16
-
17
- ### Comparison with Official SDKs
3
+ # Claude Agent SDK for Ruby
18
4
 
19
- | Capability | TypeScript | Python | Ruby (this gem) |
20
- |---|:---:|:---:|:---:|
21
- | One-shot `query()` | ✅ | ✅ | ✅ |
22
- | Bidirectional `Client` | ✅ | ✅ | ✅ |
23
- | Streaming input | `AsyncIterable` | `AsyncIterable` | `Enumerator` |
24
- | Custom tools (SDK MCP servers) | `tool()` | `@tool` decorator | `create_tool` block |
25
- | Hooks (all 27 events) | ✅ | ✅ | ✅ |
26
- | Permission callbacks | ✅ | ✅ | ✅ |
27
- | Structured output | ✅ | ✅ | ✅ |
28
- | All 25 message types | ✅ | partial | ✅ |
29
- | [Sandbox](https://github.com/anthropic-experimental/sandbox-runtime) settings | ✅ | partial | ✅ |
30
- | Bare mode (`--bare`) | ✅ | ✅ | ✅ |
31
- | File checkpointing & rewind | ✅ | ✅ | ✅ |
32
- | Session browsing & mutations | ✅ | ✅ | ✅ |
33
- | Programmatic subagents | ✅ | ✅ | ✅ |
34
- | Bundled CLI binary | ✅ | ✅ | — (install `claude` separately) |
35
- | Observability (OTel / Langfuse) | via [Arize](https://github.com/Arize-ai/openinference) | — | ✅ (built-in) |
36
- | Custom transport (pluggable I/O) | — | — | ✅ |
37
- | Rails integration | — | — | ✅ |
5
+ [![Gem Version](https://img.shields.io/gem/v/claude-agent-sdk)](https://rubygems.org/gems/claude-agent-sdk)
6
+ [![CI](https://github.com/ya-luotao/claude-agent-sdk-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/ya-luotao/claude-agent-sdk-ruby/actions/workflows/ci.yml)
7
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-CC342D)](https://www.ruby-lang.org/)
8
+ [![Docs](https://img.shields.io/badge/docs-rubydoc.info-blue)](https://rubydoc.info/gems/claude-agent-sdk)
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
38
10
 
39
- **Where Ruby goes further:** Built-in OpenTelemetry observer with Langfuse flow diagram support — no third-party instrumentation library needed. Custom transport support lets you swap the subprocess for any I/O layer (e.g., connect to a remote Claude Code instance over SSH or a container). Rails integration provides a `configure` block for initializers with thread-safe observer factories, and plays well with ActionCable for real-time streaming. Full typed coverage for all 25 CLI message types and all 27 hook events.
11
+ A Ruby SDK for the [Claude Code](https://docs.claude.com/en/docs/claude-code-overview) agent runtime. Build AI agents, automate coding workflows, and integrate Claude into Rails and other Ruby applications with the same capabilities as the official [TypeScript](https://github.com/anthropics/claude-agent-sdk-typescript) and [Python](https://github.com/anthropics/claude-agent-sdk-python) SDKs.
40
12
 
41
- **What's missing:** The Ruby gem does not bundle the `claude` CLI binary (`npm install -g @anthropic-ai/claude-code`).
13
+ > **Unofficial and community-maintained.** This project is not affiliated with or supported by Anthropic. It tracks the official SDKs release by release; see the [CHANGELOG](CHANGELOG.md) for the currently synced version.
42
14
 
43
- <details>
44
- <summary><strong>Implementation differences from the official SDKs</strong></summary>
15
+ ## Highlights
45
16
 
46
- TypeScript uses native `async`/`await`. Python uses `async`/`await` with `anyio`. Ruby uses the [`async`](https://github.com/socketry/async) gem with fibers — no `await` keyword needed; blocking calls yield automatically inside an `Async` block:
47
-
48
- ```ruby
49
- Async do
50
- client = ClaudeAgentSDK::Client.new(options: options)
51
- client.connect
52
- client.query("Hello")
53
- client.receive_response { |msg| puts msg }
54
- client.disconnect
55
- end.wait
56
- ```
57
-
58
- Types use plain Ruby classes with `attr_accessor` and keyword args — no runtime type checking, but the same structure and field names as the TS Zod schemas / Python dataclasses. Subprocess transport uses `Open3.popen3`; wire protocol is identical.
59
-
60
- </details>
17
+ - **Same wire protocol as the official SDKs.** Spawns the `claude` CLI as a subprocess and speaks stream-JSON over stdin/stdout, so every feature of the runtime is available: sessions, subagents, sandboxing, structured output, file checkpointing and rewind.
18
+ - **`query()` for one-shot calls, `Client` for bidirectional sessions** with interrupts, mid-session model switching, and streaming input from any `Enumerator`.
19
+ - **In-process custom tools.** Define tools as Ruby blocks; they run inside your process with direct access to your app state (SDK MCP servers), with JSON-Schema-validated arguments.
20
+ - **All 27 hook events and permission callbacks** with typed inputs, so you can gate, audit, or rewrite every tool call.
21
+ - **Rails-ready.** Fiber-safe callback dispatch, an initializer-style `configure` block, ActionCable streaming, background-job session resumption, and a `callback_scheduling: :inline` mode for fiber workers.
22
+ - **Built-in OpenTelemetry observer** with Langfuse support; no third-party instrumentation library required.
23
+ - **Pluggable transport** to run the CLI somewhere else (an E2B microVM, a container, over SSH).
24
+ - **Hermetic deploys.** `CLIInstaller` vendors a checksum-verified, pinned CLI binary into your project so production never depends on a global `npm install`.
61
25
 
62
26
  ## Installation
63
27
 
64
- Add this line to your application's Gemfile:
65
-
66
28
  ```ruby
67
- # Recommended: use the latest from GitHub for newest features
68
- gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
69
-
70
- # Or use a stable version from RubyGems
71
- gem 'claude-agent-sdk', '~> 0.31.0'
29
+ # Gemfile
30
+ gem 'claude-agent-sdk', '~> 0.33.0'
72
31
  ```
73
32
 
74
- Then `bundle install`, or install directly: `gem install claude-agent-sdk`.
75
-
76
- **Prerequisites:**
77
- - Ruby 3.2+
78
- - Node.js
79
- - Claude Code 2.0.0+: `npm install -g @anthropic-ai/claude-code`
33
+ Then `bundle install`, or install directly with `gem install claude-agent-sdk`. To track unreleased changes, point the Gemfile at GitHub: `gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'`.
80
34
 
81
- ### Vendoring the CLI (hermetic deploys)
35
+ **Prerequisites**
82
36
 
83
- The SDK runs the `claude` CLI as a subprocess, so a deploy is only reproducible if the CLI version is pinned with it. `CLIInstaller` downloads a pinned binary from the official release endpoint into a project-local directory (`vendor/claude` by default) — checksum-verified, no npm/Node at runtime, and nothing extra shipped inside the gem.
37
+ - Ruby 3.2 or newer
38
+ - Claude Code CLI 2.0.0 or newer, either installed globally (`npm install -g @anthropic-ai/claude-code`) or vendored with `CLIInstaller`:
84
39
 
85
40
  ```ruby
86
- require 'claude_agent_sdk'
87
-
88
- # 'stable' (default), 'latest', or a concrete version — pin it in production.
89
- ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220')
90
- # => "/app/vendor/claude/claude"
91
-
92
- ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220', dir: '/opt/claude')
93
-
94
- # nil unless a binary is already installed there
95
- ClaudeAgentSDK::CLIInstaller.installed_path
41
+ # bin/setup or a cached Docker layer — pin a concrete version in production
42
+ ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220') # => "/app/vendor/claude/claude"
96
43
  ```
97
44
 
98
- `install` is idempotent and safe to run concurrently, so it fits `bin/setup`, a cached Docker layer, and every process of a multi-process boot:
99
-
100
- - The install directory's `VERSION` file records the installed version **and** the SHA-256 that was verified at download time. The shortcut re-hashes the vendored binary (~0.1s for the real 245MB binary) and only skips the download when both match — a truncated, swapped or half-written binary is reinstalled instead of trusted. It makes **no network request**, so repeat boots work offline — with a pinned concrete version; `'stable'`/`'latest'` must always re-resolve through the endpoint, which is one more reason to pin in production.
101
- - An exclusive `flock` on `<dir>/.install.lock` covers the whole check → download → place → record sequence, so parallel installs into one directory don't race; the loser simply observes the finished install.
102
-
103
- Failures (unsupported platform, invalid version, HTTP error, response-size cap, oversized download, checksum mismatch, filesystem errors) raise `ClaudeAgentSDK::CLIInstallError`.
104
-
105
- **A failed install never breaks a working one.** The new binary is downloaded to a temp file, checksum-verified and recorded, and only then renamed into place — the rename is the last step, and nothing can fail after it. So a failed upgrade leaves the previously installed binary intact and runnable (the SDK keeps working), and the next `install` redoes it cleanly. A first install that fails leaves nothing behind at all.
106
-
107
- > The vendored directory is trusted input: anything that can write to it can replace the binary the SDK executes. Keep it inside your deploy artifact, owned by the deploy user and not world-writable, exactly as you would treat `bin/`.
108
-
109
- ```dockerfile
110
- # Dockerfile — pin the CLI in its own cached layer
111
- RUN bundle exec ruby -e "require 'claude_agent_sdk'; \
112
- ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220')"
113
- ```
114
-
115
- ```ruby
116
- #!/usr/bin/env ruby
117
- # bin/setup
118
- require 'claude_agent_sdk'
119
- puts ClaudeAgentSDK::CLIInstaller.install(version: ENV.fetch('CLAUDE_CLI_VERSION', 'stable'))
120
- ```
121
-
122
- Supported platforms: `darwin-arm64`, `darwin-x64` (Rosetta 2 gets the arm64 build), `linux-x64`, `linux-arm64`, and the `-musl` variants. Windows is not supported.
123
-
124
- **CLI discovery order.** With no explicit `cli_path:` in `ClaudeAgentOptions`, the transport probes in this order:
125
-
126
- 1. `CLAUDE_CLI_PATH` — an explicit path to an executable, no discovery at all (a relative value is resolved against the process's working directory, not `cwd:`)
127
- 2. The vendored binary (`CLIInstaller.installed_path`) — deliberately ahead of `PATH`, so a pinned install beats whatever is installed globally
128
- 3. `which claude`
129
- 4. Common install locations (`~/.claude/local/claude`, `/usr/local/bin/claude`, …)
130
-
131
- ### Agentic Coding Skill
132
-
133
- If you're using [Claude Code](https://claude.ai/claude-code), this repo is a Claude Code plugin marketplace. Add it once, then install the skill:
134
-
135
- ```bash
136
- /plugin marketplace add ya-luotao/claude-agent-sdk-ruby
137
- /plugin install claude-agent-ruby@claude-agent-sdk-ruby
138
- ```
139
-
140
- This skill teaches your AI coding assistant about the SDK's APIs, patterns, and best practices.
45
+ The vendored binary is found ahead of `PATH`, installs are idempotent and concurrency-safe, and a failed upgrade never breaks a working install. See [docs/cli-installer.md](docs/cli-installer.md) for the full behaviour, supported platforms, and the CLI discovery order.
141
46
 
142
47
  ## Quick Start
143
48
 
@@ -145,46 +50,29 @@ This skill teaches your AI coding assistant about the SDK's APIs, patterns, and
145
50
  require 'claude_agent_sdk'
146
51
 
147
52
  ClaudeAgentSDK.query(prompt: "What is 2 + 2?") do |message|
148
- puts message
53
+ puts message.text if message.is_a?(ClaudeAgentSDK::AssistantMessage)
149
54
  end
150
55
  ```
151
56
 
152
- ## Basic Usage: `query()`
57
+ ### `query()` — one-shot and streaming
153
58
 
154
- `query()` is a function for querying Claude Code. It yields response messages to a block.
59
+ `query()` runs a single conversation and yields each response message to the block.
155
60
 
156
61
  ```ruby
157
- require 'claude_agent_sdk'
158
-
159
- # Simple query
160
- ClaudeAgentSDK.query(prompt: "Hello Claude") do |message|
161
- puts message.text if message.is_a?(ClaudeAgentSDK::AssistantMessage)
162
- end
163
-
164
- # With options
165
62
  options = ClaudeAgentSDK::ClaudeAgentOptions.new(
166
63
  system_prompt: "You are a helpful assistant",
167
- max_turns: 1
168
- )
169
-
170
- ClaudeAgentSDK.query(prompt: "Tell me a joke", options: options) do |message|
171
- puts message
172
- end
173
- ```
174
-
175
- **Using tools:**
176
-
177
- ```ruby
178
- options = ClaudeAgentSDK::ClaudeAgentOptions.new(
179
64
  allowed_tools: ['Read', 'Write', 'Bash'],
180
65
  permission_mode: 'acceptEdits',
181
- cwd: "/path/to/project"
66
+ cwd: "/path/to/project",
67
+ max_turns: 5
182
68
  )
183
69
 
184
- ClaudeAgentSDK.query(prompt: "Create a hello.rb file", options: options) { |message| }
70
+ ClaudeAgentSDK.query(prompt: "Create a hello.rb file", options: options) do |message|
71
+ puts message
72
+ end
185
73
  ```
186
74
 
187
- **Streaming input** — send multiple messages dynamically instead of a single prompt string:
75
+ Pass an `Enumerator` instead of a string to stream several user messages into one session:
188
76
 
189
77
  ```ruby
190
78
  stream = ClaudeAgentSDK::Streaming.from_array(['Hello!', 'What is 2+2?', 'Thanks!'])
@@ -194,11 +82,9 @@ ClaudeAgentSDK.query(prompt: stream) do |message|
194
82
  end
195
83
  ```
196
84
 
197
- See [examples/streaming_input_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/streaming_input_example.rb) and [examples/quick_start.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/quick_start.rb).
198
-
199
- ## `Client` — Bidirectional Sessions
85
+ ### `Client` — bidirectional sessions
200
86
 
201
- `Client` supports interactive conversations with hooks, permission callbacks, and custom tools. It uses streaming mode automatically.
87
+ `Client` keeps a session open so you can send follow-up queries, interrupt, switch models, and use hooks, permission callbacks, and custom tools. It runs inside an [`async`](https://github.com/socketry/async) block; blocking calls yield automatically, no `await` needed.
202
88
 
203
89
  ```ruby
204
90
  require 'claude_agent_sdk'
@@ -217,11 +103,11 @@ Async do
217
103
  end.wait
218
104
  ```
219
105
 
220
- Advanced features (`interrupt`, mid-session model/permission switching, MCP status, custom transports for E2B/SSH/etc.) → see [docs/client.md](docs/client.md).
106
+ See [docs/client.md](docs/client.md) for `interrupt`, mid-session model and permission switching, MCP status, and custom transports.
221
107
 
222
- ## Custom Tools (SDK MCP Servers)
108
+ ### Custom tools (SDK MCP servers)
223
109
 
224
- Define tools as Ruby procs/lambdas that run in-process — no subprocess, no IPC, direct access to your app state.
110
+ Tools are Ruby blocks that run in-process, with no subprocess or IPC between Claude's tool call and your code.
225
111
 
226
112
  ```ruby
227
113
  greet = ClaudeAgentSDK.create_tool('greet', 'Greet a user', { name: :string }) do |args|
@@ -236,13 +122,11 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
236
122
  )
237
123
  ```
238
124
 
239
- Tool arguments are JSON-Schema-validated (draft4, via the official mcp gem) before your handler runs: the simple `{ name: :string }` idiom marks every parameter required, so a missing argument returns an in-band error to the model instead of invoking the handler with `nil`. Handler exceptions and unknown tools are also reported in-band (`isError: true`) so the model can read the text and self-correct. Opt out globally with `MCP.configure { |c| c.validate_tool_call_arguments = false }`. Schemas the draft4 metaschema rejects (e.g. numeric `exclusiveMinimum`, `$ref`) fall back to validation-disabled with a warning.
125
+ Arguments are validated against the tool's JSON Schema before your handler runs, and handler exceptions are reported back to the model in-band so it can self-correct. See [docs/mcp-servers.md](docs/mcp-servers.md) for resources, prompts, mixed SDK + external servers, and schema details.
240
126
 
241
- Resources, prompts, mixed (SDK + external) servers, RubyLLM schema compatibility → see [docs/mcp-servers.md](docs/mcp-servers.md).
127
+ ### Hooks and permission callbacks
242
128
 
243
- ## Hooks & Permission Callbacks
244
-
245
- **Hooks** let the Claude Code application invoke your Ruby code at all 27 lifecycle points (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `PreCompact`, etc.) with typed input objects. **Permission callbacks** give you programmatic control over tool execution.
129
+ Hooks run your Ruby code at any of the 27 lifecycle events (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `PreCompact`, …) with typed inputs. Permission callbacks decide programmatically whether a tool call may proceed.
246
130
 
247
131
  ```ruby
248
132
  options = ClaudeAgentSDK::ClaudeAgentOptions.new(
@@ -251,88 +135,89 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
251
135
  )
252
136
  ```
253
137
 
254
- → Full event list, typed inputs, and worked examples in [docs/hooks-and-permissions.md](docs/hooks-and-permissions.md).
255
-
256
- ## Advanced Topics
257
-
258
- | Topic | Reference |
259
- |-------|-----------|
260
- | Structured output, thinking config, budget, fallback model, advisor model, beta features, sandbox, bare mode, file checkpointing | [docs/configuration.md](docs/configuration.md) |
261
- | Session listing, reading, renaming, tagging, deleting, forking, resume-at-message | [docs/sessions.md](docs/sessions.md) |
262
- | OpenTelemetry tracing, Langfuse setup, custom observers | [docs/observability.md](docs/observability.md) |
263
- | Rails integration (fiber safety, solid_queue fiber workers / `callback_scheduling: :inline`, ActionCable, sessions, jobs, HTTP MCP, observability initializer) | [docs/rails.md](docs/rails.md) |
138
+ See [docs/hooks-and-permissions.md](docs/hooks-and-permissions.md) for the full event list and worked examples.
139
+
140
+ ## Documentation
141
+
142
+ | Topic | Guide |
143
+ |-------|-------|
144
+ | `Client` advanced features and custom transports | [docs/client.md](docs/client.md) |
145
+ | SDK MCP servers: tools, resources, prompts, schema compatibility | [docs/mcp-servers.md](docs/mcp-servers.md) |
146
+ | All hook events, typed inputs, permission callbacks | [docs/hooks-and-permissions.md](docs/hooks-and-permissions.md) |
147
+ | Structured output, thinking, budget, fallback and advisor models, sandbox, bare mode, checkpointing | [docs/configuration.md](docs/configuration.md) |
148
+ | Session listing, reading, renaming, tagging, forking, resume-at-message | [docs/sessions.md](docs/sessions.md) |
149
+ | Subagent capabilities, event contracts, and minimal example | [docs/subagents.md](docs/subagents.md) |
150
+ | OpenTelemetry tracing, Langfuse, custom observers | [docs/observability.md](docs/observability.md) |
151
+ | Rails: fiber safety, solid_queue fiber workers, ActionCable, jobs, initializer | [docs/rails.md](docs/rails.md) |
152
+ | Vendoring a pinned CLI binary and CLI discovery order | [docs/cli-installer.md](docs/cli-installer.md) |
264
153
  | Message, content block, and configuration type reference | [docs/types.md](docs/types.md) |
265
- | Error handling, exception hierarchy, timeout configuration | [docs/errors.md](docs/errors.md) |
154
+ | Error handling, exception hierarchy, timeouts | [docs/errors.md](docs/errors.md) |
155
+
156
+ API reference: [rubydoc.info/gems/claude-agent-sdk](https://rubydoc.info/gems/claude-agent-sdk). Available built-in tools: [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-code/settings#tools-available-to-claude).
266
157
 
267
158
  ## Examples
268
159
 
269
- ### Core
160
+ Runnable scripts live in [`examples/`](https://github.com/ya-luotao/claude-agent-sdk-ruby/tree/main/examples).
270
161
 
271
- | Example | Description |
272
- |---------|-------------|
273
- | [quick_start.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/quick_start.rb) | Basic `query()` usage with options |
274
- | [client_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/client_example.rb) | Interactive Client usage |
275
- | [message_types_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/message_types_example.rb) | Handling all 25 SDK message types |
276
- | [streaming_input_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/streaming_input_example.rb) | Streaming input for multi-turn conversations |
277
- | [session_resumption_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/session_resumption_example.rb) | Multi-turn conversations with session persistence |
278
- | [structured_output_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/structured_output_example.rb) | JSON schema structured output |
279
- | [error_handling_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/error_handling_example.rb) | Error handling with `AssistantMessage.error` |
280
- | [bare_mode_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/bare_mode_example.rb) | Minimal startup with `bare: true` |
281
- | [sandbox_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/sandbox_example.rb) | Full sandbox settings (network, filesystem, violations) |
162
+ | Area | Examples |
163
+ |------|----------|
164
+ | Getting started | [quick_start](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/quick_start.rb) · [client](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/client_example.rb) · [streaming_input](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/streaming_input_example.rb) · [message_types](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/message_types_example.rb) · [error_handling](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/error_handling_example.rb) |
165
+ | Sessions and output | [session_resumption](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/session_resumption_example.rb) · [structured_output](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/structured_output_example.rb) · [extended_thinking](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/extended_thinking_example.rb) · [session_stores/](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/session_stores/README.md) |
166
+ | Tools and MCP | [mcp_calculator](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/mcp_calculator.rb) · [mcp_resources_prompts](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/mcp_resources_prompts_example.rb) · [http_mcp_server](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/http_mcp_server_example.rb) |
167
+ | Hooks and permissions | [hooks](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/hooks_example.rb) · [advanced_hooks](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/advanced_hooks_example.rb) · [lifecycle_hooks](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/lifecycle_hooks_example.rb) · [permission_callback](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/permission_callback_example.rb) |
168
+ | Models and limits | [budget_control](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/budget_control_example.rb) · [fallback_model](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/fallback_model_example.rb) · [advisor](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/advisor_example.rb) · [bare_mode](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/bare_mode_example.rb) · [sandbox](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/sandbox_example.rb) |
169
+ | Rails, observability, transports | [rails_actioncable](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/rails_actioncable_example.rb) · [rails_background_job](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/rails_background_job_example.rb) · [otel_langfuse](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/otel_langfuse_example.rb) · [e2b_transport](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/e2b_transport_example.rb) |
282
170
 
283
- ### MCP Servers
171
+ ## Comparison with the official SDKs
284
172
 
285
- | Example | Description |
286
- |---------|-------------|
287
- | [mcp_calculator.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/mcp_calculator.rb) | Custom tools with SDK MCP servers |
288
- | [mcp_resources_prompts_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/mcp_resources_prompts_example.rb) | MCP resources and prompts |
289
- | [http_mcp_server_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/http_mcp_server_example.rb) | HTTP/SSE MCP server configuration |
173
+ All three SDKs drive the same CLI over the same protocol, so capabilities line up feature for feature. Ruby differs mainly in idiom: `Enumerator` for streaming input, blocks for tools, and the `async` gem with fibers instead of `async`/`await`.
290
174
 
291
- ### Hooks & Permissions
175
+ | Capability | TypeScript | Python | Ruby (this gem) |
176
+ |---|:---:|:---:|:---:|
177
+ | One-shot `query()` | ✅ | ✅ | ✅ |
178
+ | Bidirectional `Client` | ✅ | ✅ | ✅ |
179
+ | Streaming input | `AsyncIterable` | `AsyncIterable` | `Enumerator` |
180
+ | Custom tools (SDK MCP servers) | `tool()` | `@tool` decorator | `create_tool` block |
181
+ | Hooks (all 27 events) | ✅ | ✅ | ✅ |
182
+ | Permission callbacks | ✅ | ✅ | ✅ |
183
+ | Structured output | ✅ | ✅ | ✅ |
184
+ | All 28 message types | ✅ | partial | ✅ |
185
+ | [Sandbox](https://github.com/anthropic-experimental/sandbox-runtime) settings | ✅ | partial | ✅ |
186
+ | Bare mode (`--bare`) | ✅ | ✅ | ✅ |
187
+ | File checkpointing & rewind | ✅ | ✅ | ✅ |
188
+ | Session browsing & mutations | ✅ | ✅ | ✅ |
189
+ | Programmatic subagents | ✅ | ✅ | ✅ |
190
+ | CLI binary | bundled | bundled | vendored on demand (`CLIInstaller`) |
191
+ | Observability (OTel / Langfuse) | via [Arize](https://github.com/Arize-ai/openinference) | — | ✅ built-in |
192
+ | Custom transport (pluggable I/O) | — | — | ✅ |
193
+ | Rails integration | — | — | ✅ |
292
194
 
293
- | Example | Description |
294
- |---------|-------------|
295
- | [hooks_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/hooks_example.rb) | Using hooks to control tool execution |
296
- | [advanced_hooks_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/advanced_hooks_example.rb) | Typed hook inputs/outputs |
297
- | [lifecycle_hooks_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/lifecycle_hooks_example.rb) | All 27 hook events |
298
- | [permission_callback_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/permission_callback_example.rb) | Dynamic tool permission control |
195
+ Types are plain Ruby classes with `attr_accessor` and keyword arguments, mirroring the field names of the TypeScript Zod schemas and Python dataclasses; there is no runtime type checking.
299
196
 
300
- ### Advanced
197
+ ## Claude Code plugin
301
198
 
302
- | Example | Description |
303
- |---------|-------------|
304
- | [budget_control_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/budget_control_example.rb) | Budget control with `max_budget_usd` |
305
- | [fallback_model_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/fallback_model_example.rb) | Fallback model configuration |
306
- | [advisor_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/advisor_example.rb) | Server-side advisor tool (`advisor_model`) |
307
- | [extended_thinking_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/extended_thinking_example.rb) | Extended thinking |
308
- | [e2b_transport_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/e2b_transport_example.rb) | Custom transport running CLI in an E2B microVM |
199
+ This repository is also a Claude Code plugin marketplace. The bundled skill teaches Claude Code the gem's APIs and patterns:
309
200
 
310
- ### Observability & Rails
201
+ ```bash
202
+ /plugin marketplace add ya-luotao/claude-agent-sdk-ruby
203
+ /plugin install claude-agent-ruby@claude-agent-sdk-ruby
204
+ ```
311
205
 
312
- | Example | Description |
313
- |---------|-------------|
314
- | [otel_langfuse_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/otel_langfuse_example.rb) | OpenTelemetry tracing with Langfuse backend |
315
- | [rails_actioncable_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/rails_actioncable_example.rb) | ActionCable streaming to frontend |
316
- | [rails_background_job_example.rb](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/examples/rails_background_job_example.rb) | Background jobs with session resumption |
206
+ ## Development
317
207
 
318
- ## Available Tools
208
+ ```bash
209
+ bundle install
210
+ bundle exec rspec # unit suite
211
+ bundle exec rubocop # lint
212
+ RUN_INTEGRATION=1 bundle exec rspec # also run the real-CLI integration suite (needs `claude` and ANTHROPIC_API_KEY)
213
+ ```
319
214
 
320
- See the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-code/settings#tools-available-to-claude) for a complete list of available tools.
215
+ CI runs the suite and RuboCop on Ruby 3.2, 3.3, and 3.4. See [spec/README.md](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/spec/README.md) for the test layout.
321
216
 
322
- ## Development
217
+ ## Contributing
323
218
 
324
- After checking out the repo, run `bundle install` to install dependencies. Then `bundle exec rspec` to run the tests.
219
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/ya-luotao/claude-agent-sdk-ruby/issues). Please include a failing spec with bug reports where possible, and keep pull requests focused on one change. Releases follow [Semantic Versioning](https://semver.org/) and are recorded in the [CHANGELOG](CHANGELOG.md).
325
220
 
326
221
  ## License
327
222
 
328
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
329
-
330
- ## Star History
331
-
332
- <a href="https://www.star-history.com/?repos=ya-luotao%2Fclaude-agent-sdk-ruby&type=date&legend=top-left">
333
- <picture>
334
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=ya-luotao/claude-agent-sdk-ruby&type=date&theme=dark&legend=top-left" />
335
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=ya-luotao/claude-agent-sdk-ruby&type=date&legend=top-left" />
336
- <img alt="Star History Chart" src="https://api.star-history.com/chart?repos=ya-luotao/claude-agent-sdk-ruby&type=date&legend=top-left" />
337
- </picture>
338
- </a>
223
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,57 @@
1
+ # Vendoring the CLI (`CLIInstaller`)
2
+
3
+ The SDK runs the `claude` CLI as a subprocess, so a deploy is only reproducible if the CLI version is pinned with it. `ClaudeAgentSDK::CLIInstaller` downloads a pinned binary from the official release endpoint into a project-local directory (`vendor/claude` by default) — checksum-verified, no npm/Node at runtime, and nothing extra shipped inside the gem.
4
+
5
+ ## Usage
6
+
7
+ ```ruby
8
+ require 'claude_agent_sdk'
9
+
10
+ # 'stable' (default), 'latest', or a concrete version — pin it in production.
11
+ ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220')
12
+ # => "/app/vendor/claude/claude"
13
+
14
+ ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220', dir: '/opt/claude')
15
+
16
+ # nil unless a binary is already installed there
17
+ ClaudeAgentSDK::CLIInstaller.installed_path
18
+ ```
19
+
20
+ `install` is idempotent and safe to run concurrently, so it fits `bin/setup`, a cached Docker layer, and every process of a multi-process boot:
21
+
22
+ - The install directory's `VERSION` file records the installed version **and** the SHA-256 that was verified at download time. The shortcut re-hashes the vendored binary (~0.1s for the real 245MB binary) and only skips the download when both match — a truncated, swapped or half-written binary is reinstalled instead of trusted. It makes **no network request**, so repeat boots work offline — with a pinned concrete version; `'stable'`/`'latest'` must always re-resolve through the endpoint, which is one more reason to pin in production.
23
+ - An exclusive `flock` on `<dir>/.install.lock` covers the whole check → download → place → record sequence, so parallel installs into one directory don't race; the loser simply observes the finished install.
24
+
25
+ Failures (unsupported platform, invalid version, HTTP error, response-size cap, oversized download, checksum mismatch, filesystem errors) raise `ClaudeAgentSDK::CLIInstallError`.
26
+
27
+ **A failed install never breaks a working one.** The new binary is downloaded to a temp file, checksum-verified and recorded, and only then renamed into place — the rename is the last step, and nothing can fail after it. So a failed upgrade leaves the previously installed binary intact and runnable (the SDK keeps working), and the next `install` redoes it cleanly. A first install that fails leaves nothing behind at all.
28
+
29
+ > The vendored directory is trusted input: anything that can write to it can replace the binary the SDK executes. Keep it inside your deploy artifact, owned by the deploy user and not world-writable, exactly as you would treat `bin/`.
30
+
31
+ ## Docker and `bin/setup`
32
+
33
+ ```dockerfile
34
+ # Dockerfile — pin the CLI in its own cached layer
35
+ RUN bundle exec ruby -e "require 'claude_agent_sdk'; \
36
+ ClaudeAgentSDK::CLIInstaller.install(version: '2.1.220')"
37
+ ```
38
+
39
+ ```ruby
40
+ #!/usr/bin/env ruby
41
+ # bin/setup
42
+ require 'claude_agent_sdk'
43
+ puts ClaudeAgentSDK::CLIInstaller.install(version: ENV.fetch('CLAUDE_CLI_VERSION', 'stable'))
44
+ ```
45
+
46
+ ## Supported platforms
47
+
48
+ `darwin-arm64`, `darwin-x64` (Rosetta 2 gets the arm64 build), `linux-x64`, `linux-arm64`, and the `-musl` variants. Windows is not supported.
49
+
50
+ ## CLI discovery order
51
+
52
+ With no explicit `cli_path:` in `ClaudeAgentOptions`, the transport probes in this order:
53
+
54
+ 1. `CLAUDE_CLI_PATH` — an explicit path to an executable, no discovery at all (a relative value is resolved against the process's working directory, not `cwd:`)
55
+ 2. The vendored binary (`CLIInstaller.installed_path`) — deliberately ahead of `PATH`, so a pinned install beats whatever is installed globally
56
+ 3. `which claude`
57
+ 4. Common install locations (`~/.claude/local/claude`, `/usr/local/bin/claude`, …)
data/docs/client.md CHANGED
@@ -44,6 +44,10 @@ Async do
44
44
  client.reconnect_mcp_server('my-server') # Reconnect a failed MCP server
45
45
  client.toggle_mcp_server('my-server', false) # Enable/disable an MCP server
46
46
  client.stop_task('task_abc123') # Stop a running background task
47
+ client.background_tasks # Background every foreground task (Ctrl+B) => {}
48
+ client.background_tasks(tool_use_id: 'toolu_01') # Only the task spawned by that tool_use block
49
+ # => { backgrounded: true } | { backgrounded: false } (definitive miss)
50
+ # '' or a non-String raises ArgumentError; nil is the all-tasks form
47
51
 
48
52
  client.disconnect
49
53
  end.wait