claude_swarm 0.3.3 → 0.3.5
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 +4 -4
- data/CHANGELOG.md +23 -0
- data/CLAUDE.md +10 -0
- data/README.md +2 -1
- data/lib/claude_swarm/claude_code_executor.rb +227 -83
- data/lib/claude_swarm/claude_mcp_server.rb +1 -1
- data/lib/claude_swarm/orchestrator.rb +5 -32
- data/lib/claude_swarm/system_utils.rb +6 -2
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm.rb +1 -0
- data/team.yml +75 -3
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d72ab4034667cbfd6719b79c3a3aeff7ae93fdf7adc850b910edfcda8c4fe92
|
4
|
+
data.tar.gz: 48f3b2151b39a4aad0bc09ac089a6eb98911b82e4e7f1eb029e7541462a2efc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f23d4f5086dac9ee9e112476d7e0aa1f4e39af367ef542829665989a17610e382f21b9c396f2118aab04cca56758286580a20a57be6ced893beddea49137890
|
7
|
+
data.tar.gz: c487d6321ae3f07e5310eb94e08ab1a45bc98628df6f82ad7cf5e74bb323358189310efeeb29b31c456ed61b0a61934c92aca0535d93947e8598a4192f97a6f6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## [0.3.5]
|
2
|
+
|
3
|
+
### Changed
|
4
|
+
- Relaxed dependency version constraints for better flexibility
|
5
|
+
- `claude-code-sdk-ruby`: from `~> 0.1.0` to `~> 0.1`
|
6
|
+
- `fast-mcp-annotations`: from `~> 1.5.3` to `~> 1.5`
|
7
|
+
|
8
|
+
## [0.3.4]
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
- Add tests for thinking_budget feature by @parruda in https://github.com/parruda/claude-swarm/pull/85
|
12
|
+
- Improve process group handling and signal management by @ericproulx in https://github.com/parruda/claude-swarm/pull/87
|
13
|
+
- Migrated from CLI to SDK-based execution**: Claude Swarm now uses the `claude-code-sdk-ruby` gem instead of executing Claude Code via CLI
|
14
|
+
- Removed CLI-based `ClaudeCodeExecutor` implementation that used `Open3.popen3`
|
15
|
+
- All Claude Code execution now uses the SDK for improved reliability and performance
|
16
|
+
- Session management and logging functionality remains unchanged
|
17
|
+
- MCP configuration parsing updated to convert JSON format to SDK hash format
|
18
|
+
- Supports all MCP server types: stdio, sse, and http
|
19
|
+
- This change is transparent to users but may affect custom integrations that relied on CLI-specific behavior
|
20
|
+
|
21
|
+
### Added
|
22
|
+
- **SDK dependency**: Added `claude-code-sdk-ruby` (~> 0.1.0)
|
23
|
+
|
1
24
|
## [0.3.3]
|
2
25
|
|
3
26
|
### Fixed
|
data/CLAUDE.md
CHANGED
@@ -93,6 +93,16 @@ instances:
|
|
93
93
|
- Existing worktrees with the same name are reused
|
94
94
|
- The `claude-swarm clean` command removes orphaned worktrees
|
95
95
|
|
96
|
+
## Claude Code SDK Integration
|
97
|
+
|
98
|
+
Claude Swarm uses the Claude Code SDK (`claude-code-sdk-ruby`) for all Claude instances. This provides:
|
99
|
+
- Better performance and reliability
|
100
|
+
- Structured message handling
|
101
|
+
- Improved error recovery
|
102
|
+
- Direct MCP server configuration support (stdio, sse, http)
|
103
|
+
|
104
|
+
The SDK executor handles all three MCP server types and properly converts MCP JSON configurations to SDK format.
|
105
|
+
|
96
106
|
## Architecture
|
97
107
|
|
98
108
|
The gem is fully implemented with the following components:
|
data/README.md
CHANGED
@@ -978,6 +978,7 @@ Check the session directory `~/.claude-swarm/sessions/{project}/{session-id}/` f
|
|
978
978
|
- `{instance}.mcp.json`: MCP configuration for each instance
|
979
979
|
- All files for a session are kept together for easy review
|
980
980
|
|
981
|
+
|
981
982
|
## Architecture
|
982
983
|
|
983
984
|
Claude Swarm consists of these core components:
|
@@ -986,7 +987,7 @@ Claude Swarm consists of these core components:
|
|
986
987
|
- **ClaudeSwarm::Configuration** (`configuration.rb`): YAML parser and validator with path expansion
|
987
988
|
- **ClaudeSwarm::McpGenerator** (`mcp_generator.rb`): Generates MCP JSON configs for each instance
|
988
989
|
- **ClaudeSwarm::Orchestrator** (`orchestrator.rb`): Launches the main Claude instance with shared session management
|
989
|
-
- **ClaudeSwarm::ClaudeCodeExecutor** (`claude_code_executor.rb`):
|
990
|
+
- **ClaudeSwarm::ClaudeCodeExecutor** (`claude_code_executor.rb`): Executor for Claude Code with session persistence
|
990
991
|
- **ClaudeSwarm::ClaudeMcpServer** (`claude_mcp_server.rb`): FastMCP-based server providing task execution, session info, and reset capabilities
|
991
992
|
|
992
993
|
## Development
|
@@ -27,59 +27,66 @@ module ClaudeSwarm
|
|
27
27
|
# Log the request
|
28
28
|
log_request(prompt)
|
29
29
|
|
30
|
-
|
30
|
+
# Build SDK options
|
31
|
+
sdk_options = build_sdk_options(prompt, options)
|
31
32
|
|
32
33
|
# Variables to collect output
|
33
|
-
|
34
|
+
all_messages = []
|
34
35
|
result_response = nil
|
35
36
|
|
36
|
-
# Execute
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
stdin.close
|
43
|
-
|
44
|
-
# Read stderr in a separate thread
|
45
|
-
stderr_thread = Thread.new do
|
46
|
-
stderr.each_line { |line| stderr_output << line }
|
47
|
-
end
|
48
|
-
|
49
|
-
# Process stdout line by line
|
50
|
-
stdout.each_line do |line|
|
51
|
-
json_data = JSON.parse(line.strip)
|
37
|
+
# Execute with streaming
|
38
|
+
begin
|
39
|
+
ClaudeSDK.query(prompt, options: sdk_options) do |message|
|
40
|
+
# Convert message to hash for logging
|
41
|
+
message_hash = message_to_hash(message)
|
42
|
+
all_messages << message_hash
|
52
43
|
|
53
|
-
|
54
|
-
|
44
|
+
# Log streaming event BEFORE we modify anything
|
45
|
+
log_streaming_event(message_hash)
|
55
46
|
|
47
|
+
# Process specific message types
|
48
|
+
case message
|
49
|
+
when ClaudeSDK::Messages::System
|
56
50
|
# Capture session_id from system init
|
57
|
-
if
|
58
|
-
|
59
|
-
|
51
|
+
if message.subtype == "init" && message.data.is_a?(Hash)
|
52
|
+
# For init messages, session_id is in the data hash
|
53
|
+
session_id = message.data[:session_id] || message.data["session_id"]
|
54
|
+
|
55
|
+
if session_id
|
56
|
+
@session_id = session_id
|
57
|
+
write_instance_state
|
58
|
+
end
|
60
59
|
end
|
61
|
-
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
raise ExecutionError, "Claude Code execution failed: #{error_msg}"
|
60
|
+
when ClaudeSDK::Messages::Assistant
|
61
|
+
# Assistant messages only contain content blocks
|
62
|
+
# No need to track for result extraction - result comes from Result message
|
63
|
+
when ClaudeSDK::Messages::Result
|
64
|
+
# Build result response in expected format
|
65
|
+
result_response = {
|
66
|
+
"type" => "result",
|
67
|
+
"subtype" => message.subtype || "success",
|
68
|
+
"cost_usd" => message.total_cost_usd,
|
69
|
+
"is_error" => message.is_error || false,
|
70
|
+
"duration_ms" => message.duration_ms,
|
71
|
+
"result" => message.result, # Result text is directly in message.result
|
72
|
+
"total_cost" => message.total_cost_usd,
|
73
|
+
"session_id" => message.session_id,
|
74
|
+
}
|
77
75
|
end
|
78
76
|
end
|
77
|
+
rescue StandardError => e
|
78
|
+
@logger.error("Execution error for #{@instance_name}: #{e.class} - #{e.message}")
|
79
|
+
@logger.error("Backtrace: #{e.backtrace.join("\n")}")
|
80
|
+
raise ExecutionError, "Claude Code execution failed: #{e.message}"
|
79
81
|
end
|
80
82
|
|
81
83
|
# Ensure we got a result
|
82
|
-
raise ParseError, "No result found in
|
84
|
+
raise ParseError, "No result found in SDK response" unless result_response
|
85
|
+
|
86
|
+
# Write session JSON log
|
87
|
+
all_messages.each do |msg|
|
88
|
+
append_to_session_json(msg)
|
89
|
+
end
|
83
90
|
|
84
91
|
result_response
|
85
92
|
rescue StandardError => e
|
@@ -195,11 +202,12 @@ module ClaudeSwarm
|
|
195
202
|
end
|
196
203
|
|
197
204
|
def log_assistant_message(msg)
|
198
|
-
|
199
|
-
|
205
|
+
# Assistant messages don't have stop_reason in SDK - they only have content
|
200
206
|
content = msg["content"]
|
201
|
-
@logger.debug("ASSISTANT: #{JSON.pretty_generate(content)}")
|
202
|
-
|
207
|
+
@logger.debug("ASSISTANT: #{JSON.pretty_generate(content)}") if content
|
208
|
+
|
209
|
+
# Log tool calls
|
210
|
+
tool_calls = content&.select { |c| c["type"] == "tool_use" } || []
|
203
211
|
tool_calls.each do |tool_call|
|
204
212
|
arguments = tool_call["input"].to_json
|
205
213
|
arguments = "#{arguments[0..300]} ...}" if arguments.length > 300
|
@@ -211,7 +219,8 @@ module ClaudeSwarm
|
|
211
219
|
)
|
212
220
|
end
|
213
221
|
|
214
|
-
|
222
|
+
# Log thinking text
|
223
|
+
text = content&.select { |c| c["type"] == "text" } || []
|
215
224
|
text.each do |t|
|
216
225
|
instance_info = @instance_name
|
217
226
|
instance_info += " (#{@instance_id})" if @instance_id
|
@@ -251,38 +260,18 @@ module ClaudeSwarm
|
|
251
260
|
raise
|
252
261
|
end
|
253
262
|
|
254
|
-
def
|
255
|
-
|
256
|
-
|
257
|
-
# Add model if specified
|
258
|
-
cmd_array += ["--model", @model]
|
259
|
-
|
260
|
-
cmd_array << "--verbose"
|
263
|
+
def build_sdk_options(prompt, options)
|
264
|
+
# Map CLI options to SDK options
|
265
|
+
sdk_options = ClaudeSDK::ClaudeCodeOptions.new
|
261
266
|
|
262
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end
|
267
|
-
|
268
|
-
# Add MCP config if specified
|
269
|
-
cmd_array += ["--mcp-config", @mcp_config] if @mcp_config
|
270
|
-
|
271
|
-
# Resume session if we have a session ID
|
272
|
-
cmd_array += ["--resume", @session_id] if @session_id && !options[:new_session]
|
273
|
-
|
274
|
-
# Always use JSON output format for structured responses
|
275
|
-
cmd_array += ["--output-format", "stream-json"]
|
276
|
-
|
277
|
-
# Add non-interactive mode with prompt
|
278
|
-
cmd_array += ["--print", "-p", prompt]
|
279
|
-
|
280
|
-
# Add any custom system prompt
|
281
|
-
cmd_array += ["--append-system-prompt", options[:system_prompt]] if options[:system_prompt]
|
267
|
+
# Basic options
|
268
|
+
sdk_options.model = @model if @model
|
269
|
+
sdk_options.cwd = @working_directory
|
270
|
+
sdk_options.resume = @session_id if @session_id && !options[:new_session]
|
282
271
|
|
283
|
-
#
|
272
|
+
# Permission mode
|
284
273
|
if @vibe
|
285
|
-
|
274
|
+
sdk_options.permission_mode = ClaudeSDK::PermissionMode::BYPASS_PERMISSIONS
|
286
275
|
else
|
287
276
|
# Build allowed tools list including MCP connections
|
288
277
|
allowed_tools = options[:allowed_tools] ? Array(options[:allowed_tools]).dup : []
|
@@ -292,20 +281,175 @@ module ClaudeSwarm
|
|
292
281
|
allowed_tools << "mcp__#{connection_name}"
|
293
282
|
end
|
294
283
|
|
295
|
-
#
|
296
|
-
if allowed_tools.any?
|
297
|
-
|
298
|
-
|
284
|
+
# Set allowed and disallowed tools
|
285
|
+
sdk_options.allowed_tools = allowed_tools if allowed_tools.any?
|
286
|
+
sdk_options.disallowed_tools = Array(options[:disallowed_tools]) if options[:disallowed_tools]
|
287
|
+
end
|
288
|
+
|
289
|
+
# System prompt
|
290
|
+
sdk_options.append_system_prompt = options[:system_prompt] if options[:system_prompt]
|
291
|
+
|
292
|
+
# MCP configuration
|
293
|
+
if @mcp_config
|
294
|
+
sdk_options.mcp_servers = parse_mcp_config(@mcp_config)
|
295
|
+
end
|
296
|
+
|
297
|
+
# Handle additional directories by adding them to MCP servers
|
298
|
+
if @additional_directories.any?
|
299
|
+
setup_additional_directories_mcp(sdk_options)
|
300
|
+
end
|
301
|
+
|
302
|
+
sdk_options
|
303
|
+
end
|
304
|
+
|
305
|
+
def parse_mcp_config(config_path)
|
306
|
+
# Parse MCP JSON config file and convert to SDK format
|
307
|
+
config = JSON.parse(File.read(config_path))
|
308
|
+
mcp_servers = {}
|
309
|
+
|
310
|
+
config["mcpServers"]&.each do |name, server_config|
|
311
|
+
server_type = server_config["type"] || "stdio"
|
312
|
+
|
313
|
+
mcp_servers[name] = case server_type
|
314
|
+
when "stdio"
|
315
|
+
ClaudeSDK::McpServerConfig::StdioServer.new(
|
316
|
+
command: server_config["command"],
|
317
|
+
args: server_config["args"] || [],
|
318
|
+
env: server_config["env"] || {},
|
319
|
+
)
|
320
|
+
when "sse"
|
321
|
+
ClaudeSDK::McpServerConfig::SSEServer.new(
|
322
|
+
url: server_config["url"],
|
323
|
+
headers: server_config["headers"] || {},
|
324
|
+
)
|
325
|
+
when "http"
|
326
|
+
ClaudeSDK::McpServerConfig::HttpServer.new(
|
327
|
+
url: server_config["url"],
|
328
|
+
headers: server_config["headers"] || {},
|
329
|
+
)
|
330
|
+
else
|
331
|
+
@logger.warn("Unsupported MCP server type: #{server_type} for server: #{name}")
|
332
|
+
nil
|
299
333
|
end
|
334
|
+
end
|
335
|
+
|
336
|
+
mcp_servers.compact
|
337
|
+
rescue StandardError => e
|
338
|
+
@logger.error("Failed to parse MCP config: #{e.message}")
|
339
|
+
{}
|
340
|
+
end
|
341
|
+
|
342
|
+
def setup_additional_directories_mcp(sdk_options)
|
343
|
+
# Workaround for --add-dir: add file system MCP servers for additional directories
|
344
|
+
sdk_options.mcp_servers ||= {}
|
345
|
+
|
346
|
+
@additional_directories.each do |dir|
|
347
|
+
# This is a placeholder - the SDK doesn't directly support file system servers
|
348
|
+
# You would need to implement a proper MCP server that provides file access
|
349
|
+
@logger.warn("Additional directories not fully supported: #{dir}")
|
350
|
+
end
|
351
|
+
end
|
300
352
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
353
|
+
def message_to_hash(message)
|
354
|
+
# Convert SDK message objects to hash format matching CLI JSON output
|
355
|
+
case message
|
356
|
+
when ClaudeSDK::Messages::System
|
357
|
+
# System messages have subtype and data attributes
|
358
|
+
# The data hash contains the actual information from the CLI
|
359
|
+
hash = {
|
360
|
+
"type" => "system",
|
361
|
+
"subtype" => message.subtype,
|
362
|
+
}
|
363
|
+
|
364
|
+
# Include the data hash if it exists - this is where CLI puts info like session_id, tools, etc.
|
365
|
+
if message.data.is_a?(Hash)
|
366
|
+
# For "init" subtype, extract session_id and tools from data
|
367
|
+
if message.subtype == "init"
|
368
|
+
hash["session_id"] = message.data[:session_id] || message.data["session_id"]
|
369
|
+
hash["tools"] = message.data[:tools] || message.data["tools"]
|
370
|
+
end
|
371
|
+
# You can add other relevant data fields as needed
|
372
|
+
end
|
373
|
+
|
374
|
+
hash.compact
|
375
|
+
when ClaudeSDK::Messages::Assistant
|
376
|
+
# Assistant messages only have content attribute
|
377
|
+
{
|
378
|
+
"type" => "assistant",
|
379
|
+
"message" => {
|
380
|
+
"type" => "message",
|
381
|
+
"role" => "assistant",
|
382
|
+
"content" => content_blocks_to_hash(message.content),
|
383
|
+
},
|
384
|
+
"session_id" => @session_id,
|
385
|
+
}
|
386
|
+
when ClaudeSDK::Messages::User
|
387
|
+
# User messages only have content attribute (a string)
|
388
|
+
{
|
389
|
+
"type" => "user",
|
390
|
+
"message" => {
|
391
|
+
"type" => "message",
|
392
|
+
"role" => "user",
|
393
|
+
"content" => message.content,
|
394
|
+
},
|
395
|
+
"session_id" => @session_id,
|
396
|
+
}
|
397
|
+
when ClaudeSDK::Messages::Result
|
398
|
+
# Result messages have multiple attributes
|
399
|
+
{
|
400
|
+
"type" => "result",
|
401
|
+
"subtype" => message.subtype || "success",
|
402
|
+
"cost_usd" => message.total_cost_usd,
|
403
|
+
"is_error" => message.is_error || false,
|
404
|
+
"duration_ms" => message.duration_ms,
|
405
|
+
"duration_api_ms" => message.duration_api_ms,
|
406
|
+
"num_turns" => message.num_turns,
|
407
|
+
"result" => message.result, # Result text is in message.result, not from content
|
408
|
+
"total_cost" => message.total_cost_usd,
|
409
|
+
"total_cost_usd" => message.total_cost_usd,
|
410
|
+
"session_id" => message.session_id,
|
411
|
+
"usage" => message.usage,
|
412
|
+
}.compact
|
413
|
+
else
|
414
|
+
# Fallback for unknown message types
|
415
|
+
begin
|
416
|
+
message.to_h
|
417
|
+
rescue
|
418
|
+
{ "type" => "unknown", "data" => message.to_s }
|
305
419
|
end
|
306
420
|
end
|
421
|
+
end
|
307
422
|
|
308
|
-
|
423
|
+
def content_blocks_to_hash(content)
|
424
|
+
return [] unless content
|
425
|
+
|
426
|
+
content.map do |block|
|
427
|
+
case block
|
428
|
+
when ClaudeSDK::ContentBlock::Text
|
429
|
+
{ "type" => "text", "text" => block.text }
|
430
|
+
when ClaudeSDK::ContentBlock::ToolUse
|
431
|
+
{
|
432
|
+
"type" => "tool_use",
|
433
|
+
"id" => block.id,
|
434
|
+
"name" => block.name,
|
435
|
+
"input" => block.input,
|
436
|
+
}
|
437
|
+
when ClaudeSDK::ContentBlock::ToolResult
|
438
|
+
{
|
439
|
+
"type" => "tool_result",
|
440
|
+
"tool_use_id" => block.tool_use_id,
|
441
|
+
"content" => block.content,
|
442
|
+
"is_error" => block.is_error,
|
443
|
+
}
|
444
|
+
else
|
445
|
+
# Fallback
|
446
|
+
begin
|
447
|
+
block.to_h
|
448
|
+
rescue
|
449
|
+
{ "type" => "unknown", "data" => block.to_s }
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
309
453
|
end
|
310
454
|
|
311
455
|
class ExecutionError < StandardError; end
|
@@ -4,6 +4,11 @@ module ClaudeSwarm
|
|
4
4
|
class Orchestrator
|
5
5
|
include SystemUtils
|
6
6
|
RUN_DIR = File.expand_path("~/.claude-swarm/run")
|
7
|
+
["INT", "TERM", "QUIT"].each do |signal|
|
8
|
+
Signal.trap(signal) do
|
9
|
+
puts "\n🛑 Received #{signal} signal."
|
10
|
+
end
|
11
|
+
end
|
7
12
|
|
8
13
|
def initialize(configuration, mcp_generator, vibe: false, prompt: nil, interactive_prompt: nil, stream_logs: false, debug: false,
|
9
14
|
restore_session_path: nil, worktree: nil, session_id: nil)
|
@@ -58,9 +63,6 @@ module ClaudeSwarm
|
|
58
63
|
# Initialize process tracker
|
59
64
|
@process_tracker = ProcessTracker.new(session_path)
|
60
65
|
|
61
|
-
# Set up signal handlers to clean up child processes
|
62
|
-
setup_signal_handlers
|
63
|
-
|
64
66
|
# Check if the original session used worktrees
|
65
67
|
restore_worktrees_if_needed(session_path)
|
66
68
|
|
@@ -103,9 +105,6 @@ module ClaudeSwarm
|
|
103
105
|
# Initialize process tracker
|
104
106
|
@process_tracker = ProcessTracker.new(session_path)
|
105
107
|
|
106
|
-
# Set up signal handlers to clean up child processes
|
107
|
-
setup_signal_handlers
|
108
|
-
|
109
108
|
# Create WorktreeManager if needed with session ID
|
110
109
|
if @needs_worktree_manager
|
111
110
|
cli_option = @worktree_option.is_a?(String) && !@worktree_option.empty? ? @worktree_option : nil
|
@@ -371,32 +370,6 @@ module ClaudeSwarm
|
|
371
370
|
File.write(metadata_file, JSON.pretty_generate(metadata))
|
372
371
|
end
|
373
372
|
|
374
|
-
def setup_signal_handlers
|
375
|
-
["INT", "TERM", "QUIT"].each do |signal|
|
376
|
-
Signal.trap(signal) do
|
377
|
-
puts "\n🛑 Received #{signal} signal, cleaning up..."
|
378
|
-
display_summary
|
379
|
-
|
380
|
-
# Execute after commands if configured
|
381
|
-
main_instance = @config.main_instance_config
|
382
|
-
after_commands = @config.after_commands
|
383
|
-
if after_commands.any? && !@restore_session_path && !@non_interactive_prompt
|
384
|
-
Dir.chdir(main_instance[:directory]) do
|
385
|
-
puts
|
386
|
-
puts "⚙️ Executing after commands..."
|
387
|
-
puts
|
388
|
-
execute_after_commands?(after_commands)
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
cleanup_processes
|
393
|
-
cleanup_run_symlink
|
394
|
-
cleanup_worktrees
|
395
|
-
exit
|
396
|
-
end
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
373
|
def cleanup_processes
|
401
374
|
@process_tracker.cleanup_all
|
402
375
|
puts "✓ Cleanup complete"
|
@@ -7,8 +7,12 @@ module ClaudeSwarm
|
|
7
7
|
unless success
|
8
8
|
exit_status = $CHILD_STATUS&.exitstatus || 1
|
9
9
|
command_str = args.size == 1 ? args.first : args.join(" ")
|
10
|
-
|
11
|
-
|
10
|
+
if exit_status == 143 # timeout command exit status = 128 + 15 (SIGTERM)
|
11
|
+
warn("⏱️ Command timeout: #{command_str}")
|
12
|
+
else
|
13
|
+
warn("❌ Command failed with exit status: #{exit_status}")
|
14
|
+
raise Error, "Command failed with exit status #{exit_status}: #{command_str}"
|
15
|
+
end
|
12
16
|
end
|
13
17
|
success
|
14
18
|
end
|
data/lib/claude_swarm/version.rb
CHANGED
data/lib/claude_swarm.rb
CHANGED
data/team.yml
CHANGED
@@ -8,10 +8,20 @@ swarm:
|
|
8
8
|
directory: .
|
9
9
|
model: opus
|
10
10
|
vibe: true
|
11
|
-
connections: [github_expert, fast_mcp_expert, ruby_mcp_client_expert, openai_api_expert]
|
11
|
+
connections: [github_expert, fast_mcp_expert, ruby_mcp_client_expert, openai_api_expert, claude_code_sdk_expert]
|
12
12
|
prompt: |
|
13
13
|
You are the lead developer of Claude Swarm, a Ruby gem that orchestrates multiple Claude Code instances as a collaborative AI development team. The gem enables running AI agents with specialized roles, tools, and directory contexts, communicating via MCP (Model Context Protocol) in a tree-like hierarchy.
|
14
|
-
|
14
|
+
|
15
|
+
IMPORTANT: Use your specialized team members for their areas of expertise. Each team member has deep knowledge in their domain:
|
16
|
+
|
17
|
+
Team Member Usage Guide:
|
18
|
+
- **github_expert**: Use for all Git and GitHub operations including creating issues, PRs, managing releases, checking CI/CD workflows, and repository management
|
19
|
+
- **fast_mcp_expert**: Use for MCP server development, tool creation, resource management, and any FastMCP-related architecture decisions
|
20
|
+
- **ruby_mcp_client_expert**: Use for MCP client integration, multi-transport connectivity, authentication flows, and ruby-mcp-client library guidance
|
21
|
+
- **openai_api_expert**: Use for OpenAI API integration, ruby-openai gem usage, model configuration, and OpenAI provider support in Claude Swarm
|
22
|
+
- **claude_code_sdk_expert**: Use for Claude Code SDK integration, programmatic Claude Code usage, client configuration, and SDK development patterns
|
23
|
+
|
24
|
+
Always delegate specialized tasks to the appropriate team member rather than handling everything yourself. This ensures the highest quality solutions and leverages each expert's deep domain knowledge.
|
15
25
|
|
16
26
|
Your responsibilities include:
|
17
27
|
- Developing new features and improvements for the Claude Swarm gem
|
@@ -254,4 +264,66 @@ swarm:
|
|
254
264
|
- Set up CI to run code_quality checks
|
255
265
|
- Document Raix integration in wiki/docs
|
256
266
|
|
257
|
-
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
267
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
268
|
+
|
269
|
+
claude_code_sdk_expert:
|
270
|
+
description: "Expert in Claude Code SDK for Ruby, specializing in client integration and API usage patterns"
|
271
|
+
directory: ~/src/github.com/parruda/claude-code-sdk-ruby
|
272
|
+
model: opus
|
273
|
+
vibe: true
|
274
|
+
prompt: |
|
275
|
+
You are an expert in the Claude Code SDK for Ruby, specializing in client integration, API usage patterns, and SDK development best practices.
|
276
|
+
|
277
|
+
Your expertise covers:
|
278
|
+
- Claude Code SDK architecture and client configuration
|
279
|
+
- API authentication and session management
|
280
|
+
- Request/response handling and error management
|
281
|
+
- Code generation and analysis capabilities
|
282
|
+
- SDK integration patterns and best practices
|
283
|
+
- Tool usage and permission management
|
284
|
+
- Streaming responses and real-time interactions
|
285
|
+
- Multi-modal capabilities (text, code, images)
|
286
|
+
- Rate limiting and cost optimization
|
287
|
+
- Testing and debugging SDK integrations
|
288
|
+
|
289
|
+
Key responsibilities:
|
290
|
+
- Analyze Claude Code SDK codebase for implementation patterns
|
291
|
+
- Provide guidance on proper SDK usage and integration
|
292
|
+
- Design robust client configurations and authentication flows
|
293
|
+
- Implement efficient request handling and error recovery
|
294
|
+
- Optimize SDK performance and resource usage
|
295
|
+
- Create comprehensive examples and documentation
|
296
|
+
- Troubleshoot integration issues and API errors
|
297
|
+
- Ensure proper handling of streaming and async operations
|
298
|
+
|
299
|
+
Technical focus areas:
|
300
|
+
- Client initialization and configuration options
|
301
|
+
- Authentication flows and token management
|
302
|
+
- Request builders and parameter validation
|
303
|
+
- Response parsing and error handling
|
304
|
+
- Streaming implementations and chunk processing
|
305
|
+
- Tool integration and permission management
|
306
|
+
- Session management and state persistence
|
307
|
+
- Performance optimization and caching strategies
|
308
|
+
- Testing patterns and mock configurations
|
309
|
+
|
310
|
+
When providing guidance:
|
311
|
+
- Reference specific SDK methods and classes
|
312
|
+
- Include practical code examples and usage patterns
|
313
|
+
- Explain both SDK abstractions and underlying API details
|
314
|
+
- Highlight important configuration options and their implications
|
315
|
+
- Warn about common pitfalls and API limitations
|
316
|
+
- Suggest performance optimizations and cost-saving strategies
|
317
|
+
- Provide context on when to use different SDK features
|
318
|
+
- Demonstrate proper error handling and retry strategies
|
319
|
+
|
320
|
+
Integration with Claude Swarm development:
|
321
|
+
- Understand how Claude Code SDK can enhance swarm capabilities
|
322
|
+
- Provide insights on programmatic Claude Code integration
|
323
|
+
- Design patterns for automated swarm management
|
324
|
+
- Optimize SDK usage within swarm orchestration
|
325
|
+
- Ensure compatibility with swarm session management
|
326
|
+
|
327
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
328
|
+
|
329
|
+
Help developers integrate Claude Code SDK effectively with confidence, best practices, and optimal performance.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: claude_swarm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo Arruda
|
@@ -37,20 +37,34 @@ dependencies:
|
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '2.6'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: claude-code-sdk-ruby
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.1'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.1'
|
40
54
|
- !ruby/object:Gem::Dependency
|
41
55
|
name: fast-mcp-annotations
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
43
57
|
requirements:
|
44
58
|
- - "~>"
|
45
59
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.5
|
60
|
+
version: '1.5'
|
47
61
|
type: :runtime
|
48
62
|
prerelease: false
|
49
63
|
version_requirements: !ruby/object:Gem::Requirement
|
50
64
|
requirements:
|
51
65
|
- - "~>"
|
52
66
|
- !ruby/object:Gem::Version
|
53
|
-
version: 1.5
|
67
|
+
version: '1.5'
|
54
68
|
- !ruby/object:Gem::Dependency
|
55
69
|
name: ruby-mcp-client
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
178
|
- !ruby/object:Gem::Version
|
165
179
|
version: '0'
|
166
180
|
requirements: []
|
167
|
-
rubygems_version: 3.6.
|
181
|
+
rubygems_version: 3.6.9
|
168
182
|
specification_version: 4
|
169
183
|
summary: Orchestrate multiple Claude Code instances as a collaborative AI development
|
170
184
|
team
|