claude_swarm 0.1.11 → 0.1.13

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: 6d943c577c58e38133c56a9852a175bb4167916784c2fcda3487740f70c9ff4e
4
- data.tar.gz: 8aaebece866fd8f73f12a86b300d85ffb0a1ac3b160e3c3d70e4a3df48ba1594
3
+ metadata.gz: 57e09297e72fe54ef127b5f7b5789c9e9d825279404ffa96c29f2ff0e7fbc622
4
+ data.tar.gz: 6e1d719d97d2f8fefa4ec8d0e9396e5fba867012b94d88d915247aea18690523
5
5
  SHA512:
6
- metadata.gz: 20cc61370c608454ec588cf5771290cb7e9c563ef6bbc13c3d02009d33292445d6da3b069a7dc4cc5c69ce916183b49adfbd86cbaf2df41185912fa7e73aab87
7
- data.tar.gz: 2b2f63db094d495da5b76bede0b2c49e933a6a5adfd4b9fb9967753e51011302291ad0c47d721c120113b0d182b501650ffd4cd1eacd47fa8a5c21db08249475
6
+ metadata.gz: 8ad9a411c8bc503829927ea534fed74e9f525dd62d68fc6eb9978667afdb40435b9bac0c13320c097f1f41556a8d50215c723c8066dec7366cf7d847d07a7e9e
7
+ data.tar.gz: 490824249ac673b7904f05a084d2ff0190a1034529a9302f423d00e965cd7c793b519b1d8e318a36a1e58af80706cb1fda80dd507af525b45f7df26299abd3f9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,50 @@
1
+ ## [Unreleased]
2
+
3
+ ### Added
4
+ - **Session restoration support (Experimental)**: Session management with the ability to resume previous Claude Swarm sessions. Note: This is an experimental feature with limitations - the main instance's conversation context is not fully restored
5
+ - New `--session-id` flag to resume a session by ID or path
6
+ - New `list-sessions` command to view available sessions with metadata
7
+ - Automatic capture and persistence of Claude session IDs for all instances
8
+ - Individual instance states stored in `state/` directory with instance ID as filename (e.g., `state/lead_abc123.json`)
9
+ - Swarm configuration copied to session directory as `config.yml` for restoration
10
+ - **Instance ID tracking**: Each instance now gets a unique ID in the format `instance_name_<hex>` for better identification in logs
11
+ - **Enhanced logging with instance IDs**: All log messages now include instance IDs when available (e.g., `lead (lead_1234abcd) -> backend (backend_5678efgh)`)
12
+ - **Calling instance ID propagation**: When one instance calls another, both the calling instance name and ID are passed for complete tracking
13
+ - Instance IDs are stored in MCP configuration files with `instance_id` and `instance_name` fields
14
+ - New CLI options: `--instance-id` and `--calling-instance-id` for the `mcp-serve` command
15
+ - ClaudeCodeExecutor now tracks and logs both instance and calling instance IDs
16
+ - **Process tracking and cleanup**: Added automatic tracking and cleanup of child MCP server processes
17
+ - New `ProcessTracker` class creates individual PID files in a `pids/` directory within the session path
18
+ - Signal handlers (INT, TERM, QUIT) ensure all child processes are terminated when the main instance exits
19
+ - Prevents orphaned MCP server processes from continuing to run after swarm termination
20
+
21
+ ### Changed
22
+ - Human-readable logs improved to show instance IDs in parentheses after instance names for easier tracking of multi-instance interactions
23
+ - `log_request` method enhanced to include instance IDs in structured JSON logs
24
+ - Configuration class now accepts optional `base_dir` parameter to support session restoration from different directories
25
+
26
+ ### Fixed
27
+ - Fixed issue where child MCP server processes would continue running after the main instance exits
28
+
29
+ ## [0.1.12]
30
+ ### Added
31
+ - **Circular dependency detection**: Configuration validation now detects and reports circular dependencies between instances
32
+ - Clear error messages showing the dependency cycle (e.g., "Circular dependency detected: lead -> backend -> lead")
33
+ - Comprehensive test coverage for various circular dependency scenarios
34
+ - **Session management improvements**: Session files are now stored in `~/.claude-swarm/sessions/` organized by project path
35
+ - Added `SessionPath` module to centralize session path management
36
+ - Sessions are now organized by project directory for better multi-project support
37
+ - Added `CLAUDE_SWARM_HOME` environment variable support for custom storage location
38
+ - Log full JSON to `session.log.json` as JSONL
39
+
40
+ ### Changed
41
+ - Session files moved from `./.claude-swarm/sessions/` to `~/.claude-swarm/sessions/[project]/[timestamp]/`
42
+ - Replaced `CLAUDE_SWARM_SESSION_TIMESTAMP` with `CLAUDE_SWARM_SESSION_PATH` environment variable
43
+ - MCP server configurations now use the new centralized session path
44
+
45
+ ### Fixed
46
+ - Fixed circular dependency example in README documentation
47
+
1
48
  ## [0.1.11]
2
49
  ### Added
3
50
  - Main instance debug mode with `claude-swarm --debug`
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Claude Swarm
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0)](https://badge.fury.io/rb/claude_swarm)
3
+ [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0.1.12)](https://badge.fury.io/rb/claude_swarm)
4
4
  [![CI](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml/badge.svg)](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml)
5
5
 
6
6
  Claude Swarm orchestrates multiple Claude Code instances as a collaborative AI development team. It enables running AI agents with specialized roles, tools, and directory contexts, communicating via MCP (Model Context Protocol) in a tree-like hierarchy. Define your swarm topology in simple YAML and let Claude instances delegate tasks through connected instances. Perfect for complex projects requiring specialized AI agents for frontend, backend, testing, DevOps, or research tasks.
@@ -80,7 +80,7 @@ claude-swarm --vibe # That will allow ALL tools for all instances! Be Careful!
80
80
  This will:
81
81
  - Launch the main instance (lead) with connections to other instances
82
82
  - The lead instance can communicate with the other instances via MCP
83
- - All session files are stored in `.claude-swarm/sessions/{timestamp}/`
83
+ - All session files are stored in `~/.claude-swarm/sessions/{project}/{timestamp}/` (customizable via `CLAUDE_SWARM_HOME`)
84
84
 
85
85
  #### Multi-Level Swarm Example
86
86
 
@@ -327,7 +327,7 @@ swarm:
327
327
  description: "Backend developer building APIs and services"
328
328
  directory: ./backend
329
329
  model: opus
330
- connections: [architect, database]
330
+ connections: [database]
331
331
  allowed_tools:
332
332
  - Edit
333
333
  - Write
@@ -446,6 +446,14 @@ claude-swarm --vibe
446
446
  claude-swarm -p "Implement the new user authentication feature"
447
447
  claude-swarm --prompt "Fix the bug in the payment module"
448
448
 
449
+ # Resume a previous session by ID
450
+ claude-swarm --session-id 20241206_143022
451
+ claude-swarm --session-id ~/path/to/session
452
+
453
+ # List available sessions
454
+ claude-swarm list-sessions
455
+ claude-swarm list-sessions --limit 20
456
+
449
457
  # Show version
450
458
  claude-swarm version
451
459
 
@@ -457,6 +465,70 @@ claude-swarm tools-mcp --allowed-tools 'Read,Edit' --disallowed-tools 'Edit(*.lo
457
465
  claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TIMESTAMP
458
466
  ```
459
467
 
468
+ ### Session Management and Restoration (Experimental)
469
+
470
+ Claude Swarm provides experimental session management with restoration capabilities. **Note: This feature is experimental and has limitations - the main instance's conversation context is not fully restored.**
471
+
472
+ #### Session Structure
473
+ All session files are organized in `~/.claude-swarm/sessions/{project}/{timestamp}/`:
474
+ - `config.yml`: Copy of the original swarm configuration
475
+ - `state/`: Directory containing individual instance states
476
+ - `{instance_id}.json`: Claude session ID and status for each instance (e.g., `lead_abc123.json`)
477
+ - `{instance_name}.mcp.json`: MCP configuration files
478
+ - `session.log`: Human-readable request/response tracking
479
+ - `session.log.json`: All events in JSONL format (one JSON per line)
480
+ - `permissions.log`: Permission checks and decisions
481
+
482
+ #### Listing Sessions
483
+ View your previous Claude Swarm sessions:
484
+
485
+ ```bash
486
+ # List recent sessions (default: 10)
487
+ claude-swarm list-sessions
488
+
489
+ # List more sessions
490
+ claude-swarm list-sessions --limit 20
491
+ ```
492
+
493
+ Output shows:
494
+ - Session ID (timestamp)
495
+ - Creation time
496
+ - Main instance name
497
+ - Number of instances
498
+ - Configuration file used
499
+ - Full session path
500
+
501
+ #### Resuming Sessions
502
+ Resume a previous session with all instances restored to their Claude session states:
503
+
504
+ ```bash
505
+ # Resume by session ID
506
+ claude-swarm --session-id 20241206_143022
507
+
508
+ # Resume by full path
509
+ claude-swarm --session-id ~/.claude-swarm/sessions/my-project/20241206_143022
510
+ ```
511
+
512
+ This will:
513
+ 1. Load the session manifest and instance states
514
+ 2. Restore the original swarm configuration
515
+ 3. Resume the main instance with its Claude session ID
516
+ 4. Restore all connected instances with their session IDs
517
+ 5. Maintain the same working directories and tool permissions
518
+
519
+ #### How Session Restoration Works
520
+ - Each instance's Claude session ID is automatically captured and persisted
521
+ - Instance states are stored in separate files named by instance ID to prevent concurrency issues
522
+ - MCP configurations are regenerated with the saved session IDs
523
+ - The main instance uses Claude's `--resume` flag (limited effectiveness)
524
+ - Connected instances receive their session IDs via `--claude-session-id`
525
+
526
+ **Important Limitations:**
527
+ - The main instance's conversation history and context are not fully restored
528
+ - Only the session ID is preserved, not the actual conversation state
529
+ - Connected instances restore more reliably than the main instance
530
+ - This is an experimental feature and may not work as expected
531
+
460
532
  ## How It Works
461
533
 
462
534
  1. **Configuration Parsing**: Claude Swarm reads your YAML configuration and validates it
@@ -471,20 +543,17 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
471
543
  - Eliminates the need to manually accept each tool or use global `--vibe` mode
472
544
  - Per-instance `vibe: true` skips all permission checks for that specific instance
473
545
  - The permission MCP uses `--permission-prompt-tool` to check tool access
474
- - Permission decisions are logged to `.claude-swarm/sessions/{timestamp}/permissions.log`
475
- 4. **Session Management**: Claude Swarm maintains session continuity:
476
- - Generates a shared session timestamp for all instances
477
- - Each instance can maintain its own Claude session ID
478
- - Sessions can be reset via the MCP server interface
546
+ - Permission decisions are logged to `~/.claude-swarm/sessions/{project}/{timestamp}/permissions.log`
547
+ 4. **Session Persistence**: Claude Swarm automatically tracks session state:
548
+ - Generates a shared session path for all instances
549
+ - Each instance's Claude session ID is captured and saved
550
+ - Instance states are stored using instance IDs as filenames to avoid conflicts
551
+ - Sessions can be fully restored with all instances reconnected
479
552
  5. **Main Instance Launch**: The main instance is launched with its MCP configuration, giving it access to all connected instances
480
553
  6. **Inter-Instance Communication**: Connected instances expose themselves as MCP servers with these tools:
481
554
  - **task**: Execute tasks using Claude Code with configurable tools and return results. The tool description includes the instance name and description (e.g., "Execute a task using Agent frontend_dev. Frontend developer specializing in React and TypeScript")
482
555
  - **session_info**: Get current Claude session information including ID and working directory
483
556
  - **reset_session**: Reset the Claude session for a fresh start
484
- 7. **Session Management**: All session files are organized in `.claude-swarm/sessions/{timestamp}/`:
485
- - MCP configuration files: `{instance_name}.mcp.json`
486
- - Session log: `session.log` with detailed request/response tracking
487
- - Permission log: `permissions.log` with all permission checks and decisions
488
557
 
489
558
  ## Troubleshooting
490
559
 
@@ -507,14 +576,15 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
507
576
  ### Debug Output
508
577
 
509
578
  The swarm will display:
510
- - Session directory location (`.claude-swarm/sessions/{timestamp}/`)
579
+ - Session directory location (`~/.claude-swarm/sessions/{project}/{timestamp}/`)
511
580
  - Main instance details (model, directory, tools, connections)
512
581
  - The exact command being run
513
582
 
514
583
  ### Session Files
515
584
 
516
- Check the session directory `.claude-swarm/sessions/{timestamp}/` for:
517
- - `session.log`: Detailed logs with request/response tracking
585
+ Check the session directory `~/.claude-swarm/sessions/{project}/{timestamp}/` for:
586
+ - `session.log`: Human-readable logs with request/response tracking
587
+ - `session.log.json`: All events in JSONL format (one JSON object per line)
518
588
  - `{instance}.mcp.json`: MCP configuration for each instance
519
589
  - All files for a session are kept together for easy review
520
590
 
data/claude-swarm.yml ADDED
@@ -0,0 +1,42 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Swarm Name"
4
+ main: lead_developer
5
+ instances:
6
+ lead_developer:
7
+ description: "Lead developer who coordinates the team and makes architectural decisions"
8
+ directory: .
9
+ model: sonnet
10
+ prompt: "You are the lead developer coordinating the team"
11
+ allowed_tools: [Read, Edit, Bash, Write]
12
+ connections: [frontend_dev, backend_dev]
13
+
14
+ # Example instances (uncomment and modify as needed):
15
+
16
+ frontend_dev:
17
+ description: "Frontend developer specializing in React and modern web technologies"
18
+ directory: .
19
+ model: sonnet
20
+ prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
21
+ allowed_tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
22
+
23
+ backend_dev:
24
+ description: "Backend developer focusing on APIs, databases, and server architecture"
25
+ directory: .
26
+ model: sonnet
27
+ prompt: "You specialize in backend development, APIs, databases, and server architecture"
28
+ allowed_tools: [Read, Edit, Write, Bash]
29
+
30
+ # devops_engineer:
31
+ # description: "DevOps engineer managing infrastructure, CI/CD, and deployments"
32
+ # directory: .
33
+ # model: sonnet
34
+ # prompt: "You specialize in infrastructure, CI/CD, containerization, and deployment"
35
+ # allowed_tools: [Read, Edit, Write, "Bash(docker:*)", "Bash(kubectl:*)", "Bash(terraform:*)"]
36
+
37
+ # qa_engineer:
38
+ # description: "QA engineer ensuring quality through comprehensive testing"
39
+ # directory: ./tests
40
+ # model: sonnet
41
+ # prompt: "You specialize in testing, quality assurance, and test automation"
42
+ # allowed_tools: [Read, Edit, Write, Bash]
@@ -0,0 +1,19 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Session Restoration Demo"
4
+ main: coordinator
5
+ instances:
6
+ coordinator:
7
+ description: "Main coordinator that manages the team"
8
+ directory: .
9
+ model: sonnet
10
+ prompt: "You are the team coordinator. You help manage tasks and coordinate between team members."
11
+ allowed_tools: [Read, Edit, Bash]
12
+ connections: [developer]
13
+
14
+ developer:
15
+ description: "Developer who writes code"
16
+ directory: .
17
+ model: sonnet
18
+ prompt: "You are a skilled developer who writes clean, well-tested code."
19
+ allowed_tools: [Read, Edit, Write, Bash]
@@ -4,23 +4,25 @@ require "json"
4
4
  require "open3"
5
5
  require "logger"
6
6
  require "fileutils"
7
+ require_relative "session_path"
7
8
 
8
9
  module ClaudeSwarm
9
10
  class ClaudeCodeExecutor
10
- SWARM_DIR = ".claude-swarm"
11
- SESSIONS_DIR = "sessions"
11
+ attr_reader :session_id, :last_response, :working_directory, :logger, :session_path
12
12
 
13
- attr_reader :session_id, :last_response, :working_directory, :logger, :session_timestamp
14
-
15
- def initialize(working_directory: Dir.pwd, model: nil, mcp_config: nil, vibe: false, instance_name: nil, calling_instance: nil)
13
+ def initialize(working_directory: Dir.pwd, model: nil, mcp_config: nil, vibe: false,
14
+ instance_name: nil, instance_id: nil, calling_instance: nil, calling_instance_id: nil,
15
+ claude_session_id: nil)
16
16
  @working_directory = working_directory
17
17
  @model = model
18
18
  @mcp_config = mcp_config
19
19
  @vibe = vibe
20
- @session_id = nil
20
+ @session_id = claude_session_id
21
21
  @last_response = nil
22
22
  @instance_name = instance_name
23
+ @instance_id = instance_id
23
24
  @calling_instance = calling_instance
25
+ @calling_instance_id = calling_instance_id
24
26
 
25
27
  # Setup logging
26
28
  setup_logging
@@ -53,7 +55,10 @@ module ClaudeSwarm
53
55
  log_streaming_event(json_data)
54
56
 
55
57
  # Capture session_id from system init
56
- @session_id = json_data["session_id"] if json_data["type"] == "system" && json_data["subtype"] == "init"
58
+ if json_data["type"] == "system" && json_data["subtype"] == "init"
59
+ @session_id = json_data["session_id"]
60
+ write_instance_state
61
+ end
57
62
 
58
63
  # Capture the final result
59
64
  result_response = json_data if json_data["type"] == "result"
@@ -94,18 +99,35 @@ module ClaudeSwarm
94
99
 
95
100
  private
96
101
 
97
- def setup_logging
98
- # Use environment variable for session timestamp if available (set by orchestrator)
99
- # Otherwise create a new timestamp
100
- @session_timestamp = ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
102
+ def write_instance_state
103
+ return unless @instance_id && @session_id
104
+
105
+ state_dir = File.join(@session_path, "state")
106
+ FileUtils.mkdir_p(state_dir)
107
+
108
+ state_file = File.join(state_dir, "#{@instance_id}.json")
109
+ state_data = {
110
+ instance_name: @instance_name,
111
+ instance_id: @instance_id,
112
+ claude_session_id: @session_id,
113
+ status: "active",
114
+ updated_at: Time.now.iso8601
115
+ }
101
116
 
102
- # Ensure the session directory exists
103
- session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, @session_timestamp)
104
- FileUtils.mkdir_p(session_dir)
117
+ File.write(state_file, JSON.pretty_generate(state_data))
118
+ @logger.info("Wrote instance state for #{@instance_name} (#{@instance_id}) with session ID: #{@session_id}")
119
+ rescue StandardError => e
120
+ @logger.error("Failed to write instance state for #{@instance_name} (#{@instance_id}): #{e.message}")
121
+ end
122
+
123
+ def setup_logging
124
+ # Use session path from environment (required)
125
+ @session_path = SessionPath.from_env
126
+ SessionPath.ensure_directory(@session_path)
105
127
 
106
128
  # Create logger with session.log filename
107
129
  log_filename = "session.log"
108
- log_path = File.join(session_dir, log_filename)
130
+ log_path = File.join(@session_path, log_filename)
109
131
  @logger = Logger.new(log_path)
110
132
  @logger.level = Logger::INFO
111
133
 
@@ -114,20 +136,47 @@ module ClaudeSwarm
114
136
  "[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
115
137
  end
116
138
 
117
- @logger.info("Started Claude Code executor for instance: #{@instance_name}") if @instance_name
139
+ return unless @instance_name
140
+
141
+ instance_info = @instance_name
142
+ instance_info += " (#{@instance_id})" if @instance_id
143
+ @logger.info("Started Claude Code executor for instance: #{instance_info}")
118
144
  end
119
145
 
120
146
  def log_request(prompt)
121
- @logger.info("#{@calling_instance} -> #{@instance_name}: \n---\n#{prompt}\n---")
147
+ caller_info = @calling_instance
148
+ caller_info += " (#{@calling_instance_id})" if @calling_instance_id
149
+ instance_info = @instance_name
150
+ instance_info += " (#{@instance_id})" if @instance_id
151
+ @logger.info("#{caller_info} -> #{instance_info}: \n---\n#{prompt}\n---")
152
+
153
+ # Build event hash for JSON logging
154
+ event = {
155
+ type: "request",
156
+ from_instance: @calling_instance,
157
+ from_instance_id: @calling_instance_id,
158
+ to_instance: @instance_name,
159
+ to_instance_id: @instance_id,
160
+ prompt: prompt,
161
+ timestamp: Time.now.iso8601
162
+ }
163
+
164
+ append_to_session_json(event)
122
165
  end
123
166
 
124
167
  def log_response(response)
168
+ caller_info = @calling_instance
169
+ caller_info += " (#{@calling_instance_id})" if @calling_instance_id
170
+ instance_info = @instance_name
171
+ instance_info += " (#{@instance_id})" if @instance_id
125
172
  @logger.info(
126
- "($#{response["total_cost"]} - #{response["duration_ms"]}ms) #{@instance_name} -> #{@calling_instance}: \n---\n#{response["result"]}\n---"
173
+ "($#{response["total_cost"]} - #{response["duration_ms"]}ms) #{instance_info} -> #{caller_info}: \n---\n#{response["result"]}\n---"
127
174
  )
128
175
  end
129
176
 
130
177
  def log_streaming_event(event)
178
+ append_to_session_json(event)
179
+
131
180
  return log_system_message(event) if event["type"] == "system"
132
181
 
133
182
  # Add specific details based on event type
@@ -155,14 +204,18 @@ module ClaudeSwarm
155
204
  arguments = tool_call["input"].to_json
156
205
  arguments = "#{arguments[0..300]} ...}" if arguments.length > 300
157
206
 
207
+ instance_info = @instance_name
208
+ instance_info += " (#{@instance_id})" if @instance_id
158
209
  @logger.info(
159
- "Tool call from #{@instance_name} -> Tool: #{tool_call["name"]}, ID: #{tool_call["id"]}, Arguments: #{arguments}"
210
+ "Tool call from #{instance_info} -> Tool: #{tool_call["name"]}, ID: #{tool_call["id"]}, Arguments: #{arguments}"
160
211
  )
161
212
  end
162
213
 
163
214
  text = content.select { |c| c["type"] == "text" }
164
215
  text.each do |t|
165
- @logger.info("#{@instance_name} is thinking:\n---\n#{t["text"]}\n---")
216
+ instance_info = @instance_name
217
+ instance_info += " (#{@instance_id})" if @instance_id
218
+ @logger.info("#{instance_info} is thinking:\n---\n#{t["text"]}\n---")
166
219
  end
167
220
  end
168
221
 
@@ -170,6 +223,34 @@ module ClaudeSwarm
170
223
  @logger.debug("USER: #{JSON.pretty_generate(content)}")
171
224
  end
172
225
 
226
+ def append_to_session_json(event)
227
+ json_filename = "session.log.json"
228
+ json_path = File.join(@session_path, json_filename)
229
+
230
+ # Use file locking to ensure thread-safe writes
231
+ File.open(json_path, File::WRONLY | File::APPEND | File::CREAT) do |file|
232
+ file.flock(File::LOCK_EX)
233
+
234
+ # Create entry with metadata
235
+ entry = {
236
+ instance: @instance_name,
237
+ instance_id: @instance_id,
238
+ calling_instance: @calling_instance,
239
+ calling_instance_id: @calling_instance_id,
240
+ timestamp: Time.now.iso8601,
241
+ event: event
242
+ }
243
+
244
+ # Write as single line JSON (JSONL format)
245
+ file.puts(entry.to_json)
246
+
247
+ file.flock(File::LOCK_UN)
248
+ end
249
+ rescue StandardError => e
250
+ @logger.error("Failed to append to session JSON: #{e.message}")
251
+ raise
252
+ end
253
+
173
254
  def build_command_array(prompt, options)
174
255
  cmd_array = ["claude"]
175
256
 
@@ -6,35 +6,47 @@ require_relative "claude_code_executor"
6
6
  require_relative "task_tool"
7
7
  require_relative "session_info_tool"
8
8
  require_relative "reset_session_tool"
9
+ require_relative "process_tracker"
9
10
 
10
11
  module ClaudeSwarm
11
12
  class ClaudeMcpServer
12
13
  # Class variables to share state with tool classes
13
14
  class << self
14
- attr_accessor :executor, :instance_config, :logger, :session_timestamp, :calling_instance
15
+ attr_accessor :executor, :instance_config, :logger, :session_path, :calling_instance, :calling_instance_id
15
16
  end
16
17
 
17
- def initialize(instance_config, calling_instance:)
18
+ def initialize(instance_config, calling_instance:, calling_instance_id: nil)
18
19
  @instance_config = instance_config
19
20
  @calling_instance = calling_instance
21
+ @calling_instance_id = calling_instance_id
20
22
  @executor = ClaudeCodeExecutor.new(
21
23
  working_directory: instance_config[:directory],
22
24
  model: instance_config[:model],
23
25
  mcp_config: instance_config[:mcp_config_path],
24
26
  vibe: instance_config[:vibe],
25
27
  instance_name: instance_config[:name],
26
- calling_instance: calling_instance
28
+ instance_id: instance_config[:instance_id],
29
+ calling_instance: calling_instance,
30
+ calling_instance_id: calling_instance_id,
31
+ claude_session_id: instance_config[:claude_session_id]
27
32
  )
28
33
 
29
34
  # Set class variables so tools can access them
30
35
  self.class.executor = @executor
31
36
  self.class.instance_config = @instance_config
32
37
  self.class.logger = @executor.logger
33
- self.class.session_timestamp = @executor.session_timestamp
38
+ self.class.session_path = @executor.session_path
34
39
  self.class.calling_instance = @calling_instance
40
+ self.class.calling_instance_id = @calling_instance_id
35
41
  end
36
42
 
37
43
  def start
44
+ # Track this process
45
+ if @executor.session_path && File.exist?(@executor.session_path)
46
+ tracker = ProcessTracker.new(@executor.session_path)
47
+ tracker.track_pid(Process.pid, "mcp_#{@instance_config[:name]}")
48
+ end
49
+
38
50
  server = FastMcp::Server.new(
39
51
  name: @instance_config[:name],
40
52
  version: "1.0.0"