claude_swarm 0.1.20 → 0.2.1

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 +106 -0
  5. data/CLAUDE.md +61 -0
  6. data/README.md +174 -16
  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 +161 -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 +39 -22
  29. data/lib/claude_swarm.rb +23 -10
  30. data/single.yml +481 -6
  31. metadata +54 -14
  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: 4f802dadf6aa3673a354582aed75d8887d8ff83c4d30d700eba89f7f3663eec9
4
+ data.tar.gz: a2aea35f422333fd2421d078bf336745c61172c900ac96da1d20d50100fef607
5
5
  SHA512:
6
- metadata.gz: b7fb86ea647403073665b860dde694ef2ae0c983f8fc011189d999f97d57e8b80f552b9e736b1f46cb888ac3c4877666dac060e1335db39bd2719f120ad35937
7
- data.tar.gz: f1670e4c35e9681054dbe5ab2bcc87d7ef24a6ad4f33a21d3e445c32cb1e917eedbc2984c483b06750469fa08e8256109693a121f337ff813a8ff1944ecc42f1
6
+ metadata.gz: 7fb47e9059f561852a81411920dc0a0d568c5c47a2361823ee5425e3820ef21699c26072e1bf330fdfff4514b8fc3c182df59975266f3f62b5e89aeba31dd63a
7
+ data.tar.gz: dec84ec9eb8b0f95edb1d079fcc7ab155ed754713343f8730f68d248f28db1e9ad0571b734f885cbcc30fbda58a0cde077d5e7d18c960c5a9ad1e5c03df396a6
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,109 @@
1
+ ## [0.2.1]
2
+
3
+ ### Added
4
+ - **Ruby-OpenAI version validation**: Added validation to ensure ruby-openai >= 8.0 when using OpenAI provider with `api_version: "responses"`
5
+ - The responses API requires ruby-openai version 8.0 or higher
6
+ - Configuration validation now checks the installed version and provides helpful error messages
7
+ - Gracefully handles cases where ruby-openai is not installed
8
+
9
+ ### Changed
10
+ - **Relaxed ruby-openai dependency**: The gemspec now accepts ruby-openai versions 7.x and 8.x (`>= 7.0, < 9.0`)
11
+ - Version 7.x works with the default chat_completion API
12
+ - Version 8.x is required for the responses API
13
+
14
+ ## [0.2.0]
15
+
16
+ ### Added
17
+ - **After commands support**: Added `after` field to swarm configuration for executing cleanup commands
18
+ - Commands run after Claude exits but before cleanup processes
19
+ - Execute in the main instance's directory (including worktree if enabled)
20
+ - Run even when interrupted by signals (Ctrl+C)
21
+ - Failures in after commands do not prevent cleanup from proceeding
22
+ - Not executed during session restoration
23
+ - Example: `after: ["docker-compose down", "rm -rf temp/*"]`
24
+
25
+ ### Changed
26
+ - **Session restoration command**: Session restoration now uses a dedicated `restore` command instead of the `--session-id` flag
27
+ - Previous: `claude-swarm start --session-id SESSION_ID`
28
+ - New: `claude-swarm restore SESSION_ID`
29
+ - More intuitive command structure following standard CLI patterns
30
+ - **Removed redundant -c flag**: The `-c/--config` option has been removed from the `start` command
31
+ - Config file can still be specified as a positional argument: `claude-swarm start my-config.yml`
32
+ - Default remains `claude-swarm.yml` when no file is specified
33
+ - **Session ID format**: Session IDs now use UUIDs instead of timestamp format
34
+ - Previous format: `YYYYMMDD_HHMMSS` (e.g., `20250707_181341`)
35
+ - New format: UUID v4 (e.g., `550e8400-e29b-41d4-a716-446655440000`)
36
+ - Provides globally unique identifiers suitable for external application integration
37
+ - Sessions remain sorted by file creation time, not by ID
38
+ - **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
39
+ - This ensures commands like `npm install` or `bundle install` affect only the isolated worktree
40
+ - Makes behavior more intuitive and consistent with user expectations
41
+ - Existing swarms relying on before commands running in the original directory will need to be updated
42
+ - **Custom session ID support**: Added `--session-id` option to the `start` command to allow users to specify their own session ID
43
+ - Use `claude-swarm start --session-id my-custom-id` to use a custom session ID
44
+ - If not provided, a UUID is generated automatically
45
+ - Useful for integrating with external systems that need predictable session identifiers
46
+ - **Environment variable interpolation in configuration**: Claude Swarm now supports environment variable interpolation in all YAML configuration values
47
+ - Use `${ENV_VAR_NAME}` syntax to reference environment variables
48
+ - Variables are interpolated recursively in strings, arrays, and nested structures
49
+ - Fails with clear error message if referenced environment variable is not set
50
+ - Supports multiple variables in the same string and partial string interpolation
51
+ - **Environment variable defaults**: Environment variables in configuration now support default values
52
+ - Use `${ENV_VAR_NAME:=default_value}` syntax to provide defaults
53
+ - Default values are used when the environment variable is not set
54
+ - Supports any string as default value, including spaces and special characters
55
+ - Examples: `${DB_PORT:=5432}`, `${API_URL:=https://api.example.com}`
56
+ - **Session path display in show command**: The `claude-swarm show SESSION_ID` command now displays the session path for easier access to session files
57
+ - **Main process PID tracking**: The orchestrator now writes its process ID to `SESSION_PATH/main_pid` for external monitoring and management
58
+ - **Swarm execution summary**: Display runtime duration and total cost at the end of each swarm run [@claudenm]
59
+ - Shows total execution time in hours, minutes, and seconds format
60
+ - Calculates and displays aggregate cost across all instances
61
+ - Indicates when main instance cost is excluded (e.g., for interactive sessions)
62
+ - Session metadata now includes end time and duration in seconds
63
+
64
+ - **Session cost calculator**: New `SessionCostCalculator` class for aggregating costs from session logs [@claudenm]
65
+ - Processes session.log.json files to calculate total usage costs
66
+ - Tracks which instances have cost data available
67
+
68
+ - **Session cost calculator**: New `SessionCostCalculator` class for aggregating costs from session logs
69
+ - Processes session.log.json files to calculate total usage costs
70
+ - Tracks which instances have cost data available
71
+
72
+ - **OpenAI provider support**: Claude Swarm now supports OpenAI models as an alternative provider
73
+ - Instances can specify `provider: openai` in configuration (default remains "claude")
74
+ - Full MCP tool support for OpenAI instances via automatic conversion
75
+ - Mixed provider swarms allow Claude and OpenAI instances to collaborate
76
+ - OpenAI instances only work with `vibe: true` at the moment. There's no way to set allowed/disallowed tools.
77
+
78
+ - **Dual OpenAI API support**: Two API versions available for different use cases
79
+ - Chat Completion API (`api_version: "chat_completion"`) - Traditional format with tool calling
80
+ - Responses API (`api_version: "responses"`) - New structured format with function calls
81
+ - Both APIs support recursive tool execution with proper conversation tracking
82
+
83
+ - **OpenAI-specific configuration options**:
84
+ - `temperature`: Control response randomness (default: 0.3)
85
+ - `api_version`: Choose between "chat_completion" or "responses"
86
+ - `openai_token_env`: Custom environment variable for API key (default: "OPENAI_API_KEY")
87
+ - `base_url`: Support for OpenAI-compatible endpoints and proxies
88
+
89
+ - **Enhanced debugging and logging**:
90
+ - Detailed error response logging for OpenAI API calls
91
+ - Conversation flow tracking with IDs for debugging
92
+ - Full request/response logging in session JSONL files
93
+
94
+ ### Changed
95
+ - Configuration validation now enforces provider-specific fields
96
+ - Example configurations moved from `example/` to `examples/` directory
97
+ - Added `mixed-provider-swarm.yml` example demonstrating Claude-OpenAI collaboration
98
+ - Session metadata now includes `start_time`, `end_time`, and `duration_seconds` fields [@claudenm]
99
+ - Updated `ps` and `show` commands to use the new cost calculation functionality [@claudenm]
100
+
101
+
102
+ ### Internal
103
+ - New classes: `OpenAIExecutor`, `OpenAIChatCompletion`, `OpenAIResponses`
104
+ - Comprehensive test coverage for OpenAI provider functionality
105
+ - MCP generator enhanced to support provider-specific configurations
106
+
1
107
  ## [0.1.20]
2
108
 
3
109
  ### 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
@@ -1,6 +1,6 @@
1
1
  # Claude Swarm
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0.1.17)](https://badge.fury.io/rb/claude_swarm)
3
+ [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0.2.0)](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.
@@ -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,25 @@ 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`
323
+ - **Using the responses API requires ruby-openai >= 8.0** - the gem will validate this requirement at runtime
245
324
 
246
325
  ```yaml
247
326
  instance_name:
@@ -268,6 +347,27 @@ instance_name:
268
347
  args: ["arg1", "arg2"]
269
348
  env:
270
349
  VAR1: value1
350
+
351
+ # OpenAI instance examples
352
+
353
+ # GPT model with temperature
354
+ gpt_instance:
355
+ description: "OpenAI GPT-powered creative assistant"
356
+ provider: openai
357
+ model: gpt-4o
358
+ temperature: 0.7 # Supported for GPT models
359
+ api_version: chat_completion
360
+ openai_token_env: OPENAI_API_KEY
361
+ prompt: "You are a creative assistant specializing in content generation"
362
+
363
+ # O-series reasoning model with reasoning_effort
364
+ reasoning_instance:
365
+ description: "OpenAI O-series reasoning assistant"
366
+ provider: openai
367
+ model: o1-mini
368
+ reasoning_effort: medium # Only for O-series models
369
+ api_version: responses # Can use either API version
370
+ prompt: "You are a reasoning assistant for complex problem solving"
271
371
  ```
272
372
 
273
373
  ### MCP Server Types
@@ -459,9 +559,54 @@ When using multiple directories:
459
559
  - Additional directories are accessible via the `--add-dir` flag in Claude
460
560
  - All directories must exist or the configuration will fail validation
461
561
 
462
- #### Before Commands
562
+ #### Mixed AI Provider Team
563
+
564
+ Combine Claude and OpenAI instances in a single swarm:
565
+
566
+ ```yaml
567
+ version: 1
568
+ swarm:
569
+ name: "Mixed AI Development Team"
570
+ main: lead_developer
571
+ instances:
572
+ lead_developer:
573
+ description: "Claude lead developer coordinating the team"
574
+ directory: .
575
+ model: opus
576
+ connections: [creative_assistant, reasoning_expert, backend_dev]
577
+ prompt: "You are the lead developer coordinating a mixed AI team"
578
+ allowed_tools: [Read, Edit, Bash, Write]
579
+
580
+ creative_assistant:
581
+ description: "OpenAI-powered assistant for creative and UI/UX tasks"
582
+ provider: openai
583
+ model: gpt-4o
584
+ temperature: 0.7 # Supported for GPT models
585
+ directory: ./frontend
586
+ prompt: "You are a creative frontend developer specializing in UI/UX design"
587
+ # OpenAI instances default to vibe: true
588
+
589
+ reasoning_expert:
590
+ description: "OpenAI O-series model for complex problem solving"
591
+ provider: openai
592
+ model: o1-mini
593
+ reasoning_effort: high # For O-series models only
594
+ directory: ./architecture
595
+ prompt: "You solve complex architectural and algorithmic problems"
596
+
597
+ backend_dev:
598
+ description: "Claude backend developer for system architecture"
599
+ directory: ./backend
600
+ model: sonnet
601
+ prompt: "You specialize in backend development and system architecture"
602
+ allowed_tools: [Read, Edit, Write, Bash]
603
+ ```
604
+
605
+ Note: OpenAI instances require the API key to be set in the environment variable (default: `OPENAI_API_KEY`).
463
606
 
464
- You can specify commands to run before launching the swarm using the `before` field:
607
+ #### Before and After Commands
608
+
609
+ You can specify commands to run before launching and after exiting the swarm using the `before` and `after` fields:
465
610
 
466
611
  ```yaml
467
612
  version: 1
@@ -473,6 +618,10 @@ swarm:
473
618
  - "npm install"
474
619
  - "docker-compose up -d"
475
620
  - "bundle install"
621
+ after:
622
+ - "echo '🛑 Cleaning up development environment...'"
623
+ - "docker-compose down"
624
+ - "rm -rf temp/*"
476
625
  instances:
477
626
  lead_developer:
478
627
  description: "Lead developer coordinating the team"
@@ -483,16 +632,26 @@ swarm:
483
632
 
484
633
  The `before` commands:
485
634
  - Are executed in sequence before launching any Claude instances
635
+ - Execute in the main instance's directory (including worktree if enabled)
486
636
  - Must all succeed for the swarm to launch (exit code 0)
487
637
  - Are only executed on initial swarm launch, not when restoring sessions
488
638
  - Have their output logged to the session log file
489
639
  - Will abort the swarm launch if any command fails
490
640
 
641
+ The `after` commands:
642
+ - Are executed after Claude exits but before cleanup processes
643
+ - Execute in the main instance's directory (including worktree if enabled)
644
+ - Run even when the swarm is interrupted by signals (Ctrl+C)
645
+ - Failures do not prevent cleanup from proceeding
646
+ - Are only executed on initial swarm runs, not when restoring sessions
647
+ - Have their output logged to the session log file
648
+
491
649
  This is useful for:
492
- - Installing dependencies
650
+ - Installing dependencies in the isolated worktree environment
493
651
  - Starting required services (databases, Docker containers, etc.)
494
652
  - Setting up the development environment
495
653
  - Running any prerequisite setup scripts
654
+ - Ensuring setup commands affect only the working directory, not the original repository
496
655
 
497
656
 
498
657
  #### Mixed Permission Modes
@@ -598,8 +757,8 @@ swarm:
598
757
  claude-swarm
599
758
 
600
759
  # Specify a different configuration file
601
- claude-swarm --config my-swarm.yml
602
- claude-swarm -c team-config.yml
760
+ claude-swarm my-swarm.yml
761
+ claude-swarm team-config.yml
603
762
 
604
763
  # Run with --dangerously-skip-permissions for all instances
605
764
  claude-swarm --vibe
@@ -608,9 +767,11 @@ claude-swarm --vibe
608
767
  claude-swarm -p "Implement the new user authentication feature"
609
768
  claude-swarm --prompt "Fix the bug in the payment module"
610
769
 
611
- # Resume a previous session by ID
612
- claude-swarm --session-id 20241206_143022
613
- claude-swarm --session-id ~/path/to/session
770
+ # Use a custom session ID instead of auto-generated UUID
771
+ claude-swarm --session-id my-custom-session-123
772
+
773
+ # Stream logs to stdout in prompt mode
774
+ claude-swarm -p "Fix the tests" --stream-logs
614
775
 
615
776
  # Run all instances in Git worktrees
616
777
  claude-swarm --worktree # Auto-generated name (worktree-SESSION_ID)
@@ -671,7 +832,7 @@ Example output from `claude-swarm ps`:
671
832
  SESSION_ID SWARM_NAME TOTAL_COST UPTIME DIRECTORY
672
833
  -------------------------------------------------------------------------------
673
834
  20250617_235233 Feature Development $0.3847 15m .
674
- 20250617_143022 Bug Investigation $1.2156 1h ./shopify
835
+ 20250617_143022 Bug Investigation $1.2156 1h .
675
836
  20250617_091547 Multi-Module Dev $0.8932 30m ./frontend, ./backend, ./shared
676
837
  ```
677
838
 
@@ -734,11 +895,8 @@ Output shows:
734
895
  Resume a previous session with all instances restored to their Claude session states:
735
896
 
736
897
  ```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
898
+ # Restore using the session's UUID
899
+ claude-swarm restore 550e8400-e29b-41d4-a716-446655440000
742
900
  ```
743
901
 
744
902
  This will:
@@ -810,7 +968,7 @@ The swarm will display:
810
968
 
811
969
  ### Session Files
812
970
 
813
- Check the session directory `~/.claude-swarm/sessions/{project}/{timestamp}/` for:
971
+ Check the session directory `~/.claude-swarm/sessions/{project}/{session-id}/` for:
814
972
  - `session.log`: Human-readable logs with request/response tracking
815
973
  - `session.log.json`: All events in JSONL format (one JSON object per line)
816
974
  - `{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)