claude_swarm 1.0.10 → 1.0.11

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGELOG.md → CHANGELOG.claude-swarm.md} +3 -0
  3. data/CLAUDE.md +0 -1
  4. data/decisions/2025-11-22-001-global-agent-registry.md +172 -0
  5. data/docs/v2/CHANGELOG.swarm_cli.md +12 -0
  6. data/docs/v2/CHANGELOG.swarm_memory.md +139 -0
  7. data/docs/v2/CHANGELOG.swarm_sdk.md +249 -1
  8. data/docs/v2/README.md +15 -5
  9. data/docs/v2/guides/complete-tutorial.md +93 -7
  10. data/docs/v2/guides/getting-started.md +3 -1
  11. data/docs/v2/guides/memory-adapters.md +41 -0
  12. data/docs/v2/guides/{migrating-to-2.3.md → migrating-to-2.x.md} +213 -8
  13. data/docs/v2/guides/plugins.md +52 -5
  14. data/docs/v2/guides/rails-integration.md +6 -0
  15. data/docs/v2/guides/swarm-memory.md +2 -13
  16. data/docs/v2/reference/cli.md +0 -1
  17. data/docs/v2/reference/configuration_reference.md +300 -0
  18. data/docs/v2/reference/event_payload_structures.md +26 -4
  19. data/docs/v2/reference/ruby-dsl.md +457 -4
  20. data/docs/v2/reference/swarm_memory_technical_details.md +7 -29
  21. data/docs/v2/reference/yaml.md +2 -2
  22. data/lib/claude_swarm/mcp_generator.rb +1 -1
  23. data/lib/claude_swarm/orchestrator.rb +8 -1
  24. data/lib/claude_swarm/version.rb +1 -1
  25. data/lib/swarm_cli/version.rb +1 -1
  26. data/lib/swarm_memory/core/semantic_index.rb +10 -2
  27. data/lib/swarm_memory/core/storage.rb +7 -2
  28. data/lib/swarm_memory/dsl/memory_config.rb +37 -0
  29. data/lib/swarm_memory/integration/sdk_plugin.rb +120 -27
  30. data/lib/swarm_memory/optimization/defragmenter.rb +1 -1
  31. data/lib/swarm_memory/prompts/memory_researcher.md.erb +0 -1
  32. data/lib/swarm_memory/tools/load_skill.rb +0 -1
  33. data/lib/swarm_memory/tools/memory_edit.rb +2 -1
  34. data/lib/swarm_memory/tools/memory_read.rb +1 -1
  35. data/lib/swarm_memory/version.rb +1 -1
  36. data/lib/swarm_memory.rb +7 -5
  37. data/lib/swarm_sdk/agent/chat.rb +1 -1
  38. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +4 -0
  39. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +1 -1
  40. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +38 -4
  41. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +2 -2
  42. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +3 -5
  43. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +48 -0
  44. data/lib/swarm_sdk/agent/context.rb +1 -2
  45. data/lib/swarm_sdk/agent/definition.rb +3 -3
  46. data/lib/swarm_sdk/agent/system_prompt_builder.rb +1 -1
  47. data/lib/swarm_sdk/agent_registry.rb +146 -0
  48. data/lib/swarm_sdk/builders/base_builder.rb +91 -12
  49. data/lib/swarm_sdk/config.rb +302 -0
  50. data/lib/swarm_sdk/configuration/parser.rb +22 -2
  51. data/lib/swarm_sdk/configuration.rb +13 -4
  52. data/lib/swarm_sdk/context_compactor/token_counter.rb +2 -6
  53. data/lib/swarm_sdk/custom_tool_registry.rb +226 -0
  54. data/lib/swarm_sdk/hooks/adapter.rb +3 -3
  55. data/lib/swarm_sdk/hooks/shell_executor.rb +4 -3
  56. data/lib/swarm_sdk/models.json +4333 -1
  57. data/lib/swarm_sdk/models.rb +43 -2
  58. data/lib/swarm_sdk/plugin.rb +2 -2
  59. data/lib/swarm_sdk/result.rb +52 -0
  60. data/lib/swarm_sdk/swarm/agent_initializer.rb +1 -1
  61. data/lib/swarm_sdk/swarm/hook_triggers.rb +1 -0
  62. data/lib/swarm_sdk/swarm/logging_callbacks.rb +1 -0
  63. data/lib/swarm_sdk/swarm/tool_configurator.rb +18 -4
  64. data/lib/swarm_sdk/swarm.rb +76 -13
  65. data/lib/swarm_sdk/tools/bash.rb +7 -9
  66. data/lib/swarm_sdk/tools/glob.rb +5 -5
  67. data/lib/swarm_sdk/tools/read.rb +8 -8
  68. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +4 -3
  69. data/lib/swarm_sdk/tools/web_fetch.rb +20 -18
  70. data/lib/swarm_sdk/version.rb +1 -1
  71. data/lib/swarm_sdk/workflow/builder.rb +49 -0
  72. data/lib/swarm_sdk/workflow/node_builder.rb +4 -2
  73. data/lib/swarm_sdk/workflow/transformer_executor.rb +4 -3
  74. data/lib/swarm_sdk.rb +261 -105
  75. data/swarm_cli.gemspec +1 -1
  76. data/swarm_memory.gemspec +8 -3
  77. data/swarm_sdk.gemspec +4 -4
  78. data/team_full.yml +104 -300
  79. metadata +9 -5
  80. data/lib/swarm_memory/tools/memory_multi_edit.rb +0 -281
  81. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -119,6 +119,9 @@ module SwarmSDK
119
119
  #
120
120
  # @return [Workflow] Configured workflow instance
121
121
  def build_workflow
122
+ # Resolve any missing agents from global registry before building definitions
123
+ resolve_missing_agents_from_registry
124
+
122
125
  # Build agent definitions
123
126
  agent_definitions = build_agent_definitions
124
127
 
@@ -138,6 +141,52 @@ module SwarmSDK
138
141
 
139
142
  workflow
140
143
  end
144
+
145
+ # Resolve agents referenced in nodes that aren't defined at workflow level
146
+ #
147
+ # For each agent referenced in a node but not defined with `agent :name do ... end`,
148
+ # checks the global AgentRegistry and loads the agent if found.
149
+ #
150
+ # This allows workflows to reference globally registered agents without
151
+ # explicitly re-declaring them at the workflow level.
152
+ #
153
+ # @return [void]
154
+ # @raise [ConfigurationError] If agent not found in workflow or registry
155
+ def resolve_missing_agents_from_registry
156
+ # Collect all agents referenced in nodes
157
+ referenced_agents = collect_referenced_agents
158
+
159
+ # Find agents that aren't defined at workflow level
160
+ defined_agents = @agents.keys
161
+ missing_agents = referenced_agents - defined_agents
162
+
163
+ # Try to resolve each missing agent from the global registry
164
+ missing_agents.each do |agent_name|
165
+ if AgentRegistry.registered?(agent_name)
166
+ # Load from registry (uses same method as BaseBuilder)
167
+ load_agent_from_registry(agent_name)
168
+ else
169
+ raise ConfigurationError,
170
+ "Agent '#{agent_name}' referenced in node but not found. " \
171
+ "Either define at workflow level with `agent :#{agent_name} do ... end` " \
172
+ "or register globally with `SwarmSDK.agent :#{agent_name} do ... end`"
173
+ end
174
+ end
175
+ end
176
+
177
+ # Collect all agent names referenced across all nodes
178
+ #
179
+ # Includes both directly referenced agents and delegation targets.
180
+ #
181
+ # @return [Array<Symbol>] Unique agent names referenced in nodes
182
+ def collect_referenced_agents
183
+ @nodes.values.flat_map do |node_builder|
184
+ # Collect both direct agents and their delegation targets
185
+ node_builder.agent_configs.flat_map do |config|
186
+ [config[:agent]] + Array(config[:delegates_to])
187
+ end
188
+ end.uniq
189
+ end
141
190
  end
142
191
  end
143
192
  end
@@ -214,7 +214,8 @@ module SwarmSDK
214
214
  #
215
215
  # @example
216
216
  # input_command("scripts/validate.sh", timeout: 30)
217
- def input_command(command, timeout: TransformerExecutor::DEFAULT_TIMEOUT)
217
+ def input_command(command, timeout: nil)
218
+ timeout ||= SwarmSDK.config.transformer_command_timeout
218
219
  @input_transformer_command = { command: command, timeout: timeout }
219
220
  end
220
221
 
@@ -288,7 +289,8 @@ module SwarmSDK
288
289
  #
289
290
  # @example
290
291
  # output_command("scripts/format.sh", timeout: 30)
291
- def output_command(command, timeout: TransformerExecutor::DEFAULT_TIMEOUT)
292
+ def output_command(command, timeout: nil)
293
+ timeout ||= SwarmSDK.config.transformer_command_timeout
292
294
  @output_transformer_command = { command: command, timeout: timeout }
293
295
  end
294
296
 
@@ -90,8 +90,7 @@ module SwarmSDK
90
90
  # echo "$CONTENT"
91
91
  # exit 0
92
92
  class TransformerExecutor
93
- # Backward compatibility alias - use Defaults module for new code
94
- DEFAULT_TIMEOUT = Defaults::Timeouts::TRANSFORMER_COMMAND_SECONDS
93
+ # NOTE: Timeout now accessed via SwarmSDK.config.transformer_command_timeout
95
94
 
96
95
  # Result object for transformer execution
97
96
  TransformerResult = Struct.new(:success, :content, :skip_execution, :halt, :error_message, keyword_init: true) do
@@ -114,7 +113,9 @@ module SwarmSDK
114
113
  # @param fallback_content [String] Content to use if skip (exit 1)
115
114
  # @param timeout [Integer] Timeout in seconds (default: 60)
116
115
  # @return [TransformerResult] Result with transformed content or skip/halt flags
117
- def execute(command:, context:, event:, node_name:, fallback_content:, timeout: DEFAULT_TIMEOUT)
116
+ def execute(command:, context:, event:, node_name:, fallback_content:, timeout: nil)
117
+ timeout ||= SwarmSDK.config.transformer_command_timeout
118
+
118
119
  # Build JSON input for transformer
119
120
  input_json = build_transformer_input(context, event, node_name)
120
121
 
data/lib/swarm_sdk.rb CHANGED
@@ -63,8 +63,247 @@ module SwarmSDK
63
63
  class StateError < Error; end
64
64
 
65
65
  class << self
66
- # Settings for SwarmSDK (global configuration)
67
- attr_accessor :settings
66
+ # Get the global configuration instance
67
+ #
68
+ # @return [Config] The singleton Config instance
69
+ def config
70
+ Config.instance
71
+ end
72
+
73
+ # Configure SwarmSDK global settings
74
+ #
75
+ # @yield [Config] The configuration instance
76
+ # @return [Config] The configuration instance
77
+ #
78
+ # @example
79
+ # SwarmSDK.configure do |config|
80
+ # config.openai_api_key = "sk-..."
81
+ # config.default_model = "claude-sonnet-4"
82
+ # end
83
+ def configure
84
+ yield(config) if block_given?
85
+ config
86
+ end
87
+
88
+ # Reset configuration to defaults
89
+ #
90
+ # Clears all configuration including explicit values and cached ENV values.
91
+ # Use in tests to ensure clean state.
92
+ #
93
+ # @return [void]
94
+ def reset_config!
95
+ Config.reset!
96
+ end
97
+
98
+ # Register a global agent definition
99
+ #
100
+ # Declares an agent configuration that can be referenced by name in any
101
+ # swarm definition. This allows defining agents in separate files and
102
+ # composing them into swarms without duplication.
103
+ #
104
+ # The registered block uses the Agent::Builder DSL and is executed when
105
+ # the agent is referenced in a swarm definition.
106
+ #
107
+ # @param name [Symbol, String] Agent name (will be symbolized)
108
+ # @yield Agent configuration block using Agent::Builder DSL
109
+ # @return [void]
110
+ # @raise [ArgumentError] If no block is provided
111
+ #
112
+ # @example Register agent in separate file
113
+ # # agents/backend.rb
114
+ # SwarmSDK.agent :backend do
115
+ # model "claude-sonnet-4"
116
+ # description "Backend API developer"
117
+ # system_prompt "You build REST APIs"
118
+ # tools :Read, :Edit, :Bash
119
+ # delegates_to :database
120
+ # end
121
+ #
122
+ # @example Reference in swarm definition
123
+ # # swarm.rb
124
+ # require_relative "agents/backend"
125
+ #
126
+ # SwarmSDK.build do
127
+ # name "Dev Team"
128
+ # lead :backend
129
+ #
130
+ # agent :backend # Pulls from registry
131
+ # end
132
+ #
133
+ # @example Extend registered agent with overrides
134
+ # SwarmSDK.build do
135
+ # name "Extended Team"
136
+ # lead :backend
137
+ #
138
+ # agent :backend do
139
+ # # Registry config applied first, then this block
140
+ # tools :CustomTool # Adds to existing tools
141
+ # delegates_to :cache # Adds delegation target
142
+ # end
143
+ # end
144
+ #
145
+ # @see AgentRegistry
146
+ def agent(name, &block)
147
+ AgentRegistry.register(name, &block)
148
+ end
149
+
150
+ # Clear the global agent registry
151
+ #
152
+ # Removes all registered agent definitions. Primarily useful for testing
153
+ # to ensure clean state between tests.
154
+ #
155
+ # @return [void]
156
+ #
157
+ # @example In test teardown
158
+ # def teardown
159
+ # SwarmSDK.clear_agent_registry!
160
+ # end
161
+ def clear_agent_registry!
162
+ AgentRegistry.clear
163
+ end
164
+
165
+ # Register a custom tool for use in swarms
166
+ #
167
+ # Provides a simple way to add tools without creating a full plugin.
168
+ # Tools can be registered with an explicit name or the name can be
169
+ # inferred from the class name.
170
+ #
171
+ # Custom tools are available to any agent that includes them in their
172
+ # tools configuration, just like built-in tools.
173
+ #
174
+ # @overload register_tool(tool_class)
175
+ # Register a tool with name inferred from class name
176
+ # @param tool_class [Class] Tool class (must inherit from RubyLLM::Tool)
177
+ # @return [Symbol] The registered tool name
178
+ #
179
+ # @overload register_tool(name, tool_class)
180
+ # Register a tool with explicit name
181
+ # @param name [Symbol, String] Tool name
182
+ # @param tool_class [Class] Tool class (must inherit from RubyLLM::Tool)
183
+ # @return [Symbol] The registered tool name
184
+ #
185
+ # @raise [ArgumentError] If tool_class doesn't inherit from RubyLLM::Tool
186
+ # @raise [ArgumentError] If a tool with the same name is already registered
187
+ # @raise [ArgumentError] If the name conflicts with a built-in or plugin tool
188
+ #
189
+ # @example Register with inferred name
190
+ # class WeatherTool < RubyLLM::Tool
191
+ # description "Get weather for a city"
192
+ # param :city, type: "string", required: true
193
+ #
194
+ # def execute(city:)
195
+ # "Weather in #{city}: Sunny, 72°F"
196
+ # end
197
+ # end
198
+ #
199
+ # SwarmSDK.register_tool(WeatherTool) # Registers as :Weather
200
+ #
201
+ # @example Register with explicit name
202
+ # SwarmSDK.register_tool(:GetWeather, WeatherTool)
203
+ #
204
+ # @example Tool with agent context
205
+ # class ContextAwareTool < RubyLLM::Tool
206
+ # # Declare what context the tool needs
207
+ # def self.creation_requirements
208
+ # [:agent_name, :directory]
209
+ # end
210
+ #
211
+ # def initialize(agent_name:, directory:)
212
+ # super()
213
+ # @agent_name = agent_name
214
+ # @directory = directory
215
+ # end
216
+ #
217
+ # description "Shows agent context"
218
+ # def execute
219
+ # "Agent: #{@agent_name} in #{@directory}"
220
+ # end
221
+ # end
222
+ #
223
+ # SwarmSDK.register_tool(ContextAwareTool)
224
+ #
225
+ # @example Use registered tool in a swarm
226
+ # SwarmSDK.register_tool(WeatherTool)
227
+ #
228
+ # swarm = SwarmSDK.build do
229
+ # name "Weather Assistant"
230
+ # lead :assistant
231
+ #
232
+ # agent :assistant do
233
+ # model "claude-sonnet-4"
234
+ # description "Weather helper"
235
+ # tools :Weather, :Read # Custom + built-in tools
236
+ # end
237
+ # end
238
+ #
239
+ # @see CustomToolRegistry For the underlying registry
240
+ # @see Plugin For complex tool systems requiring storage or lifecycle hooks
241
+ def register_tool(name_or_class, tool_class = nil)
242
+ if tool_class.nil?
243
+ # Single argument: infer name from class
244
+ tool_class = name_or_class
245
+ name = CustomToolRegistry.infer_name(tool_class)
246
+ else
247
+ # Two arguments: explicit name
248
+ name = name_or_class.to_sym
249
+ end
250
+
251
+ CustomToolRegistry.register(name, tool_class)
252
+ name
253
+ end
254
+
255
+ # Check if a custom tool is registered
256
+ #
257
+ # @param name [Symbol, String] Tool name
258
+ # @return [Boolean] true if the tool is registered
259
+ #
260
+ # @example
261
+ # SwarmSDK.register_tool(WeatherTool)
262
+ # SwarmSDK.custom_tool_registered?(:Weather) #=> true
263
+ # SwarmSDK.custom_tool_registered?(:Unknown) #=> false
264
+ def custom_tool_registered?(name)
265
+ CustomToolRegistry.registered?(name)
266
+ end
267
+
268
+ # Get all registered custom tool names
269
+ #
270
+ # @return [Array<Symbol>] List of registered custom tool names
271
+ #
272
+ # @example
273
+ # SwarmSDK.register_tool(WeatherTool)
274
+ # SwarmSDK.register_tool(StockTool)
275
+ # SwarmSDK.custom_tools #=> [:Weather, :Stock]
276
+ def custom_tools
277
+ CustomToolRegistry.tool_names
278
+ end
279
+
280
+ # Unregister a custom tool
281
+ #
282
+ # @param name [Symbol, String] Tool name to unregister
283
+ # @return [Class, nil] The unregistered tool class, or nil if not found
284
+ #
285
+ # @example
286
+ # SwarmSDK.register_tool(WeatherTool)
287
+ # SwarmSDK.unregister_tool(:Weather)
288
+ # SwarmSDK.custom_tool_registered?(:Weather) #=> false
289
+ def unregister_tool(name)
290
+ CustomToolRegistry.unregister(name)
291
+ end
292
+
293
+ # Clear all registered custom tools
294
+ #
295
+ # Removes all custom tool registrations. Primarily useful for testing
296
+ # to ensure clean state between tests.
297
+ #
298
+ # @return [void]
299
+ #
300
+ # @example In test teardown
301
+ # def teardown
302
+ # SwarmSDK.clear_custom_tools!
303
+ # end
304
+ def clear_custom_tools!
305
+ CustomToolRegistry.clear
306
+ end
68
307
 
69
308
  # Main entry point for DSL - builds simple multi-agent swarms
70
309
  #
@@ -179,6 +418,11 @@ module SwarmSDK
179
418
  #
180
419
  # @param yaml_content [String] YAML configuration content
181
420
  # @param base_dir [String, Pathname] Base directory for resolving agent file paths (default: Dir.pwd)
421
+ # @param allow_filesystem_tools [Boolean, nil] Whether to allow filesystem tools (nil uses global setting)
422
+ # @param env_interpolation [Boolean, nil] Whether to interpolate environment variables.
423
+ # When nil, uses the global SwarmSDK.config.env_interpolation setting.
424
+ # When true, interpolates ${VAR} and ${VAR:=default} patterns.
425
+ # When false, skips interpolation entirely.
182
426
  # @return [Swarm, Workflow] Configured swarm or workflow instance
183
427
  # @raise [ConfigurationError] If YAML is invalid or configuration is incorrect
184
428
  #
@@ -201,8 +445,11 @@ module SwarmSDK
201
445
  # @example Load with default base_dir (Dir.pwd)
202
446
  # yaml = File.read("config.yml")
203
447
  # swarm = SwarmSDK.load(yaml) # base_dir defaults to Dir.pwd
204
- def load(yaml_content, base_dir: Dir.pwd, allow_filesystem_tools: nil)
205
- config = Configuration.new(yaml_content, base_dir: base_dir)
448
+ #
449
+ # @example Load without environment variable interpolation
450
+ # swarm = SwarmSDK.load(yaml, env_interpolation: false)
451
+ def load(yaml_content, base_dir: Dir.pwd, allow_filesystem_tools: nil, env_interpolation: nil)
452
+ config = Configuration.new(yaml_content, base_dir: base_dir, env_interpolation: env_interpolation)
206
453
  config.load_and_validate
207
454
  swarm = config.to_swarm(allow_filesystem_tools: allow_filesystem_tools)
208
455
 
@@ -224,6 +471,11 @@ module SwarmSDK
224
471
  # loading swarms from configuration files.
225
472
  #
226
473
  # @param path [String, Pathname] Path to YAML configuration file
474
+ # @param allow_filesystem_tools [Boolean, nil] Whether to allow filesystem tools (nil uses global setting)
475
+ # @param env_interpolation [Boolean, nil] Whether to interpolate environment variables.
476
+ # When nil, uses the global SwarmSDK.config.env_interpolation setting.
477
+ # When true, interpolates ${VAR} and ${VAR:=default} patterns.
478
+ # When false, skips interpolation entirely.
227
479
  # @return [Swarm, Workflow] Configured swarm or workflow instance
228
480
  # @raise [ConfigurationError] If file not found or configuration invalid
229
481
  #
@@ -233,8 +485,11 @@ module SwarmSDK
233
485
  #
234
486
  # @example With absolute path
235
487
  # swarm = SwarmSDK.load_file("/absolute/path/config.yml")
236
- def load_file(path, allow_filesystem_tools: nil)
237
- config = Configuration.load_file(path)
488
+ #
489
+ # @example Load without environment variable interpolation
490
+ # swarm = SwarmSDK.load_file("config.yml", env_interpolation: false)
491
+ def load_file(path, allow_filesystem_tools: nil, env_interpolation: nil)
492
+ config = Configuration.load_file(path, env_interpolation: env_interpolation)
238
493
  swarm = config.to_swarm(allow_filesystem_tools: allow_filesystem_tools)
239
494
 
240
495
  # Apply hooks if any are configured (YAML-only feature)
@@ -248,21 +503,6 @@ module SwarmSDK
248
503
  swarm
249
504
  end
250
505
 
251
- # Configure SwarmSDK global settings
252
- def configure
253
- self.settings ||= Settings.new
254
- yield(settings)
255
- end
256
-
257
- # Reset settings to defaults
258
- def reset_settings!
259
- self.settings = Settings.new
260
- end
261
-
262
- # Alias for backward compatibility
263
- alias_method :configuration, :settings
264
- alias_method :reset_configuration!, :reset_settings!
265
-
266
506
  private
267
507
 
268
508
  # Check if hooks are configured in the configuration
@@ -437,88 +677,4 @@ module SwarmSDK
437
677
  error_hash.compact
438
678
  end
439
679
  end
440
-
441
- # Settings class for SwarmSDK global settings (not to be confused with Configuration for YAML loading)
442
- class Settings
443
- # WebFetch tool LLM processing configuration
444
- attr_accessor :webfetch_provider, :webfetch_model, :webfetch_base_url, :webfetch_max_tokens
445
-
446
- # Filesystem tools control
447
- attr_accessor :allow_filesystem_tools
448
-
449
- def initialize
450
- @webfetch_provider = nil
451
- @webfetch_model = nil
452
- @webfetch_base_url = nil
453
- @webfetch_max_tokens = 4096
454
- @allow_filesystem_tools = parse_env_bool("SWARM_SDK_ALLOW_FILESYSTEM_TOOLS", default: true)
455
- end
456
-
457
- # Check if WebFetch LLM processing is enabled
458
- def webfetch_llm_enabled?
459
- !@webfetch_provider.nil? && !@webfetch_model.nil?
460
- end
461
-
462
- private
463
-
464
- def parse_env_bool(key, default:)
465
- return default unless ENV.key?(key)
466
-
467
- value = ENV[key].to_s.downcase
468
- return true if ["true", "yes", "1", "on", "enabled"].include?(value)
469
- return false if ["false", "no", "0", "off", "disabled"].include?(value)
470
-
471
- default
472
- end
473
- end
474
-
475
- # Initialize default settings
476
- self.settings = Settings.new
477
- end
478
-
479
- # Automatically configure RubyLLM from environment variables
480
- # This makes SwarmSDK "just work" when users set standard ENV variables
481
- RubyLLM.configure do |config|
482
- # Only set if config not already set (||= handles nil ENV values gracefully)
483
-
484
- # OpenAI
485
- config.openai_api_key ||= ENV["OPENAI_API_KEY"]
486
- config.openai_api_base ||= ENV["OPENAI_API_BASE"]
487
- config.openai_organization_id ||= ENV["OPENAI_ORG_ID"]
488
- config.openai_project_id ||= ENV["OPENAI_PROJECT_ID"]
489
-
490
- # Anthropic
491
- config.anthropic_api_key ||= ENV["ANTHROPIC_API_KEY"]
492
-
493
- # Google Gemini
494
- config.gemini_api_key ||= ENV["GEMINI_API_KEY"]
495
-
496
- # Google Vertex AI (note: vertexai, not vertex_ai)
497
- config.vertexai_project_id ||= ENV["GOOGLE_CLOUD_PROJECT"] || ENV["VERTEXAI_PROJECT_ID"]
498
- config.vertexai_location ||= ENV["GOOGLE_CLOUD_LOCATION"] || ENV["VERTEXAI_LOCATION"]
499
-
500
- # DeepSeek
501
- config.deepseek_api_key ||= ENV["DEEPSEEK_API_KEY"]
502
-
503
- # Mistral
504
- config.mistral_api_key ||= ENV["MISTRAL_API_KEY"]
505
-
506
- # Perplexity
507
- config.perplexity_api_key ||= ENV["PERPLEXITY_API_KEY"]
508
-
509
- # OpenRouter
510
- config.openrouter_api_key ||= ENV["OPENROUTER_API_KEY"]
511
-
512
- # AWS Bedrock
513
- config.bedrock_api_key ||= ENV["AWS_ACCESS_KEY_ID"]
514
- config.bedrock_secret_key ||= ENV["AWS_SECRET_ACCESS_KEY"]
515
- config.bedrock_region ||= ENV["AWS_REGION"]
516
- config.bedrock_session_token ||= ENV["AWS_SESSION_TOKEN"]
517
-
518
- # Ollama (local)
519
- config.ollama_api_base ||= ENV["OLLAMA_API_BASE"]
520
-
521
- # GPUStack (local)
522
- config.gpustack_api_base ||= ENV["GPUSTACK_API_BASE"]
523
- config.gpustack_api_key ||= ENV["GPUSTACK_API_KEY"]
524
680
  end
data/swarm_cli.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
 
37
37
  spec.add_dependency("fast-mcp", "~> 1.6")
38
38
  spec.add_dependency("pastel")
39
- spec.add_dependency("swarm_sdk", "~> 2.2")
39
+ spec.add_dependency("swarm_sdk", "~> 2.5.1")
40
40
  spec.add_dependency("tty-box")
41
41
  spec.add_dependency("tty-cursor")
42
42
  spec.add_dependency("tty-link")
data/swarm_memory.gemspec CHANGED
@@ -14,7 +14,12 @@ Gem::Specification.new do |spec|
14
14
  spec.metadata["source_code_uri"] = "https://github.com/parruda/claude-swarm"
15
15
  spec.metadata["changelog_uri"] = "https://github.com/parruda/claude-swarm/blob/main/docs/v2/CHANGELOG.swarm_memory.md"
16
16
 
17
- spec.files = Dir["lib/**/*", "LICENSE"]
17
+ spec.files = IO.popen(["git", "ls-files", "-z"], chdir: __dir__, err: IO::NULL) do |ls|
18
+ ls.readlines("\x0", chomp: true).select do |f|
19
+ (f == "lib/swarm_memory.rb") ||
20
+ f.match?(%r{\Alib/swarm_memory/}) || (f == "LICENSE")
21
+ end
22
+ end
18
23
  spec.require_paths = ["lib"]
19
24
  spec.required_ruby_version = ">= 3.2.0"
20
25
 
@@ -22,7 +27,7 @@ Gem::Specification.new do |spec|
22
27
  spec.add_dependency("async", "~> 2.0")
23
28
  spec.add_dependency("informers", "~> 1.2.1")
24
29
  spec.add_dependency("rice", "~> 4.6.0")
25
- spec.add_dependency("ruby_llm_swarm", "~> 1.9.2")
26
- spec.add_dependency("swarm_sdk", "~> 2.2")
30
+ spec.add_dependency("ruby_llm_swarm", "~> 1.9.5")
31
+ spec.add_dependency("swarm_sdk", "~> 2.5.1")
27
32
  spec.add_dependency("zeitwerk", "~> 2.6")
28
33
  end
data/swarm_sdk.gemspec CHANGED
@@ -23,11 +23,10 @@ Gem::Specification.new do |spec|
23
23
  spec.metadata["source_code_uri"] = "https://github.com/parruda/claude-swarm"
24
24
  spec.metadata["changelog_uri"] = "https://github.com/parruda/claude-swarm/blob/main/docs/v2/CHANGELOG.swarm_sdk.md"
25
25
 
26
- File.basename(__FILE__)
27
26
  spec.files = IO.popen(["git", "ls-files", "-z"], chdir: __dir__, err: IO::NULL) do |ls|
28
27
  ls.readlines("\x0", chomp: true).select do |f|
29
28
  (f == "lib/swarm_sdk.rb") ||
30
- f.match?(%r{\Alib/swarm_sdk/})
29
+ f.match?(%r{\Alib/swarm_sdk/}) || (f == "LICENSE")
31
30
  end
32
31
  end
33
32
  # spec.bindir = "exe"
@@ -37,7 +36,8 @@ Gem::Specification.new do |spec|
37
36
  spec.add_dependency("async", "~> 2.0")
38
37
  spec.add_dependency("async-http-faraday", "~> 0.22")
39
38
  spec.add_dependency("faraday-follow_redirects", "~> 0.4")
40
- spec.add_dependency("ruby_llm-mcp", "~> 0.8")
41
- spec.add_dependency("ruby_llm_swarm", "~> 1.9.2")
39
+ spec.add_dependency("openssl", "~> 3.3.2")
40
+ spec.add_dependency("ruby_llm_swarm", "~> 1.9.5")
41
+ spec.add_dependency("ruby_llm_swarm-mcp", "~> 0.8.1")
42
42
  spec.add_dependency("zeitwerk", "~> 2.6")
43
43
  end