claude_swarm 0.1.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37bc8baf6ea837fcd1e8936a1ae5a6a13138fad6e6efb3708deca991673e119d
4
- data.tar.gz: 1e3d06c4138a42e7ab54a121c4bd3e584db3859e640a5965aac88d04fe0e6132
3
+ metadata.gz: 8d82adc5c9d855904f9809e4e2c8d9e34b7dbd5b2dd6ebec98e4072d6c81f3cf
4
+ data.tar.gz: 1faa01211ebe1b05fcc0dbf8b654b77e3512b4a4653b9169ef8c1b1e40226680
5
5
  SHA512:
6
- metadata.gz: dba5aab927293d3cc5fc4190b0cd84f7addf7ae0f90224df425a166d33c5feb5e062be21c8b915589191a14609f25f6fc68da63402c5295588bd5c4ed1d177ab
7
- data.tar.gz: 6a7d5058d8583bd1bf6fec8d148fe62e617535528f49f960412eb6da065db01a0405f4de6cff8963c63c523ce81cbf661dc49fe8840df3d568eaa2fcba853284
6
+ metadata.gz: 539f27f040ff91900bd8d88f925564f07ee4ad673eaaadcfdda228e44ca21adfd46a8be3dd40ae3a9b267b15ac5df4a5bd154d855f75ef85f1888522b71a716b
7
+ data.tar.gz: 81c0d46bf825684597d80271e7b0e7480b8c92e80c2465af1513b6d80f4dea2100b173261311243f5514e4aeb07bbdedebc0467b293415647bd8397161f98e8d
data/CHANGELOG.md CHANGED
@@ -1,4 +1,15 @@
1
- ## [Unreleased]
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
2
13
 
3
14
  ## [0.1.2] - 2025-05-29
4
15
 
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 instances log to `.claude-swarm/logs/session-{timestamp}/`
80
+ - All session files are stored in `.claude-swarm/sessions/{timestamp}/`
81
81
 
82
82
  #### Multi-Level Swarm Example
83
83
 
@@ -400,7 +400,9 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
400
400
  - **task**: Execute tasks using Claude Code with configurable tools and return results
401
401
  - **session_info**: Get current Claude session information including ID and working directory
402
402
  - **reset_session**: Reset the Claude session for a fresh start
403
- 6. **Centralized Logging**: All instances log to `.claude-swarm/logs/session-{timestamp}/` with detailed request/response tracking
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
404
406
 
405
407
  ## Troubleshooting
406
408
 
@@ -423,16 +425,16 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
423
425
  ### Debug Output
424
426
 
425
427
  The swarm will display:
426
- - Generated MCP configuration location
428
+ - Session directory location (`.claude-swarm/sessions/{timestamp}/`)
427
429
  - Main instance details (model, directory, tools, connections)
428
430
  - The exact command being run
429
431
 
430
- ### Logs
432
+ ### Session Files
431
433
 
432
- Check the centralized logs in `.claude-swarm/logs/session-{timestamp}/` for:
433
- - Individual instance logs
434
- - MCP server communication
435
- - Error messages and debugging information
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
436
438
 
437
439
  ## Architecture
438
440
 
data/claude-swarm.yml CHANGED
@@ -17,3 +17,10 @@ swarm:
17
17
  model: sonnet
18
18
  prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
19
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 << "--print"
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
- LOGS_DIR = "logs"
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 logs directory exists
45
- logs_dir = File.join(Dir.pwd, SWARM_DIR, LOGS_DIR)
46
- FileUtils.mkdir_p(logs_dir)
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 timestamped filename
49
- log_filename = "session_#{self.class.session_timestamp}.log"
50
- log_path = File.join(logs_dir, log_filename)
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
- instance_name: instance_config[:name],
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 = log_entry.merge(
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
 
@@ -29,8 +29,12 @@ module ClaudeSwarm
29
29
  say "Starting Claude Swarm from #{config_path}..." unless options[:prompt]
30
30
  begin
31
31
  config = Configuration.new(config_path)
32
- generator = McpGenerator.new(config, vibe: options[:vibe])
33
- orchestrator = Orchestrator.new(config, generator, vibe: options[:vibe], prompt: options[:prompt])
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)
34
38
  orchestrator.start
35
39
  rescue Error => e
36
40
  error e.message
@@ -59,6 +63,8 @@ module ClaudeSwarm
59
63
  desc: "Enable debug output"
60
64
  method_option :vibe, type: :boolean, default: false,
61
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"
62
68
  def mcp_serve
63
69
  instance_config = {
64
70
  name: options[:name],
@@ -71,7 +77,7 @@ module ClaudeSwarm
71
77
  }
72
78
 
73
79
  begin
74
- server = ClaudeMcpServer.new(instance_config)
80
+ server = ClaudeMcpServer.new(instance_config, calling_instance: options[:calling_instance])
75
81
  server.start
76
82
  rescue StandardError => e
77
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 logs directory as well
38
- logs_dir = File.join(swarm_dir, "logs")
39
- FileUtils.mkdir_p(logs_dir)
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,11 +4,12 @@ require "shellwords"
4
4
 
5
5
  module ClaudeSwarm
6
6
  class Orchestrator
7
- def initialize(configuration, mcp_generator, vibe: false, prompt: nil)
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
11
  @prompt = prompt
12
+ @session_timestamp = session_timestamp || Time.now.strftime("%Y%m%d_%H%M%S")
12
13
  end
13
14
 
14
15
  def start
@@ -18,18 +19,17 @@ module ClaudeSwarm
18
19
  puts
19
20
  end
20
21
 
21
- # Set session timestamp for all instances to share the same log file
22
- session_timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
23
- ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] = session_timestamp
22
+ # Set session timestamp for all instances to share the same session directory
23
+ ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] = @session_timestamp
24
24
  unless @prompt
25
- puts "📝 Session logs will be saved to: .claude-swarm/logs/session_#{session_timestamp}.log"
25
+ puts "📝 Session files will be saved to: .claude-swarm/sessions/#{@session_timestamp}/"
26
26
  puts
27
27
  end
28
28
 
29
29
  # Generate all MCP configuration files
30
30
  @generator.generate_all
31
31
  unless @prompt
32
- puts "✓ Generated MCP configurations in .claude-swarm/"
32
+ puts "✓ Generated MCP configurations in session directory"
33
33
  puts
34
34
  end
35
35
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
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.2
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-29 00:00:00.000000000 Z
10
+ date: 2025-05-30 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: thor