claude_agent 0.1.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 +7 -0
- data/.claude/commands/spec/complete.md +105 -0
- data/.claude/commands/spec/update.md +95 -0
- data/.claude/rules/conventions.md +622 -0
- data/.claude/rules/git.md +86 -0
- data/.claude/rules/pull-requests.md +31 -0
- data/.claude/rules/releases.md +177 -0
- data/.claude/rules/testing.md +267 -0
- data/.claude/settings.json +49 -0
- data/CHANGELOG.md +13 -0
- data/CLAUDE.md +94 -0
- data/LICENSE.txt +21 -0
- data/README.md +679 -0
- data/Rakefile +63 -0
- data/SPEC.md +558 -0
- data/lib/claude_agent/abort_controller.rb +113 -0
- data/lib/claude_agent/client.rb +298 -0
- data/lib/claude_agent/content_blocks.rb +163 -0
- data/lib/claude_agent/control_protocol.rb +717 -0
- data/lib/claude_agent/errors.rb +103 -0
- data/lib/claude_agent/hooks.rb +228 -0
- data/lib/claude_agent/mcp/server.rb +166 -0
- data/lib/claude_agent/mcp/tool.rb +137 -0
- data/lib/claude_agent/message_parser.rb +262 -0
- data/lib/claude_agent/messages.rb +421 -0
- data/lib/claude_agent/options.rb +264 -0
- data/lib/claude_agent/permissions.rb +164 -0
- data/lib/claude_agent/query.rb +90 -0
- data/lib/claude_agent/sandbox_settings.rb +139 -0
- data/lib/claude_agent/spawn.rb +235 -0
- data/lib/claude_agent/transport/base.rb +61 -0
- data/lib/claude_agent/transport/subprocess.rb +432 -0
- data/lib/claude_agent/types.rb +193 -0
- data/lib/claude_agent/version.rb +5 -0
- data/lib/claude_agent.rb +28 -0
- data/sig/claude_agent.rbs +912 -0
- data/sig/manifest.yaml +5 -0
- metadata +97 -0
data/README.md
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
# ClaudeAgent
|
|
2
|
+
|
|
3
|
+
A Ruby SDK for building AI-powered applications with [Claude Code](https://docs.anthropic.com/en/docs/claude-code). This library wraps the Claude Code CLI, providing both simple one-shot queries and interactive bidirectional sessions.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Ruby 3.2+ (uses `Data.define`)
|
|
8
|
+
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code/getting-started) v2.0.0+
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Add to your Gemfile:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
gem "claude_agent"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install directly:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
gem install claude_agent
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### One-Shot Query
|
|
33
|
+
|
|
34
|
+
The simplest way to use Claude:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
require "claude_agent"
|
|
38
|
+
|
|
39
|
+
ClaudeAgent.query(prompt: "What is the capital of France?").each do |message|
|
|
40
|
+
case message
|
|
41
|
+
when ClaudeAgent::AssistantMessage
|
|
42
|
+
puts message.text
|
|
43
|
+
when ClaudeAgent::ResultMessage
|
|
44
|
+
puts "Cost: $#{message.total_cost_usd}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Interactive Client
|
|
50
|
+
|
|
51
|
+
For multi-turn conversations:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
require "claude_agent"
|
|
55
|
+
|
|
56
|
+
ClaudeAgent::Client.open do |client|
|
|
57
|
+
client.query("Remember the number 42")
|
|
58
|
+
client.receive_response.each { |msg| } # Process first response
|
|
59
|
+
|
|
60
|
+
client.query("What number did I ask you to remember?")
|
|
61
|
+
client.receive_response.each do |msg|
|
|
62
|
+
puts msg.text if msg.is_a?(ClaudeAgent::AssistantMessage)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
Use `ClaudeAgent::Options` to customize behavior:
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
options = ClaudeAgent::Options.new(
|
|
73
|
+
# Model selection
|
|
74
|
+
model: "claude-sonnet-4-5-20250514",
|
|
75
|
+
fallback_model: "claude-haiku-3-5-20241022",
|
|
76
|
+
|
|
77
|
+
# Conversation limits
|
|
78
|
+
max_turns: 10,
|
|
79
|
+
max_budget_usd: 1.0,
|
|
80
|
+
max_thinking_tokens: 10000,
|
|
81
|
+
|
|
82
|
+
# System prompt
|
|
83
|
+
system_prompt: "You are a helpful coding assistant.",
|
|
84
|
+
append_system_prompt: "Always be concise.",
|
|
85
|
+
|
|
86
|
+
# Tool configuration
|
|
87
|
+
tools: ["Read", "Write", "Bash"],
|
|
88
|
+
allowed_tools: ["Read"],
|
|
89
|
+
disallowed_tools: ["Write"],
|
|
90
|
+
|
|
91
|
+
# Permission modes: "default", "acceptEdits", "plan", "delegate", "dontAsk", "bypassPermissions"
|
|
92
|
+
permission_mode: "acceptEdits",
|
|
93
|
+
|
|
94
|
+
# Working directory for file operations
|
|
95
|
+
cwd: "/path/to/project",
|
|
96
|
+
add_dirs: ["/additional/path"],
|
|
97
|
+
|
|
98
|
+
# Session management
|
|
99
|
+
resume: "session-id",
|
|
100
|
+
continue_conversation: true,
|
|
101
|
+
fork_session: true,
|
|
102
|
+
persist_session: true, # Default: true
|
|
103
|
+
|
|
104
|
+
# Structured output
|
|
105
|
+
output_format: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: { answer: { type: "string" } }
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
ClaudeAgent.query(prompt: "Help me refactor this code", options: options)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Tools Preset
|
|
115
|
+
|
|
116
|
+
Use a preset tool configuration:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
# Using ToolsPreset class
|
|
120
|
+
options = ClaudeAgent::Options.new(
|
|
121
|
+
tools: ClaudeAgent::ToolsPreset.new(preset: "claude_code")
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Or as a Hash
|
|
125
|
+
options = ClaudeAgent::Options.new(
|
|
126
|
+
tools: { type: "preset", preset: "claude_code" }
|
|
127
|
+
)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Sandbox Settings
|
|
131
|
+
|
|
132
|
+
Configure sandboxed command execution:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
sandbox = ClaudeAgent::SandboxSettings.new(
|
|
136
|
+
enabled: true,
|
|
137
|
+
auto_allow_bash_if_sandboxed: true,
|
|
138
|
+
excluded_commands: ["docker", "kubectl"],
|
|
139
|
+
network: ClaudeAgent::SandboxNetworkConfig.new(
|
|
140
|
+
allowed_domains: ["api.example.com"],
|
|
141
|
+
allow_local_binding: true
|
|
142
|
+
),
|
|
143
|
+
ripgrep: ClaudeAgent::SandboxRipgrepConfig.new(
|
|
144
|
+
command: "/usr/local/bin/rg"
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
options = ClaudeAgent::Options.new(sandbox: sandbox)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Custom Agents
|
|
152
|
+
|
|
153
|
+
Define custom subagents:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
agents = {
|
|
157
|
+
"test-runner" => ClaudeAgent::AgentDefinition.new(
|
|
158
|
+
description: "Runs tests and reports results",
|
|
159
|
+
prompt: "You are a test runner. Execute tests and report failures clearly.",
|
|
160
|
+
tools: ["Read", "Bash"],
|
|
161
|
+
model: "haiku"
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
options = ClaudeAgent::Options.new(agents: agents)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Message Types
|
|
169
|
+
|
|
170
|
+
The SDK provides strongly-typed message classes:
|
|
171
|
+
|
|
172
|
+
### AssistantMessage
|
|
173
|
+
|
|
174
|
+
Claude's responses:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
message.text # Combined text content
|
|
178
|
+
message.thinking # Extended thinking content (if enabled)
|
|
179
|
+
message.model # Model that generated the response
|
|
180
|
+
message.uuid # Message UUID
|
|
181
|
+
message.session_id # Session identifier
|
|
182
|
+
message.tool_uses # Array of ToolUseBlock if Claude wants to use tools
|
|
183
|
+
message.has_tool_use? # Check if tools are being used
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### ResultMessage
|
|
187
|
+
|
|
188
|
+
Final message with usage statistics:
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
result.session_id # Session identifier
|
|
192
|
+
result.num_turns # Number of conversation turns
|
|
193
|
+
result.duration_ms # Total duration in milliseconds
|
|
194
|
+
result.total_cost_usd # API cost in USD
|
|
195
|
+
result.usage # Token usage breakdown
|
|
196
|
+
result.model_usage # Per-model usage breakdown
|
|
197
|
+
result.is_error # Whether the session ended in error
|
|
198
|
+
result.success? # Convenience method
|
|
199
|
+
result.error? # Convenience method
|
|
200
|
+
result.errors # Array of error messages (if any)
|
|
201
|
+
result.permission_denials # Array of SDKPermissionDenial (if any)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### SystemMessage
|
|
205
|
+
|
|
206
|
+
Internal system events:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
system_msg.subtype # e.g., "init"
|
|
210
|
+
system_msg.data # Event-specific data
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### StreamEvent
|
|
214
|
+
|
|
215
|
+
Real-time streaming events:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
event.uuid # Event UUID
|
|
219
|
+
event.session_id # Session identifier
|
|
220
|
+
event.event_type # Type of stream event
|
|
221
|
+
event.event # Raw event data
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### CompactBoundaryMessage
|
|
225
|
+
|
|
226
|
+
Conversation compaction marker:
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
boundary.uuid # Message UUID
|
|
230
|
+
boundary.session_id # Session identifier
|
|
231
|
+
boundary.trigger # "manual" or "auto"
|
|
232
|
+
boundary.pre_tokens # Token count before compaction
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### StatusMessage
|
|
236
|
+
|
|
237
|
+
Session status updates:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
status.uuid # Message UUID
|
|
241
|
+
status.session_id # Session identifier
|
|
242
|
+
status.status # e.g., "compacting"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### ToolProgressMessage
|
|
246
|
+
|
|
247
|
+
Long-running tool progress:
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
progress.tool_use_id # Tool use ID
|
|
251
|
+
progress.tool_name # Tool name
|
|
252
|
+
progress.elapsed_time_seconds # Time elapsed
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### HookResponseMessage
|
|
256
|
+
|
|
257
|
+
Hook execution output:
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
hook_response.hook_name # Hook name
|
|
261
|
+
hook_response.hook_event # Hook event type
|
|
262
|
+
hook_response.stdout # Hook stdout
|
|
263
|
+
hook_response.stderr # Hook stderr
|
|
264
|
+
hook_response.exit_code # Exit code
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### AuthStatusMessage
|
|
268
|
+
|
|
269
|
+
Authentication status during login:
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
auth.is_authenticating # Whether auth is in progress
|
|
273
|
+
auth.output # Auth output messages
|
|
274
|
+
auth.error # Error message (if any)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Content Blocks
|
|
278
|
+
|
|
279
|
+
Assistant messages contain content blocks:
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
message.content.each do |block|
|
|
283
|
+
case block
|
|
284
|
+
when ClaudeAgent::TextBlock
|
|
285
|
+
puts block.text
|
|
286
|
+
when ClaudeAgent::ThinkingBlock
|
|
287
|
+
puts "Thinking: #{block.thinking}"
|
|
288
|
+
when ClaudeAgent::ToolUseBlock
|
|
289
|
+
puts "Tool: #{block.name}, ID: #{block.id}, Input: #{block.input}"
|
|
290
|
+
when ClaudeAgent::ToolResultBlock
|
|
291
|
+
puts "Result for #{block.tool_use_id}: #{block.content}"
|
|
292
|
+
when ClaudeAgent::ServerToolUseBlock
|
|
293
|
+
puts "MCP Tool: #{block.name} from #{block.server_name}"
|
|
294
|
+
when ClaudeAgent::ServerToolResultBlock
|
|
295
|
+
puts "MCP Result from #{block.server_name}"
|
|
296
|
+
when ClaudeAgent::ImageContentBlock
|
|
297
|
+
puts "Image: #{block.media_type}, source: #{block.source_type}"
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## MCP Tools
|
|
303
|
+
|
|
304
|
+
Create in-process MCP tools that Claude can use:
|
|
305
|
+
|
|
306
|
+
```ruby
|
|
307
|
+
# Define a tool
|
|
308
|
+
calculator = ClaudeAgent::MCP::Tool.new(
|
|
309
|
+
name: "add",
|
|
310
|
+
description: "Add two numbers together",
|
|
311
|
+
schema: { a: Float, b: Float }
|
|
312
|
+
) do |args|
|
|
313
|
+
args["a"] + args["b"]
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Create a server with tools
|
|
317
|
+
server = ClaudeAgent::MCP::Server.new(
|
|
318
|
+
name: "calculator",
|
|
319
|
+
tools: [calculator]
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Use with options (SDK MCP servers)
|
|
323
|
+
options = ClaudeAgent::Options.new(
|
|
324
|
+
mcp_servers: {
|
|
325
|
+
"calculator" => { type: "sdk", instance: server }
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
ClaudeAgent.query(
|
|
330
|
+
prompt: "What is 25 + 17?",
|
|
331
|
+
options: options
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### External MCP Servers
|
|
336
|
+
|
|
337
|
+
Configure external MCP servers:
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
options = ClaudeAgent::Options.new(
|
|
341
|
+
mcp_servers: {
|
|
342
|
+
"filesystem" => {
|
|
343
|
+
type: "stdio",
|
|
344
|
+
command: "npx",
|
|
345
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Tool Schema
|
|
352
|
+
|
|
353
|
+
Define schemas using Ruby types or JSON Schema:
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
# Ruby types (converted automatically)
|
|
357
|
+
schema: {
|
|
358
|
+
name: String,
|
|
359
|
+
age: Integer,
|
|
360
|
+
score: Float,
|
|
361
|
+
active: TrueClass, # boolean
|
|
362
|
+
tags: Array,
|
|
363
|
+
metadata: Hash
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
# Or use JSON Schema directly
|
|
367
|
+
schema: {
|
|
368
|
+
type: "object",
|
|
369
|
+
properties: {
|
|
370
|
+
name: { type: "string", description: "User's name" },
|
|
371
|
+
age: { type: "integer", minimum: 0 }
|
|
372
|
+
},
|
|
373
|
+
required: ["name"]
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Tool Return Values
|
|
378
|
+
|
|
379
|
+
Tools can return various formats:
|
|
380
|
+
|
|
381
|
+
```ruby
|
|
382
|
+
# Simple string
|
|
383
|
+
ClaudeAgent::MCP::Tool.new(name: "greet", description: "Greet") do |args|
|
|
384
|
+
"Hello, #{args['name']}!"
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Number (converted to string)
|
|
388
|
+
ClaudeAgent::MCP::Tool.new(name: "add", description: "Add") do |args|
|
|
389
|
+
args["a"] + args["b"]
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Custom MCP content
|
|
393
|
+
ClaudeAgent::MCP::Tool.new(name: "fancy", description: "Fancy") do |args|
|
|
394
|
+
{ content: [{ type: "text", text: "Custom response" }] }
|
|
395
|
+
end
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Hooks
|
|
399
|
+
|
|
400
|
+
Intercept tool usage and other events with hooks:
|
|
401
|
+
|
|
402
|
+
```ruby
|
|
403
|
+
options = ClaudeAgent::Options.new(
|
|
404
|
+
hooks: {
|
|
405
|
+
"PreToolUse" => [
|
|
406
|
+
ClaudeAgent::HookMatcher.new(
|
|
407
|
+
matcher: "Bash|Write", # Match specific tools
|
|
408
|
+
callbacks: [
|
|
409
|
+
->(input, context) {
|
|
410
|
+
puts "Tool: #{input.tool_name}"
|
|
411
|
+
puts "Input: #{input.tool_input}"
|
|
412
|
+
puts "Tool Use ID: #{input.tool_use_id}"
|
|
413
|
+
{ continue_: true } # Allow the tool to proceed
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
)
|
|
417
|
+
],
|
|
418
|
+
"PostToolUse" => [
|
|
419
|
+
ClaudeAgent::HookMatcher.new(
|
|
420
|
+
matcher: /^mcp__/, # Regex matching
|
|
421
|
+
callbacks: [
|
|
422
|
+
->(input, context) {
|
|
423
|
+
puts "Result: #{input.tool_response}"
|
|
424
|
+
{ continue_: true }
|
|
425
|
+
}
|
|
426
|
+
]
|
|
427
|
+
)
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Hook Events
|
|
434
|
+
|
|
435
|
+
All available hook events:
|
|
436
|
+
|
|
437
|
+
- `PreToolUse` - Before tool execution
|
|
438
|
+
- `PostToolUse` - After successful tool execution
|
|
439
|
+
- `PostToolUseFailure` - After tool execution failure
|
|
440
|
+
- `Notification` - System notifications
|
|
441
|
+
- `UserPromptSubmit` - When user submits a prompt
|
|
442
|
+
- `SessionStart` - When session starts
|
|
443
|
+
- `SessionEnd` - When session ends
|
|
444
|
+
- `Stop` - When agent stops
|
|
445
|
+
- `SubagentStart` - When subagent starts
|
|
446
|
+
- `SubagentStop` - When subagent stops
|
|
447
|
+
- `PreCompact` - Before conversation compaction
|
|
448
|
+
- `PermissionRequest` - When permission is requested
|
|
449
|
+
|
|
450
|
+
### Hook Input Types
|
|
451
|
+
|
|
452
|
+
| Event | Input Type | Key Fields |
|
|
453
|
+
|--------------------|---------------------------|---------------------------------------------------------|
|
|
454
|
+
| PreToolUse | `PreToolUseInput` | tool_name, tool_input, tool_use_id |
|
|
455
|
+
| PostToolUse | `PostToolUseInput` | tool_name, tool_input, tool_response, tool_use_id |
|
|
456
|
+
| PostToolUseFailure | `PostToolUseFailureInput` | tool_name, tool_input, error, tool_use_id, is_interrupt |
|
|
457
|
+
| Notification | `NotificationInput` | message, title, notification_type |
|
|
458
|
+
| UserPromptSubmit | `UserPromptSubmitInput` | prompt |
|
|
459
|
+
| SessionStart | `SessionStartInput` | source, agent_type |
|
|
460
|
+
| SessionEnd | `SessionEndInput` | reason |
|
|
461
|
+
| Stop | `StopInput` | stop_hook_active |
|
|
462
|
+
| SubagentStart | `SubagentStartInput` | agent_id, agent_type |
|
|
463
|
+
| SubagentStop | `SubagentStopInput` | stop_hook_active, agent_id, agent_transcript_path |
|
|
464
|
+
| PreCompact | `PreCompactInput` | trigger, custom_instructions |
|
|
465
|
+
| PermissionRequest | `PermissionRequestInput` | tool_name, tool_input, permission_suggestions |
|
|
466
|
+
|
|
467
|
+
All hook inputs inherit from `BaseHookInput` with: `hook_event_name`, `session_id`, `transcript_path`, `cwd`, `permission_mode`.
|
|
468
|
+
|
|
469
|
+
## Permissions
|
|
470
|
+
|
|
471
|
+
Control tool permissions programmatically:
|
|
472
|
+
|
|
473
|
+
```ruby
|
|
474
|
+
options = ClaudeAgent::Options.new(
|
|
475
|
+
can_use_tool: ->(tool_name, tool_input, context) {
|
|
476
|
+
# Context includes: permission_suggestions, blocked_path, decision_reason, tool_use_id, agent_id
|
|
477
|
+
|
|
478
|
+
# Allow all read operations
|
|
479
|
+
if tool_name == "Read"
|
|
480
|
+
ClaudeAgent::PermissionResultAllow.new
|
|
481
|
+
# Deny writes to sensitive paths
|
|
482
|
+
elsif tool_name == "Write" && tool_input["file_path"].include?(".env")
|
|
483
|
+
ClaudeAgent::PermissionResultDeny.new(
|
|
484
|
+
message: "Cannot modify .env files",
|
|
485
|
+
interrupt: true
|
|
486
|
+
)
|
|
487
|
+
else
|
|
488
|
+
ClaudeAgent::PermissionResultAllow.new
|
|
489
|
+
end
|
|
490
|
+
}
|
|
491
|
+
)
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Permission Results
|
|
495
|
+
|
|
496
|
+
```ruby
|
|
497
|
+
# Allow with optional modifications
|
|
498
|
+
ClaudeAgent::PermissionResultAllow.new(
|
|
499
|
+
updated_input: { file_path: "/safe/path" }, # Modify tool input
|
|
500
|
+
updated_permissions: [...] # Update permission rules
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
# Deny
|
|
504
|
+
ClaudeAgent::PermissionResultDeny.new(
|
|
505
|
+
message: "Operation not allowed",
|
|
506
|
+
interrupt: true # Stop the agent
|
|
507
|
+
)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Permission Updates
|
|
511
|
+
|
|
512
|
+
```ruby
|
|
513
|
+
update = ClaudeAgent::PermissionUpdate.new(
|
|
514
|
+
type: "addRules", # addRules, replaceRules, removeRules, setMode, addDirectories, removeDirectories
|
|
515
|
+
rules: [
|
|
516
|
+
ClaudeAgent::PermissionRuleValue.new(tool_name: "Read", rule_content: "/**")
|
|
517
|
+
],
|
|
518
|
+
behavior: "allow",
|
|
519
|
+
destination: "session" # userSettings, projectSettings, localSettings, session, cliArg
|
|
520
|
+
)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
## Error Handling
|
|
524
|
+
|
|
525
|
+
The SDK provides specific error types:
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
begin
|
|
529
|
+
ClaudeAgent.query(prompt: "Hello")
|
|
530
|
+
rescue ClaudeAgent::CLINotFoundError
|
|
531
|
+
puts "Claude Code CLI not installed"
|
|
532
|
+
rescue ClaudeAgent::CLIVersionError => e
|
|
533
|
+
puts "CLI version too old: #{e.message}"
|
|
534
|
+
puts "Required: #{e.required_version}, Actual: #{e.actual_version}"
|
|
535
|
+
rescue ClaudeAgent::CLIConnectionError => e
|
|
536
|
+
puts "Connection failed: #{e.message}"
|
|
537
|
+
rescue ClaudeAgent::ProcessError => e
|
|
538
|
+
puts "Process error: #{e.message}, exit code: #{e.exit_code}"
|
|
539
|
+
rescue ClaudeAgent::TimeoutError => e
|
|
540
|
+
puts "Timeout: #{e.message}"
|
|
541
|
+
rescue ClaudeAgent::JSONDecodeError => e
|
|
542
|
+
puts "Invalid JSON response"
|
|
543
|
+
rescue ClaudeAgent::MessageParseError => e
|
|
544
|
+
puts "Could not parse message"
|
|
545
|
+
rescue ClaudeAgent::AbortError => e
|
|
546
|
+
puts "Operation aborted"
|
|
547
|
+
end
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Client API
|
|
551
|
+
|
|
552
|
+
For fine-grained control:
|
|
553
|
+
|
|
554
|
+
```ruby
|
|
555
|
+
client = ClaudeAgent::Client.new(options: options)
|
|
556
|
+
|
|
557
|
+
# Connect to CLI
|
|
558
|
+
client.connect
|
|
559
|
+
|
|
560
|
+
# Send queries
|
|
561
|
+
client.query("First question")
|
|
562
|
+
client.receive_response.each { |msg| process(msg) }
|
|
563
|
+
|
|
564
|
+
client.query("Follow-up question")
|
|
565
|
+
client.receive_response.each { |msg| process(msg) }
|
|
566
|
+
|
|
567
|
+
# Control methods
|
|
568
|
+
client.interrupt # Cancel current operation
|
|
569
|
+
client.set_model("claude-opus-4-5-20251101") # Change model
|
|
570
|
+
client.set_permission_mode("acceptEdits") # Change permissions
|
|
571
|
+
client.set_max_thinking_tokens(5000) # Change thinking limit
|
|
572
|
+
|
|
573
|
+
# File checkpointing (requires enable_file_checkpointing: true)
|
|
574
|
+
result = client.rewind_files("user-message-uuid", dry_run: true)
|
|
575
|
+
puts "Can rewind: #{result.can_rewind}"
|
|
576
|
+
puts "Files changed: #{result.files_changed}"
|
|
577
|
+
|
|
578
|
+
# Dynamic MCP server management
|
|
579
|
+
result = client.set_mcp_servers({
|
|
580
|
+
"my-server" => { type: "stdio", command: "node", args: ["server.js"] }
|
|
581
|
+
})
|
|
582
|
+
puts "Added: #{result.added}, Removed: #{result.removed}"
|
|
583
|
+
|
|
584
|
+
# Query capabilities
|
|
585
|
+
client.supported_commands.each { |cmd| puts "#{cmd.name}: #{cmd.description}" }
|
|
586
|
+
client.supported_models.each { |model| puts "#{model.value}: #{model.display_name}" }
|
|
587
|
+
client.mcp_server_status.each { |s| puts "#{s.name}: #{s.status}" }
|
|
588
|
+
puts client.account_info.email
|
|
589
|
+
|
|
590
|
+
# Disconnect
|
|
591
|
+
client.disconnect
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## Types Reference
|
|
595
|
+
|
|
596
|
+
### Return Types
|
|
597
|
+
|
|
598
|
+
| Type | Purpose |
|
|
599
|
+
|-----------------------|----------------------------------------------------------------------------------|
|
|
600
|
+
| `SlashCommand` | Available slash commands (name, description, argument_hint) |
|
|
601
|
+
| `ModelInfo` | Available models (value, display_name, description) |
|
|
602
|
+
| `McpServerStatus` | MCP server status (name, status, server_info) |
|
|
603
|
+
| `AccountInfo` | Account information (email, organization, subscription_type) |
|
|
604
|
+
| `ModelUsage` | Per-model usage stats (input_tokens, output_tokens, cost_usd) |
|
|
605
|
+
| `McpSetServersResult` | Result of set_mcp_servers (added, removed, errors) |
|
|
606
|
+
| `RewindFilesResult` | Result of rewind_files (can_rewind, error, files_changed, insertions, deletions) |
|
|
607
|
+
| `SDKPermissionDenial` | Permission denial info (tool_name, tool_use_id, tool_input) |
|
|
608
|
+
|
|
609
|
+
## Environment Variables
|
|
610
|
+
|
|
611
|
+
The SDK sets these automatically:
|
|
612
|
+
|
|
613
|
+
- `CLAUDE_CODE_ENTRYPOINT=sdk-rb`
|
|
614
|
+
- `CLAUDE_AGENT_SDK_VERSION=<version>`
|
|
615
|
+
|
|
616
|
+
Skip version checking (for development):
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
export CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK=true
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
## Development
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
# Install dependencies
|
|
626
|
+
bin/setup
|
|
627
|
+
|
|
628
|
+
# Run unit tests
|
|
629
|
+
bundle exec rake test
|
|
630
|
+
|
|
631
|
+
# Run integration tests (requires Claude Code CLI v2.0.0+)
|
|
632
|
+
bundle exec rake test_integration
|
|
633
|
+
|
|
634
|
+
# Run all tests
|
|
635
|
+
bundle exec rake test_all
|
|
636
|
+
|
|
637
|
+
# Validate RBS type signatures
|
|
638
|
+
bundle exec rake rbs
|
|
639
|
+
|
|
640
|
+
# Linting
|
|
641
|
+
bundle exec rubocop
|
|
642
|
+
|
|
643
|
+
# Interactive console
|
|
644
|
+
bin/console
|
|
645
|
+
|
|
646
|
+
# Binstubs for convenience
|
|
647
|
+
bin/test # Unit tests only
|
|
648
|
+
bin/test-integration # Integration tests
|
|
649
|
+
bin/test-all # All tests
|
|
650
|
+
bin/rbs-validate # Validate RBS signatures
|
|
651
|
+
bin/release 1.2.0 # Release a new version
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
## Architecture
|
|
655
|
+
|
|
656
|
+
```
|
|
657
|
+
ClaudeAgent.query() / ClaudeAgent::Client
|
|
658
|
+
│
|
|
659
|
+
▼
|
|
660
|
+
┌──────────────────────────┐
|
|
661
|
+
│ Control Protocol │ Request/response routing
|
|
662
|
+
│ - Hooks │ Permission callbacks
|
|
663
|
+
│ - MCP bridging │ Tool interception
|
|
664
|
+
└──────────┬───────────────┘
|
|
665
|
+
│
|
|
666
|
+
▼
|
|
667
|
+
┌──────────────────────────┐
|
|
668
|
+
│ Subprocess Transport │ JSON Lines protocol
|
|
669
|
+
│ - stdin/stdout │ Process management
|
|
670
|
+
│ - stderr handling │
|
|
671
|
+
└──────────┬───────────────┘
|
|
672
|
+
│
|
|
673
|
+
▼
|
|
674
|
+
Claude Code CLI
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
## License
|
|
678
|
+
|
|
679
|
+
MIT License - see [LICENSE.txt](LICENSE.txt)
|