claude-agent-sdk 0.16.1 → 0.16.2

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: 784f87932778dac72fe69ebbe3236e62a7df31f9671b52150fae392aa3d57f05
4
- data.tar.gz: cd743d1cabb8bd151457f0a4b18be725f25b5de684ba2430df8a76037900dd98
3
+ metadata.gz: 184cf490ead1c830e0733d720e8f3580310ea3d68880e4e2945b28f803bea83b
4
+ data.tar.gz: fd3d83127d247eec43f30852ee8e5a75b14420721ad32b14e2ee7a811ed1d673
5
5
  SHA512:
6
- metadata.gz: 68b582ef69fec09c0487d6d5600c76ac5d5a8420264f2b151cc7f52f95c7fb975339d6afa9fe519d4e75b48781c4f6132446fb03b2e9995decc3088ccd66c0f1
7
- data.tar.gz: d119aaba15cb76997ceb58abbc43176a82df9ad4b697089a6b26ffbe342fb50ea10173890dddb03d5dc13397989abb774b617f140b1f92b3fdb0a8d16e3298d2
6
+ metadata.gz: 054c00ec53645630c0a60e66d78503b46da03f6bc3b43f9021c4dd3274d3309345af83751a7556074e7303205f1d502361c9f1d339a8f9201aea92b5f0dfda6a
7
+ data.tar.gz: e32f7b8a2ed41fe1c5db98e5254b1abddcfffe4f935508fe61eeef1c10ed11c1bb14a344fea16f46300d1fd3e0c8e150884b0340600dd161ec1c9582a0ff4289
data/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.16.2] - 2026-04-23
11
+
12
+ ### Changed
13
+ - Extracted `ClaudeAgentSDK::CommandBuilder` from `SubprocessCLITransport`. CLI argv assembly now lives in its own class and can be exercised in isolation — `CommandBuilder.new(cli_path, options).build` returns the argv array without booting a transport. No public behavior change; `SubprocessCLITransport#build_command` still works and now delegates to `CommandBuilder`.
14
+
10
15
  ## [0.16.1] - 2026-04-21
11
16
 
12
17
  ### Fixed
data/README.md CHANGED
@@ -108,7 +108,7 @@ Add this line to your application's Gemfile:
108
108
  gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
109
109
 
110
110
  # Or use a stable version from RubyGems
111
- gem 'claude-agent-sdk', '~> 0.16.1'
111
+ gem 'claude-agent-sdk', '~> 0.16.2'
112
112
  ```
113
113
 
114
114
  And then execute:
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "errors"
5
+ require_relative "types"
6
+
7
+ module ClaudeAgentSDK
8
+ # Builds the CLI argv array from a ClaudeAgentOptions instance.
9
+ class CommandBuilder
10
+ EXTRA_ARG_FLAG_REGEXP = /\A[a-z0-9][a-z0-9-]*\z/
11
+
12
+ def initialize(cli_path, options)
13
+ @cli_path = cli_path
14
+ @options = options
15
+ end
16
+
17
+ def build
18
+ cmd = [@cli_path, "--output-format", "stream-json", "--verbose"]
19
+
20
+ append_system_prompt(cmd)
21
+ append_tools(cmd)
22
+ append_allowed_tools(cmd)
23
+ append_disallowed_tools(cmd)
24
+ append_max_turns(cmd)
25
+ append_model(cmd)
26
+ append_permission(cmd)
27
+ append_session(cmd)
28
+ append_settings(cmd)
29
+ append_budget(cmd)
30
+ append_thinking(cmd)
31
+ append_effort(cmd)
32
+ append_betas(cmd)
33
+ append_append_allowed_tools(cmd)
34
+ append_output_format(cmd)
35
+ append_additional_dirs(cmd)
36
+ append_mcp_servers(cmd)
37
+ append_boolean_flags(cmd)
38
+ append_plugins(cmd)
39
+ append_setting_sources(cmd)
40
+ append_extra_args(cmd)
41
+
42
+ # Always use streaming mode for bidirectional control protocol.
43
+ # Prompts and agents are sent via stdin (initialize + user messages),
44
+ # which avoids OS ARG_MAX limits for large prompts and agent configurations.
45
+ cmd.push("--input-format", "stream-json")
46
+
47
+ cmd
48
+ end
49
+
50
+ private
51
+
52
+ def append_system_prompt(cmd)
53
+ case @options.system_prompt
54
+ when nil
55
+ # When nil, pass empty string to ensure predictable behavior without default Claude Code system prompt
56
+ cmd.push("--system-prompt", "")
57
+ when String
58
+ cmd.push("--system-prompt", @options.system_prompt)
59
+ when SystemPromptFile
60
+ cmd.push("--system-prompt-file", @options.system_prompt.path)
61
+ when SystemPromptPreset
62
+ # Preset activates the default Claude Code system prompt by not passing --system-prompt ""
63
+ # Only --append-system-prompt is passed if append text is provided
64
+ cmd.push("--append-system-prompt", @options.system_prompt.append) if @options.system_prompt.append
65
+ when Hash
66
+ append_hash_system_prompt(cmd, @options.system_prompt)
67
+ end
68
+ end
69
+
70
+ def append_hash_system_prompt(cmd, prompt_hash)
71
+ prompt_type = prompt_hash[:type] || prompt_hash["type"]
72
+ case prompt_type
73
+ when "file"
74
+ prompt_path = prompt_hash[:path] || prompt_hash["path"]
75
+ cmd.push("--system-prompt-file", prompt_path) if prompt_path
76
+ when "preset"
77
+ append = prompt_hash[:append] || prompt_hash["append"]
78
+ # Preset activates the default Claude Code system prompt by not passing --system-prompt ""
79
+ cmd.push("--append-system-prompt", append) if append
80
+ end
81
+ end
82
+
83
+ def append_allowed_tools(cmd)
84
+ cmd.push("--allowedTools", @options.allowed_tools.join(",")) unless @options.allowed_tools.empty?
85
+ end
86
+
87
+ def append_disallowed_tools(cmd)
88
+ cmd.push("--disallowedTools", @options.disallowed_tools.join(",")) unless @options.disallowed_tools.empty?
89
+ end
90
+
91
+ def append_max_turns(cmd)
92
+ cmd.push("--max-turns", @options.max_turns.to_s) if @options.max_turns
93
+ end
94
+
95
+ def append_model(cmd)
96
+ cmd.push("--model", @options.model) if @options.model
97
+ cmd.push("--fallback-model", @options.fallback_model) if @options.fallback_model
98
+ end
99
+
100
+ def append_permission(cmd)
101
+ cmd.push("--permission-prompt-tool", @options.permission_prompt_tool_name) if @options.permission_prompt_tool_name
102
+ cmd.push("--permission-mode", @options.permission_mode) if @options.permission_mode
103
+ end
104
+
105
+ def append_session(cmd)
106
+ cmd.push("--continue") if @options.continue_conversation
107
+ cmd.push("--resume", @options.resume) if @options.resume
108
+ cmd.push("--session-id", @options.session_id) if @options.session_id
109
+ end
110
+
111
+ def append_settings(cmd)
112
+ return unless @options.settings || @options.sandbox
113
+
114
+ settings_hash = {}
115
+ settings_is_path = false
116
+
117
+ if @options.settings
118
+ if @options.settings.is_a?(String)
119
+ begin
120
+ settings_hash = JSON.parse(@options.settings)
121
+ rescue JSON::ParserError
122
+ if @options.sandbox
123
+ settings_hash = load_settings_file(@options.settings)
124
+ else
125
+ settings_is_path = true
126
+ cmd.push("--settings", @options.settings)
127
+ end
128
+ end
129
+ elsif @options.settings.is_a?(Hash)
130
+ settings_hash = @options.settings.dup
131
+ end
132
+ end
133
+
134
+ if !settings_is_path && @options.sandbox
135
+ sandbox_hash = @options.sandbox.is_a?(SandboxSettings) ? @options.sandbox.to_h : @options.sandbox
136
+ settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
137
+ end
138
+
139
+ cmd.push("--settings", JSON.generate(settings_hash)) if !settings_is_path && !settings_hash.empty?
140
+ end
141
+
142
+ def append_budget(cmd)
143
+ cmd.push("--max-budget-usd", @options.max_budget_usd.to_s) if @options.max_budget_usd
144
+
145
+ return unless @options.task_budget
146
+
147
+ total = if @options.task_budget.is_a?(TaskBudget)
148
+ @options.task_budget.total
149
+ else
150
+ @options.task_budget[:total] || @options.task_budget["total"]
151
+ end
152
+ cmd.push("--task-budget", total.to_s) if total
153
+ end
154
+
155
+ # Thinking configuration takes precedence over deprecated max_thinking_tokens
156
+ def append_thinking(cmd)
157
+ if @options.thinking
158
+ case @options.thinking
159
+ when ThinkingConfigAdaptive
160
+ cmd.push("--thinking", "adaptive")
161
+ when ThinkingConfigEnabled
162
+ cmd.push("--max-thinking-tokens", @options.thinking.budget_tokens.to_s)
163
+ when ThinkingConfigDisabled
164
+ cmd.push("--thinking", "disabled")
165
+ end
166
+ elsif @options.max_thinking_tokens
167
+ cmd.push("--max-thinking-tokens", @options.max_thinking_tokens.to_s)
168
+ end
169
+ end
170
+
171
+ # The set of supported levels is model-dependent; the CLI falls back to
172
+ # the highest supported level at or below the one requested
173
+ # (e.g. `xhigh` → `high` on Opus 4.6).
174
+ def append_effort(cmd)
175
+ cmd.push("--effort", @options.effort.to_s) if @options.effort
176
+ end
177
+
178
+ def append_betas(cmd)
179
+ return unless @options.betas && !@options.betas.empty?
180
+
181
+ cmd.push("--betas", @options.betas.join(","))
182
+ end
183
+
184
+ def append_tools(cmd)
185
+ return if @options.tools.nil?
186
+
187
+ case @options.tools
188
+ when Array
189
+ tools_value = @options.tools.empty? ? "" : @options.tools.join(",")
190
+ cmd.push("--tools", tools_value)
191
+ when ToolsPreset
192
+ cmd.push("--tools", "default")
193
+ when Hash
194
+ if (@options.tools[:type] || @options.tools["type"]) == "preset"
195
+ cmd.push("--tools", "default")
196
+ else
197
+ cmd.push("--tools", JSON.generate(@options.tools))
198
+ end
199
+ end
200
+ end
201
+
202
+ def append_append_allowed_tools(cmd)
203
+ return unless @options.append_allowed_tools && !@options.append_allowed_tools.empty?
204
+
205
+ cmd.push("--append-allowed-tools", @options.append_allowed_tools.join(","))
206
+ end
207
+
208
+ def append_output_format(cmd)
209
+ return unless @options.output_format
210
+
211
+ schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == "json_schema"
212
+ @options.output_format[:schema]
213
+ elsif @options.output_format.is_a?(Hash) && @options.output_format["type"] == "json_schema"
214
+ @options.output_format["schema"]
215
+ else
216
+ @options.output_format
217
+ end
218
+ schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
219
+ cmd.push("--json-schema", schema_json)
220
+ end
221
+
222
+ def append_additional_dirs(cmd)
223
+ @options.add_dirs.each { |dir| cmd.push("--add-dir", dir.to_s) }
224
+ end
225
+
226
+ def append_mcp_servers(cmd)
227
+ return unless @options.mcp_servers && !@options.mcp_servers.empty?
228
+
229
+ if @options.mcp_servers.is_a?(Hash)
230
+ servers_for_cli = {}
231
+ @options.mcp_servers.each do |name, config|
232
+ servers_for_cli[name] = if config.is_a?(Hash) && config[:type] == "sdk"
233
+ config.except(:instance)
234
+ else
235
+ config
236
+ end
237
+ end
238
+ cmd.push("--mcp-config", JSON.generate({ mcpServers: servers_for_cli })) unless servers_for_cli.empty?
239
+ else
240
+ cmd.push("--mcp-config", @options.mcp_servers.to_s)
241
+ end
242
+ end
243
+
244
+ # NOTE: agents are sent via the initialize control request (not CLI args)
245
+ # to avoid OS ARG_MAX limits with large agent configurations.
246
+ def append_boolean_flags(cmd)
247
+ cmd.push("--include-partial-messages") if @options.include_partial_messages
248
+ cmd.push("--fork-session") if @options.fork_session
249
+ cmd.push("--bare") if @options.bare
250
+ end
251
+
252
+ def append_plugins(cmd)
253
+ return unless @options.plugins && !@options.plugins.empty?
254
+
255
+ @options.plugins.each do |plugin|
256
+ plugin_config = plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
257
+ plugin_type = plugin_config[:type] || plugin_config["type"]
258
+ plugin_path = plugin_config[:path] || plugin_config["path"]
259
+
260
+ raise ArgumentError, "Unsupported plugin type: #{plugin_type.inspect}" unless %w[local plugin].include?(plugin_type)
261
+ next unless plugin_path
262
+
263
+ cmd.push("--plugin-dir", plugin_path)
264
+ end
265
+ end
266
+
267
+ def append_setting_sources(cmd)
268
+ return unless @options.setting_sources
269
+
270
+ cmd.push("--setting-sources", @options.setting_sources.join(","))
271
+ end
272
+
273
+ def append_extra_args(cmd)
274
+ @options.extra_args.each do |flag, value|
275
+ raise ArgumentError, "Invalid extra_args flag name: #{flag.inspect} (expected lowercase kebab-case)" unless EXTRA_ARG_FLAG_REGEXP.match?(flag)
276
+
277
+ if value.nil?
278
+ cmd.push("--#{flag}")
279
+ else
280
+ cmd.push("--#{flag}", value.to_s)
281
+ end
282
+ end
283
+ end
284
+
285
+ def load_settings_file(path)
286
+ raise CLIConnectionError, "Settings file not found: #{path}" unless File.file?(path)
287
+
288
+ JSON.parse(File.read(path))
289
+ end
290
+ end
291
+ end
@@ -6,13 +6,13 @@ require 'timeout'
6
6
  require_relative 'transport'
7
7
  require_relative 'errors'
8
8
  require_relative 'version'
9
+ require_relative 'command_builder'
9
10
 
10
11
  module ClaudeAgentSDK
11
12
  # Subprocess transport using Claude Code CLI
12
13
  class SubprocessCLITransport < Transport
13
14
  DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit
14
15
  MINIMUM_CLAUDE_CODE_VERSION = '2.0.0'
15
- EXTRA_ARG_FLAG_RE = /\A[a-z0-9][a-z0-9-]*\z/
16
16
 
17
17
  def initialize(options_or_prompt = nil, options = nil)
18
18
  # Support both new single-arg form and legacy two-arg form
@@ -67,126 +67,7 @@ module ClaudeAgentSDK
67
67
  end
68
68
 
69
69
  def build_command
70
- cmd = [@cli_path, '--output-format', 'stream-json', '--verbose']
71
-
72
- # System prompt handling
73
- # When nil, pass empty string to ensure predictable behavior without default Claude Code system prompt
74
- if @options.system_prompt.nil?
75
- cmd.concat(['--system-prompt', ''])
76
- elsif @options.system_prompt.is_a?(String)
77
- cmd.concat(['--system-prompt', @options.system_prompt])
78
- elsif @options.system_prompt.is_a?(SystemPromptFile)
79
- cmd.concat(['--system-prompt-file', @options.system_prompt.path])
80
- elsif @options.system_prompt.is_a?(SystemPromptPreset)
81
- # Preset activates the default Claude Code system prompt by not passing --system-prompt ""
82
- # Only --append-system-prompt is passed if append text is provided
83
- cmd.concat(['--append-system-prompt', @options.system_prompt.append]) if @options.system_prompt.append
84
- elsif @options.system_prompt.is_a?(Hash)
85
- prompt_type = @options.system_prompt[:type] || @options.system_prompt['type']
86
- if prompt_type == 'file'
87
- prompt_path = @options.system_prompt[:path] || @options.system_prompt['path']
88
- cmd.concat(['--system-prompt-file', prompt_path]) if prompt_path
89
- elsif prompt_type == 'preset'
90
- append = @options.system_prompt[:append] || @options.system_prompt['append']
91
- # Preset activates the default Claude Code system prompt by not passing --system-prompt ""
92
- cmd.concat(['--append-system-prompt', append]) if append
93
- end
94
- end
95
-
96
- cmd.concat(['--allowedTools', @options.allowed_tools.join(',')]) unless @options.allowed_tools.empty?
97
- cmd.concat(['--max-turns', @options.max_turns.to_s]) if @options.max_turns
98
- cmd.concat(['--disallowedTools', @options.disallowed_tools.join(',')]) unless @options.disallowed_tools.empty?
99
- cmd.concat(['--model', @options.model]) if @options.model
100
- cmd.concat(['--fallback-model', @options.fallback_model]) if @options.fallback_model
101
- cmd.concat(['--permission-prompt-tool', @options.permission_prompt_tool_name]) if @options.permission_prompt_tool_name
102
- cmd.concat(['--permission-mode', @options.permission_mode]) if @options.permission_mode
103
- cmd << '--continue' if @options.continue_conversation
104
- cmd.concat(['--resume', @options.resume]) if @options.resume
105
- cmd.concat(['--session-id', @options.session_id]) if @options.session_id
106
-
107
- # Settings handling with sandbox merge
108
- build_settings_args(cmd)
109
-
110
- # Budget limit option
111
- cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
112
-
113
- # Task budget (API-side token budget)
114
- if @options.task_budget
115
- total = if @options.task_budget.is_a?(TaskBudget)
116
- @options.task_budget.total
117
- else
118
- @options.task_budget[:total] || @options.task_budget['total']
119
- end
120
- cmd.concat(['--task-budget', total.to_s]) if total
121
- end
122
-
123
- # Thinking configuration (takes precedence over deprecated max_thinking_tokens)
124
- build_thinking_args(cmd)
125
-
126
- # Effort level — see ClaudeAgentSDK::EFFORT_LEVELS. The set of supported
127
- # levels is model-dependent; the CLI falls back to the highest supported
128
- # level at or below the one requested (e.g. `xhigh` → `high` on Opus 4.6).
129
- cmd.concat(['--effort', @options.effort.to_s]) if @options.effort
130
-
131
- # Betas option for enabling experimental features
132
- if @options.betas && !@options.betas.empty?
133
- cmd.concat(['--betas', @options.betas.join(',')])
134
- end
135
-
136
- # Tools option for base tools selection
137
- build_tools_args(cmd)
138
-
139
- # Append allowed tools option
140
- if @options.append_allowed_tools && !@options.append_allowed_tools.empty?
141
- cmd.concat(['--append-allowed-tools', @options.append_allowed_tools.join(',')])
142
- end
143
-
144
- # JSON schema for structured output
145
- build_output_format_args(cmd)
146
-
147
- # Add directories
148
- @options.add_dirs.each do |dir|
149
- cmd.concat(['--add-dir', dir.to_s])
150
- end
151
-
152
- # MCP servers
153
- build_mcp_servers_args(cmd)
154
-
155
- cmd << '--include-partial-messages' if @options.include_partial_messages
156
- cmd << '--fork-session' if @options.fork_session
157
- cmd << '--bare' if @options.bare
158
-
159
- # Note: agents are now sent via the initialize control request (not CLI args)
160
- # to avoid OS ARG_MAX limits with large agent configurations.
161
-
162
- # Plugins
163
- build_plugins_args(cmd)
164
-
165
- # Setting sources
166
- if @options.setting_sources
167
- cmd.concat(['--setting-sources', @options.setting_sources.join(',')])
168
- end
169
-
170
- # Extra args
171
- @options.extra_args.each do |flag, value|
172
- flag_str = flag.to_s
173
- unless EXTRA_ARG_FLAG_RE.match?(flag_str)
174
- raise ArgumentError, "Invalid extra_args flag name: #{flag.inspect} (expected lowercase kebab-case)"
175
- end
176
-
177
- if value.nil?
178
- cmd << "--#{flag_str}"
179
- else
180
- cmd.concat(["--#{flag_str}", value.to_s])
181
- end
182
- end
183
-
184
- # Always use streaming mode for bidirectional control protocol.
185
- # Prompts and agents are sent via stdin (initialize + user messages),
186
- # which avoids OS ARG_MAX limits for large prompts and agent configurations.
187
- cmd.concat(['--input-format', 'stream-json'])
188
-
189
- cmd
70
+ CommandBuilder.new(@cli_path, @options).build
190
71
  end
191
72
 
192
73
  def connect
@@ -514,123 +395,5 @@ module ClaudeAgentSDK
514
395
  def ready?
515
396
  @ready
516
397
  end
517
-
518
- private
519
-
520
- def build_settings_args(cmd)
521
- return unless @options.settings || @options.sandbox
522
-
523
- settings_hash = {}
524
- settings_is_path = false
525
-
526
- if @options.settings
527
- if @options.settings.is_a?(String)
528
- begin
529
- settings_hash = JSON.parse(@options.settings)
530
- rescue JSON::ParserError
531
- if @options.sandbox
532
- settings_hash = load_settings_file(@options.settings)
533
- else
534
- settings_is_path = true
535
- cmd.concat(['--settings', @options.settings])
536
- end
537
- end
538
- elsif @options.settings.is_a?(Hash)
539
- settings_hash = @options.settings.dup
540
- end
541
- end
542
-
543
- if !settings_is_path && @options.sandbox
544
- sandbox_hash = @options.sandbox.is_a?(SandboxSettings) ? @options.sandbox.to_h : @options.sandbox
545
- settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
546
- end
547
-
548
- cmd.concat(['--settings', JSON.generate(settings_hash)]) if !settings_is_path && !settings_hash.empty?
549
- end
550
-
551
- def build_tools_args(cmd)
552
- return if @options.tools.nil?
553
-
554
- if @options.tools.is_a?(Array)
555
- tools_value = @options.tools.empty? ? '' : @options.tools.join(',')
556
- cmd.concat(['--tools', tools_value])
557
- elsif @options.tools.is_a?(ToolsPreset)
558
- cmd.concat(['--tools', 'default'])
559
- elsif @options.tools.is_a?(Hash)
560
- if (@options.tools[:type] || @options.tools['type']) == 'preset'
561
- cmd.concat(['--tools', 'default'])
562
- else
563
- cmd.concat(['--tools', JSON.generate(@options.tools)])
564
- end
565
- end
566
- end
567
-
568
- def build_output_format_args(cmd)
569
- return unless @options.output_format
570
-
571
- schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == 'json_schema'
572
- @options.output_format[:schema]
573
- elsif @options.output_format.is_a?(Hash) && @options.output_format['type'] == 'json_schema'
574
- @options.output_format['schema']
575
- else
576
- @options.output_format
577
- end
578
- schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
579
- cmd.concat(['--json-schema', schema_json])
580
- end
581
-
582
- def build_mcp_servers_args(cmd)
583
- return unless @options.mcp_servers && !@options.mcp_servers.empty?
584
-
585
- if @options.mcp_servers.is_a?(Hash)
586
- servers_for_cli = {}
587
- @options.mcp_servers.each do |name, config|
588
- servers_for_cli[name] = if config.is_a?(Hash) && config[:type] == 'sdk'
589
- config.reject { |k, _| k == :instance }
590
- else
591
- config
592
- end
593
- end
594
- cmd.concat(['--mcp-config', JSON.generate({ mcpServers: servers_for_cli })]) unless servers_for_cli.empty?
595
- else
596
- cmd.concat(['--mcp-config', @options.mcp_servers.to_s])
597
- end
598
- end
599
-
600
- def build_plugins_args(cmd)
601
- return unless @options.plugins && !@options.plugins.empty?
602
-
603
- @options.plugins.each do |plugin|
604
- plugin_config = plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
605
- plugin_type = plugin_config[:type] || plugin_config['type']
606
- plugin_path = plugin_config[:path] || plugin_config['path']
607
-
608
- raise ArgumentError, "Unsupported plugin type: #{plugin_type.inspect}" unless %w[local plugin].include?(plugin_type)
609
- next unless plugin_path
610
-
611
- cmd.concat(['--plugin-dir', plugin_path])
612
- end
613
- end
614
-
615
- def load_settings_file(path)
616
- raise CLIConnectionError, "Settings file not found: #{path}" unless File.file?(path)
617
-
618
- JSON.parse(File.read(path))
619
- end
620
-
621
- def build_thinking_args(cmd)
622
- if @options.thinking
623
- case @options.thinking
624
- when ThinkingConfigAdaptive
625
- cmd.concat(['--thinking', 'adaptive'])
626
- when ThinkingConfigEnabled
627
- cmd.concat(['--max-thinking-tokens', @options.thinking.budget_tokens.to_s])
628
- when ThinkingConfigDisabled
629
- cmd.concat(['--thinking', 'disabled'])
630
- end
631
- elsif @options.max_thinking_tokens
632
- cmd.concat(['--max-thinking-tokens', @options.max_thinking_tokens.to_s])
633
- end
634
- end
635
398
  end
636
399
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.16.1'
4
+ VERSION = '0.16.2'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude-agent-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.16.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Community Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-04-22 00:00:00.000000000 Z
10
+ date: 2026-04-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async
@@ -104,6 +104,7 @@ files:
104
104
  - LICENSE
105
105
  - README.md
106
106
  - lib/claude_agent_sdk.rb
107
+ - lib/claude_agent_sdk/command_builder.rb
107
108
  - lib/claude_agent_sdk/configuration.rb
108
109
  - lib/claude_agent_sdk/errors.rb
109
110
  - lib/claude_agent_sdk/fiber_boundary.rb