claude_swarm 0.1.1 → 0.1.3
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 +18 -1
- data/README.md +14 -8
- data/claude-swarm.yml +26 -0
- data/lib/claude_swarm/claude_code_executor.rb +2 -5
- data/lib/claude_swarm/claude_mcp_server.rb +18 -12
- data/lib/claude_swarm/cli.rb +12 -4
- data/lib/claude_swarm/mcp_generator.rb +11 -7
- data/lib/claude_swarm/orchestrator.rb +29 -18
- data/lib/claude_swarm/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d82adc5c9d855904f9809e4e2c8d9e34b7dbd5b2dd6ebec98e4072d6c81f3cf
|
4
|
+
data.tar.gz: 1faa01211ebe1b05fcc0dbf8b654b77e3512b4a4653b9169ef8c1b1e40226680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 539f27f040ff91900bd8d88f925564f07ee4ad673eaaadcfdda228e44ca21adfd46a8be3dd40ae3a9b267b15ac5df4a5bd154d855f75ef85f1888522b71a716b
|
7
|
+
data.tar.gz: 81c0d46bf825684597d80271e7b0e7480b8c92e80c2465af1513b6d80f4dea2100b173261311243f5514e4aeb07bbdedebc0467b293415647bd8397161f98e8d
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,21 @@
|
|
1
|
-
## [
|
1
|
+
## [0.1.3]
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
- Fixed duplicate prompt arguments being passed to Claude Code executor, which could cause command execution failures
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- Improved logging to track request flow between instances using `from_instance` and `to_instance` fields instead of generic `instance_name`
|
8
|
+
- Added required `calling_instance` parameter to MCP server command to properly identify the source of requests in tree configurations
|
9
|
+
- Consolidated session files into a single directory structure (`.claude-swarm/sessions/<timestamp>/`)
|
10
|
+
- MCP configuration files are now stored alongside session logs in the same timestamped directory
|
11
|
+
- Session logs are now named `session.log` instead of `session_<timestamp>.log`
|
12
|
+
- Improved organization by keeping all session-related files together
|
13
|
+
|
14
|
+
## [0.1.2] - 2025-05-29
|
15
|
+
|
16
|
+
### Added
|
17
|
+
- Added `-p` / `--prompt` flag to pass prompts directly to the main Claude instance for non-interactive mode
|
18
|
+
- Output suppression when running with the `-p` flag for cleaner scripted usage
|
2
19
|
|
3
20
|
## [0.1.1] - 2025-05-24
|
4
21
|
|
data/README.md
CHANGED
@@ -77,7 +77,7 @@ claude-swarm --vibe # That will allow ALL tools for all instances! Be Careful!
|
|
77
77
|
This will:
|
78
78
|
- Launch the main instance (lead) with connections to other instances
|
79
79
|
- The lead instance can communicate with the other instances via MCP
|
80
|
-
- All
|
80
|
+
- All session files are stored in `.claude-swarm/sessions/{timestamp}/`
|
81
81
|
|
82
82
|
#### Multi-Level Swarm Example
|
83
83
|
|
@@ -374,6 +374,10 @@ claude-swarm -c team-config.yml
|
|
374
374
|
# Run with --dangerously-skip-permissions for all instances
|
375
375
|
claude-swarm --vibe
|
376
376
|
|
377
|
+
# Run in non-interactive mode with a prompt
|
378
|
+
claude-swarm -p "Implement the new user authentication feature"
|
379
|
+
claude-swarm --prompt "Fix the bug in the payment module"
|
380
|
+
|
377
381
|
# Show version
|
378
382
|
claude-swarm version
|
379
383
|
|
@@ -396,7 +400,9 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
|
|
396
400
|
- **task**: Execute tasks using Claude Code with configurable tools and return results
|
397
401
|
- **session_info**: Get current Claude session information including ID and working directory
|
398
402
|
- **reset_session**: Reset the Claude session for a fresh start
|
399
|
-
6. **
|
403
|
+
6. **Session Management**: All session files are organized in `.claude-swarm/sessions/{timestamp}/`:
|
404
|
+
- MCP configuration files: `{instance_name}.mcp.json`
|
405
|
+
- Session log: `session.log` with detailed request/response tracking
|
400
406
|
|
401
407
|
## Troubleshooting
|
402
408
|
|
@@ -419,16 +425,16 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
|
|
419
425
|
### Debug Output
|
420
426
|
|
421
427
|
The swarm will display:
|
422
|
-
-
|
428
|
+
- Session directory location (`.claude-swarm/sessions/{timestamp}/`)
|
423
429
|
- Main instance details (model, directory, tools, connections)
|
424
430
|
- The exact command being run
|
425
431
|
|
426
|
-
###
|
432
|
+
### Session Files
|
427
433
|
|
428
|
-
Check the
|
429
|
-
-
|
430
|
-
- MCP
|
431
|
-
-
|
434
|
+
Check the session directory `.claude-swarm/sessions/{timestamp}/` for:
|
435
|
+
- `session.log`: Detailed logs with request/response tracking
|
436
|
+
- `{instance}.mcp.json`: MCP configuration for each instance
|
437
|
+
- All files for a session are kept together for easy review
|
432
438
|
|
433
439
|
## Architecture
|
434
440
|
|
data/claude-swarm.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
version: 1
|
2
|
+
swarm:
|
3
|
+
name: "Swarm Name"
|
4
|
+
main: lead_developer
|
5
|
+
instances:
|
6
|
+
lead_developer:
|
7
|
+
directory: .
|
8
|
+
model: sonnet
|
9
|
+
prompt: "You are the lead developer coordinating the team"
|
10
|
+
tools: [Read, Edit, Bash, Write]
|
11
|
+
connections: [frontend_dev]
|
12
|
+
|
13
|
+
# Example instances (uncomment and modify as needed):
|
14
|
+
|
15
|
+
frontend_dev:
|
16
|
+
directory: .
|
17
|
+
model: sonnet
|
18
|
+
prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
|
19
|
+
tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
|
20
|
+
connections: [css_dev]
|
21
|
+
|
22
|
+
css_dev:
|
23
|
+
directory: .
|
24
|
+
model: sonnet
|
25
|
+
prompt: "You specialize in CSS development with Tailwind CSS, CSS Modules, and modern web technologies"
|
26
|
+
tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
|
@@ -67,8 +67,8 @@ module ClaudeSwarm
|
|
67
67
|
# Always use JSON output format for structured responses
|
68
68
|
cmd_array += ["--output-format", "json"]
|
69
69
|
|
70
|
-
# Add non-interactive mode
|
71
|
-
cmd_array
|
70
|
+
# Add non-interactive mode with prompt
|
71
|
+
cmd_array += ["--print", "-p", prompt]
|
72
72
|
|
73
73
|
# Add any custom system prompt
|
74
74
|
cmd_array += ["--system-prompt", options[:system_prompt]] if options[:system_prompt]
|
@@ -81,9 +81,6 @@ module ClaudeSwarm
|
|
81
81
|
cmd_array += ["--allowedTools", tools]
|
82
82
|
end
|
83
83
|
|
84
|
-
# Add the prompt as the last argument (no escaping needed with array syntax)
|
85
|
-
cmd_array << prompt
|
86
|
-
|
87
84
|
cmd_array
|
88
85
|
end
|
89
86
|
|
@@ -9,15 +9,16 @@ require_relative "claude_code_executor"
|
|
9
9
|
module ClaudeSwarm
|
10
10
|
class ClaudeMcpServer
|
11
11
|
SWARM_DIR = ".claude-swarm"
|
12
|
-
|
12
|
+
SESSIONS_DIR = "sessions"
|
13
13
|
|
14
14
|
# Class variables to share state with tool classes
|
15
15
|
class << self
|
16
|
-
attr_accessor :executor, :instance_config, :logger, :session_timestamp
|
16
|
+
attr_accessor :executor, :instance_config, :logger, :session_timestamp, :calling_instance
|
17
17
|
end
|
18
18
|
|
19
|
-
def initialize(instance_config)
|
19
|
+
def initialize(instance_config, calling_instance:)
|
20
20
|
@instance_config = instance_config
|
21
|
+
@calling_instance = calling_instance
|
21
22
|
@executor = ClaudeCodeExecutor.new(
|
22
23
|
working_directory: instance_config[:directory],
|
23
24
|
model: instance_config[:model],
|
@@ -32,6 +33,7 @@ module ClaudeSwarm
|
|
32
33
|
self.class.executor = @executor
|
33
34
|
self.class.instance_config = @instance_config
|
34
35
|
self.class.logger = @logger
|
36
|
+
self.class.calling_instance = @calling_instance
|
35
37
|
end
|
36
38
|
|
37
39
|
private
|
@@ -41,13 +43,13 @@ module ClaudeSwarm
|
|
41
43
|
# Otherwise create a new timestamp
|
42
44
|
self.class.session_timestamp ||= ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
|
43
45
|
|
44
|
-
# Ensure the
|
45
|
-
|
46
|
-
FileUtils.mkdir_p(
|
46
|
+
# Ensure the session directory exists
|
47
|
+
session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, self.class.session_timestamp)
|
48
|
+
FileUtils.mkdir_p(session_dir)
|
47
49
|
|
48
|
-
# Create logger with
|
49
|
-
log_filename = "
|
50
|
-
log_path = File.join(
|
50
|
+
# Create logger with session.log filename
|
51
|
+
log_filename = "session.log"
|
52
|
+
log_path = File.join(session_dir, log_filename)
|
51
53
|
@logger = Logger.new(log_path)
|
52
54
|
@logger.level = Logger::INFO
|
53
55
|
|
@@ -103,7 +105,8 @@ module ClaudeSwarm
|
|
103
105
|
# Log the request
|
104
106
|
log_entry = {
|
105
107
|
timestamp: Time.now.utc.iso8601,
|
106
|
-
|
108
|
+
from_instance: ClaudeMcpServer.calling_instance, # The instance making the request
|
109
|
+
to_instance: instance_config[:name], # This instance is receiving the request
|
107
110
|
model: instance_config[:model],
|
108
111
|
working_directory: instance_config[:directory],
|
109
112
|
session_id: executor.session_id,
|
@@ -120,7 +123,10 @@ module ClaudeSwarm
|
|
120
123
|
response = executor.execute(prompt, options)
|
121
124
|
|
122
125
|
# Log the response
|
123
|
-
response_entry =
|
126
|
+
response_entry = {
|
127
|
+
timestamp: Time.now.utc.iso8601,
|
128
|
+
from_instance: instance_config[:name], # This instance is sending the response
|
129
|
+
to_instance: ClaudeMcpServer.calling_instance, # The instance that made the request receives the response
|
124
130
|
session_id: executor.session_id, # Update with new session ID if changed
|
125
131
|
response: {
|
126
132
|
result: response["result"],
|
@@ -129,7 +135,7 @@ module ClaudeSwarm
|
|
129
135
|
is_error: response["is_error"],
|
130
136
|
total_cost: response["total_cost"]
|
131
137
|
}
|
132
|
-
|
138
|
+
}
|
133
139
|
|
134
140
|
logger.info("RESPONSE: #{JSON.pretty_generate(response_entry)}")
|
135
141
|
|
data/lib/claude_swarm/cli.rb
CHANGED
@@ -17,6 +17,8 @@ module ClaudeSwarm
|
|
17
17
|
desc: "Path to configuration file"
|
18
18
|
method_option :vibe, type: :boolean, default: false,
|
19
19
|
desc: "Run with --dangerously-skip-permissions for all instances"
|
20
|
+
method_option :prompt, aliases: "-p", type: :string,
|
21
|
+
desc: "Prompt to pass to the main Claude instance (non-interactive mode)"
|
20
22
|
def start(config_file = nil)
|
21
23
|
config_path = config_file || options[:config]
|
22
24
|
unless File.exist?(config_path)
|
@@ -24,11 +26,15 @@ module ClaudeSwarm
|
|
24
26
|
exit 1
|
25
27
|
end
|
26
28
|
|
27
|
-
say "Starting Claude Swarm from #{config_path}..."
|
29
|
+
say "Starting Claude Swarm from #{config_path}..." unless options[:prompt]
|
28
30
|
begin
|
29
31
|
config = Configuration.new(config_path)
|
30
|
-
|
31
|
-
|
32
|
+
session_timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
33
|
+
generator = McpGenerator.new(config, vibe: options[:vibe], timestamp: session_timestamp)
|
34
|
+
orchestrator = Orchestrator.new(config, generator,
|
35
|
+
vibe: options[:vibe],
|
36
|
+
prompt: options[:prompt],
|
37
|
+
session_timestamp: session_timestamp)
|
32
38
|
orchestrator.start
|
33
39
|
rescue Error => e
|
34
40
|
error e.message
|
@@ -57,6 +63,8 @@ module ClaudeSwarm
|
|
57
63
|
desc: "Enable debug output"
|
58
64
|
method_option :vibe, type: :boolean, default: false,
|
59
65
|
desc: "Run with --dangerously-skip-permissions"
|
66
|
+
method_option :calling_instance, type: :string, required: true,
|
67
|
+
desc: "Name of the instance that launched this MCP server"
|
60
68
|
def mcp_serve
|
61
69
|
instance_config = {
|
62
70
|
name: options[:name],
|
@@ -69,7 +77,7 @@ module ClaudeSwarm
|
|
69
77
|
}
|
70
78
|
|
71
79
|
begin
|
72
|
-
server = ClaudeMcpServer.new(instance_config)
|
80
|
+
server = ClaudeMcpServer.new(instance_config, calling_instance: options[:calling_instance])
|
73
81
|
server.start
|
74
82
|
rescue StandardError => e
|
75
83
|
error "Error starting MCP server: #{e.message}"
|
@@ -7,10 +7,12 @@ require "shellwords"
|
|
7
7
|
module ClaudeSwarm
|
8
8
|
class McpGenerator
|
9
9
|
SWARM_DIR = ".claude-swarm"
|
10
|
+
SESSIONS_SUBDIR = "sessions"
|
10
11
|
|
11
|
-
def initialize(configuration, vibe: false)
|
12
|
+
def initialize(configuration, vibe: false, timestamp: nil)
|
12
13
|
@config = configuration
|
13
14
|
@vibe = vibe
|
15
|
+
@timestamp = timestamp || Time.now.strftime("%Y%m%d_%H%M%S")
|
14
16
|
end
|
15
17
|
|
16
18
|
def generate_all
|
@@ -22,7 +24,7 @@ module ClaudeSwarm
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def mcp_config_path(instance_name)
|
25
|
-
File.join(Dir.pwd, SWARM_DIR, "#{instance_name}.mcp.json")
|
27
|
+
File.join(Dir.pwd, SWARM_DIR, SESSIONS_SUBDIR, @timestamp, "#{instance_name}.mcp.json")
|
26
28
|
end
|
27
29
|
|
28
30
|
private
|
@@ -34,9 +36,9 @@ module ClaudeSwarm
|
|
34
36
|
def ensure_swarm_directory
|
35
37
|
FileUtils.mkdir_p(swarm_dir)
|
36
38
|
|
37
|
-
# Create
|
38
|
-
|
39
|
-
FileUtils.mkdir_p(
|
39
|
+
# Create session directory with timestamp
|
40
|
+
session_dir = File.join(swarm_dir, SESSIONS_SUBDIR, @timestamp)
|
41
|
+
FileUtils.mkdir_p(session_dir)
|
40
42
|
|
41
43
|
gitignore_path = File.join(swarm_dir, ".gitignore")
|
42
44
|
File.write(gitignore_path, "*\n") unless File.exist?(gitignore_path)
|
@@ -53,7 +55,7 @@ module ClaudeSwarm
|
|
53
55
|
# Add connection MCPs for other instances
|
54
56
|
instance[:connections].each do |connection_name|
|
55
57
|
connected_instance = @config.instances[connection_name]
|
56
|
-
mcp_servers[connection_name] = build_instance_mcp_config(connection_name, connected_instance)
|
58
|
+
mcp_servers[connection_name] = build_instance_mcp_config(connection_name, connected_instance, calling_instance: name)
|
57
59
|
end
|
58
60
|
|
59
61
|
config = {
|
@@ -81,7 +83,7 @@ module ClaudeSwarm
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
|
-
def build_instance_mcp_config(name, instance)
|
86
|
+
def build_instance_mcp_config(name, instance, calling_instance:)
|
85
87
|
# Get the path to the claude-swarm executable
|
86
88
|
exe_path = "claude-swarm"
|
87
89
|
|
@@ -100,6 +102,8 @@ module ClaudeSwarm
|
|
100
102
|
|
101
103
|
args.push("--mcp-config-path", mcp_config_path(name))
|
102
104
|
|
105
|
+
args.push("--calling-instance", calling_instance) if calling_instance
|
106
|
+
|
103
107
|
args.push("--vibe") if @vibe
|
104
108
|
|
105
109
|
{
|
@@ -4,39 +4,48 @@ require "shellwords"
|
|
4
4
|
|
5
5
|
module ClaudeSwarm
|
6
6
|
class Orchestrator
|
7
|
-
def initialize(configuration, mcp_generator, vibe: false)
|
7
|
+
def initialize(configuration, mcp_generator, vibe: false, prompt: nil, session_timestamp: nil)
|
8
8
|
@config = configuration
|
9
9
|
@generator = mcp_generator
|
10
10
|
@vibe = vibe
|
11
|
+
@prompt = prompt
|
12
|
+
@session_timestamp = session_timestamp || Time.now.strftime("%Y%m%d_%H%M%S")
|
11
13
|
end
|
12
14
|
|
13
15
|
def start
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
unless @prompt
|
17
|
+
puts "🐝 Starting Claude Swarm: #{@config.swarm_name}"
|
18
|
+
puts "😎 Vibe mode ON" if @vibe
|
19
|
+
puts
|
20
|
+
end
|
17
21
|
|
18
|
-
# Set session timestamp for all instances to share the same
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
# Set session timestamp for all instances to share the same session directory
|
23
|
+
ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] = @session_timestamp
|
24
|
+
unless @prompt
|
25
|
+
puts "📝 Session files will be saved to: .claude-swarm/sessions/#{@session_timestamp}/"
|
26
|
+
puts
|
27
|
+
end
|
23
28
|
|
24
29
|
# Generate all MCP configuration files
|
25
30
|
@generator.generate_all
|
26
|
-
|
27
|
-
|
31
|
+
unless @prompt
|
32
|
+
puts "✓ Generated MCP configurations in session directory"
|
33
|
+
puts
|
34
|
+
end
|
28
35
|
|
29
36
|
# Launch the main instance
|
30
37
|
main_instance = @config.main_instance_config
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
unless @prompt
|
39
|
+
puts "🚀 Launching main instance: #{@config.main_instance}"
|
40
|
+
puts " Model: #{main_instance[:model]}"
|
41
|
+
puts " Directory: #{main_instance[:directory]}"
|
42
|
+
puts " Tools: #{main_instance[:tools].join(", ")}" if main_instance[:tools].any?
|
43
|
+
puts " Connections: #{main_instance[:connections].join(", ")}" if main_instance[:connections].any?
|
44
|
+
puts
|
45
|
+
end
|
37
46
|
|
38
47
|
command = build_main_command(main_instance)
|
39
|
-
if ENV["DEBUG"]
|
48
|
+
if ENV["DEBUG"] && !@prompt
|
40
49
|
puts "Running: #{command}"
|
41
50
|
puts
|
42
51
|
end
|
@@ -65,6 +74,8 @@ module ClaudeSwarm
|
|
65
74
|
mcp_config_path = @generator.mcp_config_path(@config.main_instance)
|
66
75
|
parts << "--mcp-config #{mcp_config_path}"
|
67
76
|
|
77
|
+
parts << "-p #{Shellwords.escape(@prompt)}" if @prompt
|
78
|
+
|
68
79
|
parts.join(" ")
|
69
80
|
end
|
70
81
|
end
|
data/lib/claude_swarm/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: claude_swarm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo Arruda
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-30 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: thor
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- LICENSE
|
58
58
|
- README.md
|
59
59
|
- Rakefile
|
60
|
+
- claude-swarm.yml
|
60
61
|
- example/claude-swarm.yml
|
61
62
|
- exe/claude-swarm
|
62
63
|
- lib/claude_swarm.rb
|