claude-code-sdk-ruby 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.
@@ -0,0 +1,427 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module ClaudeSDK
6
+ # Permission modes for Claude Code
7
+ module PermissionMode
8
+ # Default permission mode
9
+ DEFAULT = :default
10
+
11
+ # Accept edits automatically
12
+ ACCEPT_EDITS = :accept_edits
13
+
14
+ # Bypass permission checks
15
+ BYPASS_PERMISSIONS = :bypass_permissions
16
+
17
+ # Valid permission modes
18
+ ALL = [DEFAULT, ACCEPT_EDITS, BYPASS_PERMISSIONS].freeze
19
+
20
+ # Validate if a mode is valid
21
+ #
22
+ # @param mode [Symbol] the mode to validate
23
+ # @return [Boolean] true if valid
24
+ class << self
25
+ def valid?(mode)
26
+ ALL.include?(mode)
27
+ end
28
+ end
29
+ end
30
+
31
+ # MCP Server configurations
32
+ module McpServerConfig
33
+ # MCP stdio server configuration
34
+ #
35
+ # @!attribute command [String] the command to execute
36
+ # @!attribute args [Array<String>] command arguments
37
+ # @!attribute env [Hash<String, String>] environment variables
38
+ # @!attribute type [Symbol] always :stdio
39
+ class StdioServer
40
+ attr_accessor :command, :args, :env
41
+ attr_reader :type
42
+
43
+ # @param command [String] the command to execute
44
+ # @param args [Array<String>] command arguments
45
+ # @param env [Hash<String, String>] environment variables
46
+ def initialize(command:, args: [], env: {})
47
+ @type = :stdio
48
+ @command = command
49
+ @args = args
50
+ @env = env
51
+ end
52
+
53
+ # Convert to hash for JSON serialization
54
+ #
55
+ # @return [Hash]
56
+ def to_h
57
+ hash = { command: command }
58
+ hash[:type] = type.to_s unless type == :stdio # Optional for backwards compatibility
59
+ hash[:args] = args unless args.empty?
60
+ hash[:env] = env unless env.empty?
61
+ hash
62
+ end
63
+ end
64
+
65
+ # MCP SSE server configuration
66
+ #
67
+ # @!attribute url [String] the server URL
68
+ # @!attribute headers [Hash<String, String>] HTTP headers
69
+ # @!attribute type [Symbol] always :sse
70
+ class SSEServer
71
+ attr_accessor :url, :headers
72
+ attr_reader :type
73
+
74
+ # @param url [String] the server URL
75
+ # @param headers [Hash<String, String>] HTTP headers
76
+ def initialize(url:, headers: {})
77
+ @type = :sse
78
+ @url = url
79
+ @headers = headers
80
+ end
81
+
82
+ # Convert to hash for JSON serialization
83
+ #
84
+ # @return [Hash]
85
+ def to_h
86
+ hash = { type: type.to_s, url: url }
87
+ hash[:headers] = headers unless headers.empty?
88
+ hash
89
+ end
90
+ end
91
+
92
+ # MCP HTTP server configuration
93
+ #
94
+ # @!attribute url [String] the server URL
95
+ # @!attribute headers [Hash<String, String>] HTTP headers
96
+ # @!attribute type [Symbol] always :http
97
+ class HttpServer
98
+ attr_accessor :url, :headers
99
+ attr_reader :type
100
+
101
+ # @param url [String] the server URL
102
+ # @param headers [Hash<String, String>] HTTP headers
103
+ def initialize(url:, headers: {})
104
+ @type = :http
105
+ @url = url
106
+ @headers = headers
107
+ end
108
+
109
+ # Convert to hash for JSON serialization
110
+ #
111
+ # @return [Hash]
112
+ def to_h
113
+ hash = { type: type.to_s, url: url }
114
+ hash[:headers] = headers unless headers.empty?
115
+ hash
116
+ end
117
+ end
118
+ end
119
+
120
+ # Content block types
121
+ module ContentBlock
122
+ # Text content block
123
+ #
124
+ # @!attribute text [String] the text content
125
+ class Text
126
+ attr_accessor :text
127
+
128
+ # @param text [String] the text content
129
+ def initialize(text:)
130
+ @text = text
131
+ end
132
+
133
+ # @return [Hash] serialized representation
134
+ def to_h
135
+ { type: "text", text: text }
136
+ end
137
+ end
138
+
139
+ # Tool use content block
140
+ #
141
+ # @!attribute id [String] unique identifier for this tool use
142
+ # @!attribute name [String] the tool name
143
+ # @!attribute input [Hash<String, Object>] tool input parameters
144
+ class ToolUse
145
+ attr_accessor :id, :name, :input
146
+
147
+ # @param id [String] unique identifier
148
+ # @param name [String] tool name
149
+ # @param input [Hash<String, Object>] tool input
150
+ def initialize(id:, name:, input:)
151
+ @id = id
152
+ @name = name
153
+ @input = input
154
+ end
155
+
156
+ # @return [Hash] serialized representation
157
+ def to_h
158
+ { type: "tool_use", id: id, name: name, input: input }
159
+ end
160
+ end
161
+
162
+ # Tool result content block
163
+ #
164
+ # @!attribute tool_use_id [String] ID of the corresponding tool use
165
+ # @!attribute content [String, Array<Hash>, nil] the result content
166
+ # @!attribute is_error [Boolean, nil] whether this is an error result
167
+ class ToolResult
168
+ attr_accessor :tool_use_id, :content, :is_error
169
+
170
+ # @param tool_use_id [String] ID of the tool use
171
+ # @param content [String, Array<Hash>, nil] result content
172
+ # @param is_error [Boolean, nil] error flag
173
+ def initialize(tool_use_id:, content: nil, is_error: nil)
174
+ @tool_use_id = tool_use_id
175
+ @content = content
176
+ @is_error = is_error
177
+ end
178
+
179
+ # @return [Hash] serialized representation
180
+ def to_h
181
+ hash = { type: "tool_result", tool_use_id: tool_use_id }
182
+ hash[:content] = content unless content.nil?
183
+ hash[:is_error] = is_error unless is_error.nil?
184
+ hash
185
+ end
186
+ end
187
+ end
188
+
189
+ # Message types
190
+ module Messages
191
+ # User message
192
+ #
193
+ # @!attribute content [String] the message content
194
+ class User
195
+ attr_accessor :content
196
+
197
+ # @param content [String] the message content
198
+ def initialize(content:)
199
+ @content = content
200
+ end
201
+
202
+ # @return [Hash] serialized representation
203
+ def to_h
204
+ { role: "user", content: content }
205
+ end
206
+ end
207
+
208
+ # Assistant message with content blocks
209
+ #
210
+ # @!attribute content [Array<ContentBlock::Text, ContentBlock::ToolUse, ContentBlock::ToolResult>] content blocks
211
+ class Assistant
212
+ attr_accessor :content
213
+
214
+ # @param content [Array<ContentBlock>] content blocks
215
+ def initialize(content:)
216
+ @content = content
217
+ end
218
+
219
+ # @return [Hash] serialized representation
220
+ def to_h
221
+ { role: "assistant", content: content.map(&:to_h) }
222
+ end
223
+ end
224
+
225
+ # System message with metadata
226
+ #
227
+ # @!attribute subtype [String] the system message subtype
228
+ # @!attribute data [Hash<String, Object>] metadata
229
+ class System
230
+ attr_accessor :subtype, :data
231
+
232
+ # @param subtype [String] message subtype
233
+ # @param data [Hash<String, Object>] metadata
234
+ def initialize(subtype:, data:)
235
+ @subtype = subtype
236
+ @data = data
237
+ end
238
+
239
+ # @return [Hash] serialized representation
240
+ def to_h
241
+ { role: "system", subtype: subtype, data: data }
242
+ end
243
+ end
244
+
245
+ # Result message with cost and usage information
246
+ #
247
+ # @!attribute subtype [String] the result subtype
248
+ # @!attribute duration_ms [Integer] total duration in milliseconds
249
+ # @!attribute duration_api_ms [Integer] API duration in milliseconds
250
+ # @!attribute is_error [Boolean] whether this is an error result
251
+ # @!attribute num_turns [Integer] number of conversation turns
252
+ # @!attribute session_id [String] unique session identifier
253
+ # @!attribute total_cost_usd [Float, nil] total cost in USD
254
+ # @!attribute usage [Hash<String, Object>, nil] usage statistics
255
+ # @!attribute result [String, nil] the result content
256
+ class Result
257
+ attr_accessor :subtype,
258
+ :duration_ms,
259
+ :duration_api_ms,
260
+ :is_error,
261
+ :num_turns,
262
+ :session_id,
263
+ :total_cost_usd,
264
+ :usage,
265
+ :result
266
+
267
+ # @param subtype [String] result subtype
268
+ # @param duration_ms [Integer] total duration
269
+ # @param duration_api_ms [Integer] API duration
270
+ # @param is_error [Boolean] error flag
271
+ # @param num_turns [Integer] turn count
272
+ # @param session_id [String] session ID
273
+ # @param total_cost_usd [Float, nil] cost in USD
274
+ # @param usage [Hash, nil] usage stats
275
+ # @param result [String, nil] result content
276
+ def initialize(subtype:, duration_ms:, duration_api_ms:, is_error:,
277
+ num_turns:, session_id:, total_cost_usd: nil,
278
+ usage: nil, result: nil)
279
+ @subtype = subtype
280
+ @duration_ms = duration_ms
281
+ @duration_api_ms = duration_api_ms
282
+ @is_error = is_error
283
+ @num_turns = num_turns
284
+ @session_id = session_id
285
+ @total_cost_usd = total_cost_usd
286
+ @usage = usage
287
+ @result = result
288
+ end
289
+
290
+ # @return [Hash] serialized representation
291
+ def to_h
292
+ hash = {
293
+ role: "result",
294
+ subtype: subtype,
295
+ duration_ms: duration_ms,
296
+ duration_api_ms: duration_api_ms,
297
+ is_error: is_error,
298
+ num_turns: num_turns,
299
+ session_id: session_id,
300
+ }
301
+ hash[:total_cost_usd] = total_cost_usd unless total_cost_usd.nil?
302
+ hash[:usage] = usage unless usage.nil?
303
+ hash[:result] = result unless result.nil?
304
+ hash
305
+ end
306
+ end
307
+ end
308
+
309
+ # Query options for Claude SDK
310
+ #
311
+ # @!attribute allowed_tools [Array<String>] list of allowed tools
312
+ # @!attribute max_thinking_tokens [Integer] maximum thinking tokens
313
+ # @!attribute system_prompt [String, nil] system prompt override
314
+ # @!attribute append_system_prompt [String, nil] additional system prompt
315
+ # @!attribute mcp_tools [Array<String>] MCP tools to enable
316
+ # @!attribute mcp_servers [Hash<String, McpServerConfig>] MCP server configurations
317
+ # @!attribute permission_mode [Symbol, nil] permission mode
318
+ # @!attribute continue_conversation [Boolean] continue existing conversation
319
+ # @!attribute resume [String, nil] resume from session ID
320
+ # @!attribute max_turns [Integer, nil] maximum conversation turns
321
+ # @!attribute disallowed_tools [Array<String>] list of disallowed tools
322
+ # @!attribute model [String, nil] model to use
323
+ # @!attribute permission_prompt_tool_name [String, nil] permission prompt tool
324
+ # @!attribute cwd [String, Pathname, nil] working directory
325
+ class ClaudeCodeOptions
326
+ attr_accessor :allowed_tools,
327
+ :max_thinking_tokens,
328
+ :system_prompt,
329
+ :append_system_prompt,
330
+ :mcp_tools,
331
+ :mcp_servers,
332
+ :permission_mode,
333
+ :continue_conversation,
334
+ :resume,
335
+ :max_turns,
336
+ :disallowed_tools,
337
+ :model,
338
+ :permission_prompt_tool_name,
339
+ :cwd
340
+
341
+ # Initialize with default values
342
+ #
343
+ # @param allowed_tools [Array<String>] allowed tools (default: [])
344
+ # @param max_thinking_tokens [Integer] max thinking tokens (default: 8000)
345
+ # @param system_prompt [String, nil] system prompt
346
+ # @param append_system_prompt [String, nil] append to system prompt
347
+ # @param mcp_tools [Array<String>] MCP tools (default: [])
348
+ # @param mcp_servers [Hash] MCP servers (default: {})
349
+ # @param permission_mode [Symbol, nil] permission mode
350
+ # @param continue_conversation [Boolean] continue conversation (default: false)
351
+ # @param resume [String, nil] resume session ID
352
+ # @param max_turns [Integer, nil] max turns
353
+ # @param disallowed_tools [Array<String>] disallowed tools (default: [])
354
+ # @param model [String, nil] model name
355
+ # @param permission_prompt_tool_name [String, nil] permission tool
356
+ # @param cwd [String, Pathname, nil] working directory
357
+ def initialize(allowed_tools: [],
358
+ max_thinking_tokens: 8000,
359
+ system_prompt: nil,
360
+ append_system_prompt: nil,
361
+ mcp_tools: [],
362
+ mcp_servers: {},
363
+ permission_mode: nil,
364
+ continue_conversation: false,
365
+ resume: nil,
366
+ max_turns: nil,
367
+ disallowed_tools: [],
368
+ model: nil,
369
+ permission_prompt_tool_name: nil,
370
+ cwd: nil)
371
+ @allowed_tools = allowed_tools
372
+ @max_thinking_tokens = max_thinking_tokens
373
+ @system_prompt = system_prompt
374
+ @append_system_prompt = append_system_prompt
375
+ @mcp_tools = mcp_tools
376
+ @mcp_servers = mcp_servers
377
+ @permission_mode = permission_mode
378
+ @continue_conversation = continue_conversation
379
+ @resume = resume
380
+ @max_turns = max_turns
381
+ @disallowed_tools = disallowed_tools
382
+ @model = model
383
+ @permission_prompt_tool_name = permission_prompt_tool_name
384
+ @cwd = cwd
385
+
386
+ validate_permission_mode! if permission_mode
387
+ end
388
+
389
+ # Convert to hash for JSON serialization
390
+ #
391
+ # @return [Hash]
392
+ def to_h
393
+ hash = {}
394
+ hash[:allowed_tools] = allowed_tools unless allowed_tools.empty?
395
+ hash[:max_thinking_tokens] = max_thinking_tokens if max_thinking_tokens != 8000
396
+ hash[:system_prompt] = system_prompt if system_prompt
397
+ hash[:append_system_prompt] = append_system_prompt if append_system_prompt
398
+ hash[:mcp_tools] = mcp_tools unless mcp_tools.empty?
399
+ hash[:mcp_servers] = serialize_mcp_servers unless mcp_servers.empty?
400
+ hash[:permission_mode] = permission_mode.to_s if permission_mode
401
+ hash[:continue_conversation] = continue_conversation if continue_conversation
402
+ hash[:resume] = resume if resume
403
+ hash[:max_turns] = max_turns if max_turns
404
+ hash[:disallowed_tools] = disallowed_tools unless disallowed_tools.empty?
405
+ hash[:model] = model if model
406
+ hash[:permission_prompt_tool_name] = permission_prompt_tool_name if permission_prompt_tool_name
407
+ hash[:cwd] = cwd.to_s if cwd
408
+ hash
409
+ end
410
+
411
+ private
412
+
413
+ # Validate permission mode
414
+ def validate_permission_mode!
415
+ return if PermissionMode.valid?(@permission_mode)
416
+
417
+ raise ArgumentError, "Invalid permission mode: #{@permission_mode}"
418
+ end
419
+
420
+ # Serialize MCP servers to hashes
421
+ def serialize_mcp_servers
422
+ mcp_servers.transform_values do |server|
423
+ server.respond_to?(:to_h) ? server.to_h : server
424
+ end
425
+ end
426
+ end
427
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeSDK
4
+ VERSION = "0.1.0"
5
+ end
data/lib/claude_sdk.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async"
4
+
5
+ require_relative "claude_sdk/version"
6
+ require_relative "claude_sdk/errors"
7
+ require_relative "claude_sdk/types"
8
+ require_relative "claude_sdk/internal/client"
9
+
10
+ module ClaudeSDK
11
+ class << self
12
+ # Query Claude Code
13
+ #
14
+ # @param prompt [String] The prompt to send to Claude
15
+ # @param options [ClaudeCodeOptions, nil] Optional configuration
16
+ # @yield [Message] Messages from the conversation
17
+ # @return [Enumerator] if no block given
18
+ #
19
+ # @example Simple usage
20
+ # ClaudeSDK.query("Hello") do |message|
21
+ # puts message
22
+ # end
23
+ #
24
+ # @example With options
25
+ # options = ClaudeSDK::ClaudeCodeOptions.new(
26
+ # system_prompt: "You are helpful",
27
+ # cwd: "/home/user"
28
+ # )
29
+ # ClaudeSDK.query("Hello", options: options) do |message|
30
+ # puts message
31
+ # end
32
+ #
33
+ # @example Without block (returns Enumerator)
34
+ # messages = ClaudeSDK.query("Hello")
35
+ # messages.each { |msg| puts msg }
36
+ def query(prompt, options: nil, &block)
37
+ options ||= ClaudeCodeOptions.new
38
+
39
+ ENV["CLAUDE_CODE_ENTRYPOINT"] = "sdk-rb"
40
+
41
+ client = Internal::InternalClient.new
42
+
43
+ if block_given?
44
+ Async do
45
+ client.process_query(prompt: prompt, options: options, &block)
46
+ end.wait
47
+ else
48
+ Enumerator.new do |yielder|
49
+ Async do
50
+ client.process_query(prompt: prompt, options: options) do |message|
51
+ yielder << message
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Ruby SDK Maintenance Team"
4
+ main: ruby_sdk_maintainer
5
+ instances:
6
+ ruby_sdk_maintainer:
7
+ description: "Ruby SDK maintainer coordinating development and maintenance tasks"
8
+ directory: .
9
+ model: opus
10
+ vibe: true
11
+ connections: [github_assistant, python_sdk_guide]
12
+ prompt: |
13
+ You are the lead maintainer of the Claude Code Ruby SDK. Your responsibilities include:
14
+
15
+ - Coordinating overall SDK development and maintenance
16
+ - Implementing new features and fixing bugs
17
+ - Ensuring code quality and consistency
18
+ - Managing releases and documentation
19
+ - Working with the GitHub assistant for repository operations
20
+ - Consulting with the Python SDK guide for implementation guidance
21
+
22
+ You have access to all tools and can delegate GitHub operations to the github_assistant
23
+ and consult the python_sdk_guide for best practices and implementation patterns.
24
+
25
+ For maximum efficiency, whenever you need to perform multiple independent operations,
26
+ invoke all relevant tools simultaneously rather than sequentially.
27
+
28
+ github_assistant:
29
+ description: "GitHub operations specialist using gh CLI for all repository management"
30
+ directory: .
31
+ model: sonnet
32
+ vibe: true
33
+ prompt: |
34
+ You are a GitHub operations specialist for the Claude Code Ruby SDK project. Your role is to:
35
+
36
+ - Handle all GitHub operations using the gh CLI tool
37
+ - Manage issues, pull requests, and releases
38
+ - Perform repository management tasks
39
+ - Assist with GitHub Actions and CI/CD operations
40
+ - Provide GitHub-specific insights and automation
41
+
42
+ You primarily use the gh CLI tool and Bash for all GitHub-related operations.
43
+ Always use gh commands when possible instead of git commands for GitHub operations.
44
+
45
+ For maximum efficiency, whenever you need to perform multiple independent operations,
46
+ invoke all relevant tools simultaneously rather than sequentially.
47
+
48
+ python_sdk_guide:
49
+ description: "Python SDK consultant providing implementation guidance and best practices"
50
+ directory: .
51
+ model: sonnet
52
+ vibe: true
53
+ prompt: |
54
+ You are a Python SDK consultant specializing in the Claude Code Python SDK. Your role is to:
55
+
56
+ - Provide guidance on how the Ruby SDK should be structured based on Python SDK patterns
57
+ - Analyze Python SDK implementations for best practices
58
+ - Suggest Ruby equivalents for Python SDK features
59
+ - Help maintain consistency between SDKs while respecting language idioms
60
+ - Read and understand Python SDK code to inform Ruby development
61
+
62
+ You primarily use Read and WebFetch tools to analyze the Python SDK and provide
63
+ implementation guidance. Focus on reading the Python SDK codebase to understand
64
+ patterns and suggest how they should be adapted for Ruby.
65
+
66
+ For maximum efficiency, whenever you need to perform multiple independent operations,
67
+ invoke all relevant tools simultaneously rather than sequentially.
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: claude-code-sdk-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paulo Arruda
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-07-15 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: async
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.26'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.26'
26
+ - !ruby/object:Gem::Dependency
27
+ name: logger
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.7'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.7'
40
+ description: Ruby SDK for interacting with Claude Code
41
+ email:
42
+ - parrudaj@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".rspec"
48
+ - ".rubocop.yml"
49
+ - ".ruby-version"
50
+ - ".yardopts"
51
+ - CHANGELOG.md
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - claude-code-sdk-ruby.gemspec
56
+ - examples/async_multiple_queries.rb
57
+ - examples/basic_usage.rb
58
+ - examples/enumerator_usage.rb
59
+ - examples/error_handling.rb
60
+ - examples/mcp_servers.rb
61
+ - examples/with_options.rb
62
+ - lib/claude_sdk.rb
63
+ - lib/claude_sdk/errors.rb
64
+ - lib/claude_sdk/internal/client.rb
65
+ - lib/claude_sdk/internal/transport.rb
66
+ - lib/claude_sdk/internal/transport/subprocess_cli.rb
67
+ - lib/claude_sdk/types.rb
68
+ - lib/claude_sdk/version.rb
69
+ - ruby-sdk-maintenance-swarm.yml
70
+ homepage: https://github.com/parruda/claude-code-sdk-ruby
71
+ licenses:
72
+ - MIT
73
+ metadata:
74
+ allowed_push_host: https://rubygems.org
75
+ homepage_uri: https://github.com/parruda/claude-code-sdk-ruby
76
+ source_code_uri: https://github.com/parruda/claude-code-sdk-ruby
77
+ changelog_uri: https://github.com/parruda/claude-code-sdk-ruby/blob/main/CHANGELOG.md
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 3.0.0
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.6.2
93
+ specification_version: 4
94
+ summary: Ruby SDK for Claude Code
95
+ test_files: []