claude_swarm 0.1.20 → 0.2.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -66
  3. data/.rubocop_todo.yml +11 -0
  4. data/CHANGELOG.md +93 -0
  5. data/CLAUDE.md +61 -0
  6. data/README.md +172 -15
  7. data/Rakefile +1 -1
  8. data/examples/mixed-provider-swarm.yml +23 -0
  9. data/lib/claude_swarm/claude_code_executor.rb +7 -12
  10. data/lib/claude_swarm/claude_mcp_server.rb +26 -12
  11. data/lib/claude_swarm/cli.rb +293 -165
  12. data/lib/claude_swarm/commands/ps.rb +22 -24
  13. data/lib/claude_swarm/commands/show.rb +45 -63
  14. data/lib/claude_swarm/configuration.rb +137 -8
  15. data/lib/claude_swarm/mcp_generator.rb +39 -14
  16. data/lib/claude_swarm/openai/chat_completion.rb +264 -0
  17. data/lib/claude_swarm/openai/executor.rb +301 -0
  18. data/lib/claude_swarm/openai/responses.rb +338 -0
  19. data/lib/claude_swarm/orchestrator.rb +205 -39
  20. data/lib/claude_swarm/process_tracker.rb +7 -7
  21. data/lib/claude_swarm/session_cost_calculator.rb +93 -0
  22. data/lib/claude_swarm/session_path.rb +3 -5
  23. data/lib/claude_swarm/system_utils.rb +1 -3
  24. data/lib/claude_swarm/tools/reset_session_tool.rb +24 -0
  25. data/lib/claude_swarm/tools/session_info_tool.rb +24 -0
  26. data/lib/claude_swarm/tools/task_tool.rb +43 -0
  27. data/lib/claude_swarm/version.rb +1 -1
  28. data/lib/claude_swarm/worktree_manager.rb +13 -20
  29. data/lib/claude_swarm.rb +23 -10
  30. data/single.yml +482 -6
  31. metadata +50 -16
  32. data/claude-swarm.yml +0 -64
  33. data/lib/claude_swarm/reset_session_tool.rb +0 -22
  34. data/lib/claude_swarm/session_info_tool.rb +0 -22
  35. data/lib/claude_swarm/task_tool.rb +0 -39
  36. /data/{example → examples}/claude-swarm.yml +0 -0
  37. /data/{example → examples}/microservices-team.yml +0 -0
  38. /data/{example → examples}/session-restoration-demo.yml +0 -0
  39. /data/{example → examples}/test-generation.yml +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdad008c72dacb5ca491dc7c6b912c4bc8d46c7f2b578b3762ed7b5e09b4e3f4
4
- data.tar.gz: a426057ff0bb859cd56a0bec71e80d02c1232fe6eb69a74e47ed3044f7121a58
3
+ metadata.gz: 4085d9345464dcd7fc7be4c78155aa03f2467f7f089b056f2be0589f5de260f2
4
+ data.tar.gz: fda22e57619c30b1d9e1efcabb5a8a0b2b74f9f6df2d969944e07d27753adbd9
5
5
  SHA512:
6
- metadata.gz: b7fb86ea647403073665b860dde694ef2ae0c983f8fc011189d999f97d57e8b80f552b9e736b1f46cb888ac3c4877666dac060e1335db39bd2719f120ad35937
7
- data.tar.gz: f1670e4c35e9681054dbe5ab2bcc87d7ef24a6ad4f33a21d3e445c32cb1e917eedbc2984c483b06750469fa08e8256109693a121f337ff813a8ff1944ecc42f1
6
+ metadata.gz: 216ff3a7bfda3f2763195eec1e32821596d7047c155d0757d33da0718ad1dbb3c51f04fe50591c2a99d98da37d53255a9bda11c9bf9b6bf79dff788ac27fc0c9
7
+ data.tar.gz: 39784e3be63360c32ec3388983a10068f9d8193bde6cebef190aae856a2da9caa47cb63c772e268203a85eb8a8fad2e0717a901401fffbd0ce8e716979cd3f20
data/.rubocop.yml CHANGED
@@ -1,71 +1,14 @@
1
- AllCops:
2
- TargetRubyVersion: 3.4
3
- NewCops: enable
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ inherit_gem:
4
+ rubocop-shopify: rubocop.yml
4
5
 
5
6
  plugins:
6
7
  - rubocop-minitest
7
8
  - rubocop-rake
8
9
 
9
-
10
- Style/StringLiterals:
11
- EnforcedStyle: double_quotes
12
-
13
- Style/StringLiteralsInInterpolation:
14
- EnforcedStyle: double_quotes
15
-
16
- # Disable documentation for internal classes
17
- Style/Documentation:
18
- Enabled: false
19
-
20
- # Allow slightly longer methods
21
- Metrics/MethodLength:
22
- Enabled: false
23
-
24
- # Disable gemspec specific cops
25
- Gemspec/RequireMFA:
26
- Enabled: false
27
-
28
- Gemspec/RequiredRubyVersion:
29
- Enabled: false
30
-
31
- Gemspec/DevelopmentDependencies:
32
- Enabled: false
33
-
34
- Naming/AccessorMethodName:
35
- Enabled: false
36
-
37
- Metrics/BlockLength:
38
- Enabled: false
39
-
40
- Metrics/BlockNesting:
41
- Enabled: false
42
-
43
- Metrics/ClassLength:
44
- Enabled: false
45
-
46
- Metrics/PerceivedComplexity:
47
- Enabled: false
48
-
49
- Metrics/CyclomaticComplexity:
50
- Enabled: false
51
-
52
- Metrics/AbcSize:
53
- Enabled: false
54
-
55
- Naming/PredicateName:
56
- Enabled: false
57
-
58
- Layout/LineLength:
59
- Max: 150
60
-
61
- Metrics/ModuleLength:
62
- Enabled: false
63
-
64
- Minitest/MultipleAssertions:
65
- Enabled: false
66
-
67
- Metrics/ParameterLists:
68
- Enabled: false
69
-
70
- Style/PerlBackrefs:
71
- Enabled: false
10
+ AllCops:
11
+ TargetRubyVersion: 3.4
12
+ NewCops: enable
13
+ Exclude:
14
+ - '.ruby-lsp/**/*'
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,11 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2025-07-02 11:28:10 UTC using RuboCop version 1.77.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 34
10
+ Minitest/MultipleAssertions:
11
+ Max: 23
data/CHANGELOG.md CHANGED
@@ -1,3 +1,96 @@
1
+ ## [0.2.0]
2
+
3
+ ### Added
4
+ - **After commands support**: Added `after` field to swarm configuration for executing cleanup commands
5
+ - Commands run after Claude exits but before cleanup processes
6
+ - Execute in the main instance's directory (including worktree if enabled)
7
+ - Run even when interrupted by signals (Ctrl+C)
8
+ - Failures in after commands do not prevent cleanup from proceeding
9
+ - Not executed during session restoration
10
+ - Example: `after: ["docker-compose down", "rm -rf temp/*"]`
11
+
12
+ ### Changed
13
+ - **Session restoration command**: Session restoration now uses a dedicated `restore` command instead of the `--session-id` flag
14
+ - Previous: `claude-swarm start --session-id SESSION_ID`
15
+ - New: `claude-swarm restore SESSION_ID`
16
+ - More intuitive command structure following standard CLI patterns
17
+ - **Removed redundant -c flag**: The `-c/--config` option has been removed from the `start` command
18
+ - Config file can still be specified as a positional argument: `claude-swarm start my-config.yml`
19
+ - Default remains `claude-swarm.yml` when no file is specified
20
+ - **Session ID format**: Session IDs now use UUIDs instead of timestamp format
21
+ - Previous format: `YYYYMMDD_HHMMSS` (e.g., `20250707_181341`)
22
+ - New format: UUID v4 (e.g., `550e8400-e29b-41d4-a716-446655440000`)
23
+ - Provides globally unique identifiers suitable for external application integration
24
+ - Sessions remain sorted by file creation time, not by ID
25
+ - **BREAKING CHANGE: Before commands now execute in main instance directory**: The `before` commands specified in swarm configuration now run after changing to the main instance's directory (including worktrees when enabled), rather than in the original working directory
26
+ - This ensures commands like `npm install` or `bundle install` affect only the isolated worktree
27
+ - Makes behavior more intuitive and consistent with user expectations
28
+ - Existing swarms relying on before commands running in the original directory will need to be updated
29
+ - **Custom session ID support**: Added `--session-id` option to the `start` command to allow users to specify their own session ID
30
+ - Use `claude-swarm start --session-id my-custom-id` to use a custom session ID
31
+ - If not provided, a UUID is generated automatically
32
+ - Useful for integrating with external systems that need predictable session identifiers
33
+ - **Environment variable interpolation in configuration**: Claude Swarm now supports environment variable interpolation in all YAML configuration values
34
+ - Use `${ENV_VAR_NAME}` syntax to reference environment variables
35
+ - Variables are interpolated recursively in strings, arrays, and nested structures
36
+ - Fails with clear error message if referenced environment variable is not set
37
+ - Supports multiple variables in the same string and partial string interpolation
38
+ - **Environment variable defaults**: Environment variables in configuration now support default values
39
+ - Use `${ENV_VAR_NAME:=default_value}` syntax to provide defaults
40
+ - Default values are used when the environment variable is not set
41
+ - Supports any string as default value, including spaces and special characters
42
+ - Examples: `${DB_PORT:=5432}`, `${API_URL:=https://api.example.com}`
43
+ - **Session path display in show command**: The `claude-swarm show SESSION_ID` command now displays the session path for easier access to session files
44
+ - **Main process PID tracking**: The orchestrator now writes its process ID to `SESSION_PATH/main_pid` for external monitoring and management
45
+ - **Swarm execution summary**: Display runtime duration and total cost at the end of each swarm run [@claudenm]
46
+ - Shows total execution time in hours, minutes, and seconds format
47
+ - Calculates and displays aggregate cost across all instances
48
+ - Indicates when main instance cost is excluded (e.g., for interactive sessions)
49
+ - Session metadata now includes end time and duration in seconds
50
+
51
+ - **Session cost calculator**: New `SessionCostCalculator` class for aggregating costs from session logs [@claudenm]
52
+ - Processes session.log.json files to calculate total usage costs
53
+ - Tracks which instances have cost data available
54
+
55
+ - **Session cost calculator**: New `SessionCostCalculator` class for aggregating costs from session logs
56
+ - Processes session.log.json files to calculate total usage costs
57
+ - Tracks which instances have cost data available
58
+
59
+ - **OpenAI provider support**: Claude Swarm now supports OpenAI models as an alternative provider
60
+ - Instances can specify `provider: openai` in configuration (default remains "claude")
61
+ - Full MCP tool support for OpenAI instances via automatic conversion
62
+ - Mixed provider swarms allow Claude and OpenAI instances to collaborate
63
+ - OpenAI instances only work with `vibe: true` at the moment. There's no way to set allowed/disallowed tools.
64
+
65
+ - **Dual OpenAI API support**: Two API versions available for different use cases
66
+ - Chat Completion API (`api_version: "chat_completion"`) - Traditional format with tool calling
67
+ - Responses API (`api_version: "responses"`) - New structured format with function calls
68
+ - Both APIs support recursive tool execution with proper conversation tracking
69
+
70
+ - **OpenAI-specific configuration options**:
71
+ - `temperature`: Control response randomness (default: 0.3)
72
+ - `api_version`: Choose between "chat_completion" or "responses"
73
+ - `openai_token_env`: Custom environment variable for API key (default: "OPENAI_API_KEY")
74
+ - `base_url`: Support for OpenAI-compatible endpoints and proxies
75
+
76
+ - **Enhanced debugging and logging**:
77
+ - Detailed error response logging for OpenAI API calls
78
+ - Conversation flow tracking with IDs for debugging
79
+ - Full request/response logging in session JSONL files
80
+
81
+ ### Changed
82
+ - Configuration validation now enforces provider-specific fields
83
+ - Example configurations moved from `example/` to `examples/` directory
84
+ - Added `mixed-provider-swarm.yml` example demonstrating Claude-OpenAI collaboration
85
+ - Session metadata now includes `start_time`, `end_time`, and `duration_seconds` fields [@claudenm]
86
+ - Updated `ps` and `show` commands to use the new cost calculation functionality [@claudenm]
87
+
88
+
89
+ ### Internal
90
+ - New classes: `OpenAIExecutor`, `OpenAIChatCompletion`, `OpenAIResponses`
91
+ - Comprehensive test coverage for OpenAI provider functionality
92
+ - MCP generator enhanced to support provider-specific configurations
93
+
1
94
  ## [0.1.20]
2
95
 
3
96
  ### Added
data/CLAUDE.md CHANGED
@@ -18,6 +18,23 @@ bin/setup # Install dependencies
18
18
  rake test # Run the Minitest test suite
19
19
  ```
20
20
 
21
+ **Important**: Tests should not generate any output to stdout or stderr. When writing tests:
22
+ - Capture or suppress all stdout/stderr output from tested methods
23
+ - Use `capture_io` or `capture_subprocess_io` for Minitest
24
+ - Redirect output streams to `StringIO` or `/dev/null` when necessary
25
+ - Mock or stub methods that produce console output
26
+ - Ensure clean test output for better CI/CD integration
27
+
28
+ Example:
29
+ ```ruby
30
+ def test_command_with_output
31
+ output, err = capture_io do
32
+ # Code that produces output
33
+ end
34
+ # Test assertions here
35
+ end
36
+ ```
37
+
21
38
  ### Linting
22
39
  ```bash
23
40
  rake rubocop -A # Run RuboCop linter to auto fix problems
@@ -152,3 +169,47 @@ The gem includes comprehensive tests covering:
152
169
 
153
170
  - **thor** (~> 1.3): Command-line interface framework
154
171
  - **yaml**: Built-in Ruby YAML parser (no explicit dependency needed)
172
+
173
+ ## Zeitwerk Autoloading
174
+
175
+ This project uses Zeitwerk for automatic class loading. Important guidelines:
176
+
177
+ ### Require Statement Rules
178
+
179
+ 1. **DO NOT include any require statements for lib files**: Zeitwerk automatically loads all classes under `lib/claude_swarm/`. Never use `require`, `require_relative`, or `require "claude_swarm/..."` for internal project files.
180
+
181
+ 2. **All dependencies must be consolidated in lib/claude_swarm.rb**: Both standard library and external gem dependencies are required at the top of `lib/claude_swarm.rb`. This includes:
182
+ - Standard library dependencies (json, yaml, fileutils, etc.)
183
+ - External gem dependencies (thor, openai, mcp_client, fast_mcp_annotations)
184
+
185
+ 3. **No requires in other lib files**: Individual files in `lib/claude_swarm/` should not have any require statements. They rely on:
186
+ - Dependencies loaded in `lib/claude_swarm.rb`
187
+ - Other classes autoloaded by Zeitwerk
188
+
189
+ ### Example
190
+
191
+ ```ruby
192
+ # ✅ CORRECT - lib/claude_swarm.rb
193
+ # Standard library dependencies
194
+ require "json"
195
+ require "yaml"
196
+ require "fileutils"
197
+ # ... other standard libraries
198
+
199
+ # External dependencies
200
+ require "thor"
201
+ require "openai"
202
+ # ... other gems
203
+
204
+ # Zeitwerk setup
205
+ require "zeitwerk"
206
+ loader = Zeitwerk::Loader.for_gem
207
+ loader.setup
208
+
209
+ # ❌ INCORRECT - lib/claude_swarm/some_class.rb
210
+ require "json" # Don't do this!
211
+ require_relative "other_class" # Don't do this!
212
+ require "claude_swarm/configuration" # Don't do this!
213
+ ```
214
+
215
+ This approach ensures clean dependency management and leverages Ruby's modern autoloading capabilities.
data/README.md CHANGED
@@ -221,10 +221,70 @@ swarm:
221
221
  - "echo 'Setting up environment...'"
222
222
  - "npm install"
223
223
  - "docker-compose up -d"
224
+ after: # Optional: commands to run after exiting the swarm
225
+ - "echo 'Cleaning up environment...'"
226
+ - "docker-compose down"
224
227
  instances:
225
228
  # Instance definitions...
226
229
  ```
227
230
 
231
+ #### Environment Variable Interpolation
232
+
233
+ Claude Swarm supports environment variable interpolation in all configuration values using the `${ENV_VAR_NAME}` syntax:
234
+
235
+ ```yaml
236
+ version: 1
237
+ swarm:
238
+ name: "${APP_NAME} Development Team"
239
+ main: lead
240
+ instances:
241
+ lead:
242
+ description: "Lead developer for ${APP_NAME}"
243
+ directory: "${PROJECT_ROOT}"
244
+ model: "${CLAUDE_MODEL}"
245
+ prompt: "You are developing ${APP_NAME} version ${APP_VERSION}"
246
+ allowed_tools: ["${TOOL_1}", "${TOOL_2}", "Bash"]
247
+ mcps:
248
+ - name: github
249
+ type: stdio
250
+ command: "${MCP_GITHUB_PATH}"
251
+ env:
252
+ GITHUB_TOKEN: "${GITHUB_TOKEN}"
253
+ ```
254
+
255
+ Features:
256
+ - Variables are interpolated recursively in strings, arrays, and nested structures
257
+ - Multiple variables can be used in the same string
258
+ - Partial string interpolation is supported (e.g., `"prefix-${VAR}-suffix"`)
259
+ - If a referenced environment variable is not set, Claude Swarm will exit with a clear error message
260
+ - Non-matching patterns like `$VAR` or `{VAR}` are preserved as-is
261
+
262
+ ##### Default Values
263
+
264
+ Environment variables can have default values using the `${VAR_NAME:=default_value}` syntax:
265
+
266
+ ```yaml
267
+ version: 1
268
+ swarm:
269
+ name: "Dev Team"
270
+ main: lead
271
+ instances:
272
+ lead:
273
+ description: "Lead developer"
274
+ directory: "${PROJECT_DIR:=.}"
275
+ model: "${CLAUDE_MODEL:=sonnet}"
276
+ mcps:
277
+ - name: api-server
278
+ type: sse
279
+ url: "${API_URL:=http://localhost:8080}"
280
+ worktree: "${BRANCH_NAME:=feature-branch}"
281
+ ```
282
+
283
+ - Default values are used when the environment variable is not set
284
+ - Any string can be used as a default value, including spaces and special characters
285
+ - Empty defaults are supported: `${VAR:=}` results in an empty string if VAR is not set
286
+ - Works in all configuration values: strings, arrays, and nested structures
287
+
228
288
  #### Instance Configuration
229
289
 
230
290
  Each instance must have:
@@ -242,6 +302,24 @@ Each instance can have:
242
302
  - **prompt**: Custom system prompt to append to the instance
243
303
  - **vibe**: Enable vibe mode (--dangerously-skip-permissions) for this instance (default: false)
244
304
  - **worktree**: Configure Git worktree usage for this instance (true/false/string)
305
+ - **provider**: AI provider to use - "claude" (default) or "openai"
306
+
307
+ #### OpenAI Provider Configuration
308
+
309
+ When using `provider: openai`, the following additional fields are available:
310
+
311
+ - **temperature**: Temperature for GPT models only (0.0-2.0). Not supported for O-series reasoning models (o1, o1-preview, o1-mini, o3, etc.)
312
+ - **api_version**: API version to use - "chat_completion" (default) or "responses"
313
+ - **openai_token_env**: Environment variable name for OpenAI API key (default: "OPENAI_API_KEY")
314
+ - **base_url**: Custom base URL for OpenAI API (optional)
315
+ - **reasoning_effort**: Reasoning effort for O-series models only - "low", "medium", or "high"
316
+
317
+ **Important Notes:**
318
+ - O-series models (o1, o1-preview, o1-mini, o3, etc.) use deterministic reasoning and do not support the `temperature` parameter
319
+ - The `reasoning_effort` parameter is only supported by O-series models
320
+ - GPT models support `temperature` but not `reasoning_effort`
321
+ - OpenAI instances default to and ONLY operate as `vibe: true` and use MCP for tool access
322
+ - By default it comes with Claude Code tools, connected with MCP to `claude mcp serve`
245
323
 
246
324
  ```yaml
247
325
  instance_name:
@@ -268,6 +346,27 @@ instance_name:
268
346
  args: ["arg1", "arg2"]
269
347
  env:
270
348
  VAR1: value1
349
+
350
+ # OpenAI instance examples
351
+
352
+ # GPT model with temperature
353
+ gpt_instance:
354
+ description: "OpenAI GPT-powered creative assistant"
355
+ provider: openai
356
+ model: gpt-4o
357
+ temperature: 0.7 # Supported for GPT models
358
+ api_version: chat_completion
359
+ openai_token_env: OPENAI_API_KEY
360
+ prompt: "You are a creative assistant specializing in content generation"
361
+
362
+ # O-series reasoning model with reasoning_effort
363
+ reasoning_instance:
364
+ description: "OpenAI O-series reasoning assistant"
365
+ provider: openai
366
+ model: o1-mini
367
+ reasoning_effort: medium # Only for O-series models
368
+ api_version: responses # Can use either API version
369
+ prompt: "You are a reasoning assistant for complex problem solving"
271
370
  ```
272
371
 
273
372
  ### MCP Server Types
@@ -459,9 +558,54 @@ When using multiple directories:
459
558
  - Additional directories are accessible via the `--add-dir` flag in Claude
460
559
  - All directories must exist or the configuration will fail validation
461
560
 
462
- #### Before Commands
561
+ #### Mixed AI Provider Team
562
+
563
+ Combine Claude and OpenAI instances in a single swarm:
564
+
565
+ ```yaml
566
+ version: 1
567
+ swarm:
568
+ name: "Mixed AI Development Team"
569
+ main: lead_developer
570
+ instances:
571
+ lead_developer:
572
+ description: "Claude lead developer coordinating the team"
573
+ directory: .
574
+ model: opus
575
+ connections: [creative_assistant, reasoning_expert, backend_dev]
576
+ prompt: "You are the lead developer coordinating a mixed AI team"
577
+ allowed_tools: [Read, Edit, Bash, Write]
578
+
579
+ creative_assistant:
580
+ description: "OpenAI-powered assistant for creative and UI/UX tasks"
581
+ provider: openai
582
+ model: gpt-4o
583
+ temperature: 0.7 # Supported for GPT models
584
+ directory: ./frontend
585
+ prompt: "You are a creative frontend developer specializing in UI/UX design"
586
+ # OpenAI instances default to vibe: true
587
+
588
+ reasoning_expert:
589
+ description: "OpenAI O-series model for complex problem solving"
590
+ provider: openai
591
+ model: o1-mini
592
+ reasoning_effort: high # For O-series models only
593
+ directory: ./architecture
594
+ prompt: "You solve complex architectural and algorithmic problems"
595
+
596
+ backend_dev:
597
+ description: "Claude backend developer for system architecture"
598
+ directory: ./backend
599
+ model: sonnet
600
+ prompt: "You specialize in backend development and system architecture"
601
+ allowed_tools: [Read, Edit, Write, Bash]
602
+ ```
603
+
604
+ Note: OpenAI instances require the API key to be set in the environment variable (default: `OPENAI_API_KEY`).
463
605
 
464
- You can specify commands to run before launching the swarm using the `before` field:
606
+ #### Before and After Commands
607
+
608
+ You can specify commands to run before launching and after exiting the swarm using the `before` and `after` fields:
465
609
 
466
610
  ```yaml
467
611
  version: 1
@@ -473,6 +617,10 @@ swarm:
473
617
  - "npm install"
474
618
  - "docker-compose up -d"
475
619
  - "bundle install"
620
+ after:
621
+ - "echo '🛑 Cleaning up development environment...'"
622
+ - "docker-compose down"
623
+ - "rm -rf temp/*"
476
624
  instances:
477
625
  lead_developer:
478
626
  description: "Lead developer coordinating the team"
@@ -483,16 +631,26 @@ swarm:
483
631
 
484
632
  The `before` commands:
485
633
  - Are executed in sequence before launching any Claude instances
634
+ - Execute in the main instance's directory (including worktree if enabled)
486
635
  - Must all succeed for the swarm to launch (exit code 0)
487
636
  - Are only executed on initial swarm launch, not when restoring sessions
488
637
  - Have their output logged to the session log file
489
638
  - Will abort the swarm launch if any command fails
490
639
 
640
+ The `after` commands:
641
+ - Are executed after Claude exits but before cleanup processes
642
+ - Execute in the main instance's directory (including worktree if enabled)
643
+ - Run even when the swarm is interrupted by signals (Ctrl+C)
644
+ - Failures do not prevent cleanup from proceeding
645
+ - Are only executed on initial swarm runs, not when restoring sessions
646
+ - Have their output logged to the session log file
647
+
491
648
  This is useful for:
492
- - Installing dependencies
649
+ - Installing dependencies in the isolated worktree environment
493
650
  - Starting required services (databases, Docker containers, etc.)
494
651
  - Setting up the development environment
495
652
  - Running any prerequisite setup scripts
653
+ - Ensuring setup commands affect only the working directory, not the original repository
496
654
 
497
655
 
498
656
  #### Mixed Permission Modes
@@ -598,8 +756,8 @@ swarm:
598
756
  claude-swarm
599
757
 
600
758
  # Specify a different configuration file
601
- claude-swarm --config my-swarm.yml
602
- claude-swarm -c team-config.yml
759
+ claude-swarm my-swarm.yml
760
+ claude-swarm team-config.yml
603
761
 
604
762
  # Run with --dangerously-skip-permissions for all instances
605
763
  claude-swarm --vibe
@@ -608,9 +766,11 @@ claude-swarm --vibe
608
766
  claude-swarm -p "Implement the new user authentication feature"
609
767
  claude-swarm --prompt "Fix the bug in the payment module"
610
768
 
611
- # Resume a previous session by ID
612
- claude-swarm --session-id 20241206_143022
613
- claude-swarm --session-id ~/path/to/session
769
+ # Use a custom session ID instead of auto-generated UUID
770
+ claude-swarm --session-id my-custom-session-123
771
+
772
+ # Stream logs to stdout in prompt mode
773
+ claude-swarm -p "Fix the tests" --stream-logs
614
774
 
615
775
  # Run all instances in Git worktrees
616
776
  claude-swarm --worktree # Auto-generated name (worktree-SESSION_ID)
@@ -671,7 +831,7 @@ Example output from `claude-swarm ps`:
671
831
  SESSION_ID SWARM_NAME TOTAL_COST UPTIME DIRECTORY
672
832
  -------------------------------------------------------------------------------
673
833
  20250617_235233 Feature Development $0.3847 15m .
674
- 20250617_143022 Bug Investigation $1.2156 1h ./shopify
834
+ 20250617_143022 Bug Investigation $1.2156 1h .
675
835
  20250617_091547 Multi-Module Dev $0.8932 30m ./frontend, ./backend, ./shared
676
836
  ```
677
837
 
@@ -734,11 +894,8 @@ Output shows:
734
894
  Resume a previous session with all instances restored to their Claude session states:
735
895
 
736
896
  ```bash
737
- # Resume by session ID
738
- claude-swarm --session-id 20250617_143022
739
-
740
- # Resume by full path
741
- claude-swarm --session-id ~/.claude-swarm/sessions/my-project/20250617_143022
897
+ # Restore using the session's UUID
898
+ claude-swarm restore 550e8400-e29b-41d4-a716-446655440000
742
899
  ```
743
900
 
744
901
  This will:
@@ -810,7 +967,7 @@ The swarm will display:
810
967
 
811
968
  ### Session Files
812
969
 
813
- Check the session directory `~/.claude-swarm/sessions/{project}/{timestamp}/` for:
970
+ Check the session directory `~/.claude-swarm/sessions/{project}/{session-id}/` for:
814
971
  - `session.log`: Human-readable logs with request/response tracking
815
972
  - `session.log.json`: All events in JSONL format (one JSON object per line)
816
973
  - `{instance}.mcp.json`: MCP configuration for each instance
data/Rakefile CHANGED
@@ -9,4 +9,4 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- task default: %i[test rubocop]
12
+ task default: [:test, :rubocop]
@@ -0,0 +1,23 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Mixed AI Team"
4
+ main: lead_developer
5
+ instances:
6
+ lead_developer:
7
+ description: "Claude lead developer coordinating the team"
8
+ directory: .
9
+ model: opus
10
+ prompt: "You are the lead developer coordinating a mixed AI team"
11
+ allowed_tools: [Read, Edit, Bash, Write]
12
+ connections: [openai_assistant]
13
+
14
+ openai_assistant:
15
+ description: "OpenAI-powered assistant for creative tasks"
16
+ directory: .
17
+ provider: openai
18
+ model: o3
19
+ api_version: responses
20
+ reasoning_effort: high # Optional: low, medium, or high
21
+ prompt: "You are a creative frontend developer using React and modern web technologies"
22
+ # OpenAI instances default to vibe: true
23
+
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "open3"
5
- require "logger"
6
- require "fileutils"
7
-
8
3
  module ClaudeSwarm
9
4
  class ClaudeCodeExecutor
10
5
  attr_reader :session_id, :last_response, :working_directory, :logger, :session_path
11
6
 
12
7
  def initialize(working_directory: Dir.pwd, model: nil, mcp_config: nil, vibe: false,
13
- instance_name: nil, instance_id: nil, calling_instance: nil, calling_instance_id: nil,
14
- claude_session_id: nil, additional_directories: [])
8
+ instance_name: nil, instance_id: nil, calling_instance: nil, calling_instance_id: nil,
9
+ claude_session_id: nil, additional_directories: [])
15
10
  @working_directory = working_directory
16
11
  @additional_directories = additional_directories
17
12
  @model = model
@@ -111,7 +106,7 @@ module ClaudeSwarm
111
106
  instance_id: @instance_id,
112
107
  claude_session_id: @session_id,
113
108
  status: "active",
114
- updated_at: Time.now.iso8601
109
+ updated_at: Time.now.iso8601,
115
110
  }
116
111
 
117
112
  File.write(state_file, JSON.pretty_generate(state_data))
@@ -158,7 +153,7 @@ module ClaudeSwarm
158
153
  to_instance: @instance_name,
159
154
  to_instance_id: @instance_id,
160
155
  prompt: prompt,
161
- timestamp: Time.now.iso8601
156
+ timestamp: Time.now.iso8601,
162
157
  }
163
158
 
164
159
  append_to_session_json(event)
@@ -170,7 +165,7 @@ module ClaudeSwarm
170
165
  instance_info = @instance_name
171
166
  instance_info += " (#{@instance_id})" if @instance_id
172
167
  @logger.info(
173
- "($#{response["total_cost"]} - #{response["duration_ms"]}ms) #{instance_info} -> #{caller_info}: \n---\n#{response["result"]}\n---"
168
+ "($#{response["total_cost"]} - #{response["duration_ms"]}ms) #{instance_info} -> #{caller_info}: \n---\n#{response["result"]}\n---",
174
169
  )
175
170
  end
176
171
 
@@ -207,7 +202,7 @@ module ClaudeSwarm
207
202
  instance_info = @instance_name
208
203
  instance_info += " (#{@instance_id})" if @instance_id
209
204
  @logger.info(
210
- "Tool call from #{instance_info} -> Tool: #{tool_call["name"]}, ID: #{tool_call["id"]}, Arguments: #{arguments}"
205
+ "Tool call from #{instance_info} -> Tool: #{tool_call["name"]}, ID: #{tool_call["id"]}, Arguments: #{arguments}",
211
206
  )
212
207
  end
213
208
 
@@ -238,7 +233,7 @@ module ClaudeSwarm
238
233
  calling_instance: @calling_instance,
239
234
  calling_instance_id: @calling_instance_id,
240
235
  timestamp: Time.now.iso8601,
241
- event: event
236
+ event: event,
242
237
  }
243
238
 
244
239
  # Write as single line JSON (JSONL format)