claude_swarm 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +17 -0
- data/CLAUDE.md +3 -2
- data/README.md +18 -13
- data/examples/monitoring-demo.yml +4 -4
- data/lib/claude_swarm/claude_code_executor.rb +0 -1
- data/lib/claude_swarm/claude_mcp_server.rb +0 -5
- data/lib/claude_swarm/cli.rb +99 -108
- data/lib/claude_swarm/mcp_generator.rb +0 -1
- data/lib/claude_swarm/orchestrator.rb +16 -6
- data/lib/claude_swarm/system_utils.rb +18 -0
- data/lib/claude_swarm/templates/generation_prompt.md.erb +230 -0
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +136 -32
- data/lib/claude_swarm.rb +21 -12
- data/llms.txt +2 -2
- data/team.yml +344 -0
- metadata +18 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdad008c72dacb5ca491dc7c6b912c4bc8d46c7f2b578b3762ed7b5e09b4e3f4
|
4
|
+
data.tar.gz: a426057ff0bb859cd56a0bec71e80d02c1232fe6eb69a74e47ed3044f7121a58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7fb86ea647403073665b860dde694ef2ae0c983f8fc011189d999f97d57e8b80f552b9e736b1f46cb888ac3c4877666dac060e1335db39bd2719f120ad35937
|
7
|
+
data.tar.gz: f1670e4c35e9681054dbe5ab2bcc87d7ef24a6ad4f33a21d3e445c32cb1e917eedbc2984c483b06750469fa08e8256109693a121f337ff813a8ff1944ecc42f1
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## [0.1.20]
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- **External worktree directory**: Git worktrees are now created in `~/.claude-swarm/worktrees/` for better isolation from the main repository
|
5
|
+
- Prevents conflicts with bundler and other tools
|
6
|
+
- Each unique Git repository gets its own worktree with the same name
|
7
|
+
- Session metadata tracks worktree information for restoration
|
8
|
+
- **Zeitwerk autoloading**: Implemented Zeitwerk for better code organization and loading
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
- **Improved team coordination**: Removed circular dependencies in team configurations
|
12
|
+
- **Better code organization**: Refactored code structure to work with Zeitwerk autoloading
|
13
|
+
|
14
|
+
### Internal
|
15
|
+
- Updated Gemfile.lock to include zeitwerk dependency
|
16
|
+
- Added prompt best practices documentation
|
17
|
+
|
1
18
|
## [0.1.19]
|
2
19
|
|
3
20
|
### Added
|
data/CLAUDE.md
CHANGED
@@ -71,14 +71,15 @@ instances:
|
|
71
71
|
```
|
72
72
|
|
73
73
|
### Worktree Behavior
|
74
|
-
- Worktrees are created
|
75
|
-
-
|
74
|
+
- Worktrees are created in external directory: `~/.claude-swarm/worktrees/[session_id]/[repo_name-hash]/[worktree_name]`
|
75
|
+
- This ensures proper isolation from the main repository and avoids conflicts with bundler and other tools
|
76
76
|
- Each unique Git repository gets its own worktree with the same name
|
77
77
|
- All instance directories are mapped to their worktree equivalents
|
78
78
|
- Worktrees are automatically cleaned up when the swarm exits
|
79
79
|
- Session metadata tracks worktree information for restoration
|
80
80
|
- Non-Git directories are used as-is without creating worktrees
|
81
81
|
- Existing worktrees with the same name are reused
|
82
|
+
- The `claude-swarm clean` command removes orphaned worktrees
|
82
83
|
|
83
84
|
## Architecture
|
84
85
|
|
data/README.md
CHANGED
@@ -27,7 +27,13 @@ Claude Swarm orchestrates multiple Claude Code instances as a collaborative AI d
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
Install
|
30
|
+
Install [Claude CLI](https://docs.anthropic.com/en/docs/claude-code/overview) if you haven't already:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
npm install -g @anthropic-ai/claude-code
|
34
|
+
```
|
35
|
+
|
36
|
+
Install this gem by executing:
|
31
37
|
|
32
38
|
```bash
|
33
39
|
gem install claude_swarm
|
@@ -522,17 +528,16 @@ swarm:
|
|
522
528
|
|
523
529
|
#### Git Worktrees
|
524
530
|
|
525
|
-
Claude Swarm supports running instances in Git worktrees, allowing isolated work without affecting your main repository state. Worktrees are created
|
531
|
+
Claude Swarm supports running instances in Git worktrees, allowing isolated work without affecting your main repository state. Worktrees are created in an external directory (`~/.claude-swarm/worktrees/`) to ensure proper isolation from the main repository and avoid conflicts with bundler and other tools.
|
526
532
|
|
527
533
|
**Example Structure:**
|
528
534
|
```
|
529
|
-
|
530
|
-
|
531
|
-
├──
|
532
|
-
│
|
533
|
-
|
534
|
-
|
535
|
-
└── tests/
|
535
|
+
~/.claude-swarm/worktrees/
|
536
|
+
└── [session_id]/
|
537
|
+
├── my-repo-[hash]/
|
538
|
+
│ └── feature-x/ (worktree for feature-x branch)
|
539
|
+
└── other-repo-[hash]/
|
540
|
+
└── feature-x/ (worktree for feature-x branch)
|
536
541
|
```
|
537
542
|
|
538
543
|
**CLI Option:**
|
@@ -577,14 +582,14 @@ swarm:
|
|
577
582
|
- Omitted - Follows CLI behavior (use worktree if `--worktree` is specified)
|
578
583
|
|
579
584
|
**Notes:**
|
580
|
-
- Worktrees are created inside each repository in a `.worktrees/` directory
|
581
585
|
- Auto-generated worktree names use the session ID (e.g., `worktree-20241206_143022`)
|
582
586
|
- This makes it easy to correlate worktrees with their Claude Swarm sessions
|
583
|
-
-
|
587
|
+
- Worktrees are stored externally in `~/.claude-swarm/worktrees/[session_id]/`
|
584
588
|
- All worktrees are automatically cleaned up when the swarm exits
|
585
589
|
- Worktrees with the same name across different repositories share that name
|
586
590
|
- Non-Git directories are unaffected by worktree settings
|
587
591
|
- Existing worktrees with the same name are reused
|
592
|
+
- The `claude-swarm clean` command also removes orphaned worktrees
|
588
593
|
|
589
594
|
### Command Line Options
|
590
595
|
|
@@ -652,10 +657,10 @@ claude-swarm watch 20250617_235233 -n 50
|
|
652
657
|
claude-swarm list-sessions
|
653
658
|
claude-swarm list-sessions --limit 20
|
654
659
|
|
655
|
-
# Clean up stale session symlinks
|
660
|
+
# Clean up stale session symlinks and orphaned worktrees
|
656
661
|
claude-swarm clean
|
657
662
|
|
658
|
-
# Remove sessions older than 30 days
|
663
|
+
# Remove sessions and worktrees older than 30 days
|
659
664
|
claude-swarm clean --days 30
|
660
665
|
```
|
661
666
|
|
@@ -6,7 +6,7 @@ swarm:
|
|
6
6
|
coordinator:
|
7
7
|
description: "Main coordinator managing the team"
|
8
8
|
directory: .
|
9
|
-
model:
|
9
|
+
model: sonnet
|
10
10
|
connections: [analyzer, reporter]
|
11
11
|
prompt: "You coordinate analysis and reporting tasks"
|
12
12
|
allowed_tools: [Read, Edit]
|
@@ -14,13 +14,13 @@ swarm:
|
|
14
14
|
analyzer:
|
15
15
|
description: "Data analyzer processing information"
|
16
16
|
directory: ./data
|
17
|
-
model:
|
17
|
+
model: sonnet
|
18
18
|
prompt: "You analyze data and provide insights"
|
19
19
|
allowed_tools: [Read, Bash]
|
20
20
|
|
21
21
|
reporter:
|
22
22
|
description: "Report generator creating summaries"
|
23
23
|
directory: ./reports
|
24
|
-
model:
|
24
|
+
model: sonnet
|
25
25
|
prompt: "You generate reports from analysis"
|
26
|
-
allowed_tools: [Write, Edit]
|
26
|
+
allowed_tools: [Write, Edit]
|
@@ -2,11 +2,6 @@
|
|
2
2
|
|
3
3
|
require "fast_mcp_annotations"
|
4
4
|
require "json"
|
5
|
-
require_relative "claude_code_executor"
|
6
|
-
require_relative "task_tool"
|
7
|
-
require_relative "session_info_tool"
|
8
|
-
require_relative "reset_session_tool"
|
9
|
-
require_relative "process_tracker"
|
10
5
|
|
11
6
|
module ClaudeSwarm
|
12
7
|
class ClaudeMcpServer
|
data/lib/claude_swarm/cli.rb
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
require "thor"
|
4
4
|
require "json"
|
5
|
-
|
6
|
-
require_relative "mcp_generator"
|
7
|
-
require_relative "orchestrator"
|
8
|
-
require_relative "claude_mcp_server"
|
5
|
+
require "erb"
|
9
6
|
|
10
7
|
module ClaudeSwarm
|
11
8
|
class CLI < Thor
|
9
|
+
include SystemUtils
|
12
10
|
def self.exit_on_failure?
|
13
11
|
true
|
14
12
|
end
|
@@ -207,7 +205,9 @@ module ClaudeSwarm
|
|
207
205
|
desc: "Claude model to use for generation"
|
208
206
|
def generate
|
209
207
|
# Check if claude command exists
|
210
|
-
|
208
|
+
begin
|
209
|
+
system!("command -v claude > /dev/null 2>&1")
|
210
|
+
rescue Error
|
211
211
|
error "Claude CLI is not installed or not in PATH"
|
212
212
|
say "To install Claude CLI, visit: https://docs.anthropic.com/en/docs/claude-code"
|
213
213
|
exit 1
|
@@ -238,49 +238,30 @@ module ClaudeSwarm
|
|
238
238
|
|
239
239
|
desc "ps", "List running Claude Swarm sessions"
|
240
240
|
def ps
|
241
|
-
require_relative "commands/ps"
|
242
241
|
Commands::Ps.new.execute
|
243
242
|
end
|
244
243
|
|
245
244
|
desc "show SESSION_ID", "Show detailed session information"
|
246
245
|
def show(session_id)
|
247
|
-
require_relative "commands/show"
|
248
246
|
Commands::Show.new.execute(session_id)
|
249
247
|
end
|
250
248
|
|
251
|
-
desc "clean", "Remove stale session symlinks"
|
249
|
+
desc "clean", "Remove stale session symlinks and orphaned worktrees"
|
252
250
|
method_option :days, aliases: "-d", type: :numeric, default: 7,
|
253
251
|
desc: "Remove sessions older than N days"
|
254
252
|
def clean
|
255
|
-
|
256
|
-
|
257
|
-
say "No run directory found", :yellow
|
258
|
-
return
|
259
|
-
end
|
253
|
+
# Clean stale symlinks
|
254
|
+
cleaned_symlinks = clean_stale_symlinks(options[:days])
|
260
255
|
|
261
|
-
|
262
|
-
|
263
|
-
next unless File.symlink?(symlink)
|
256
|
+
# Clean orphaned worktrees
|
257
|
+
cleaned_worktrees = clean_orphaned_worktrees(options[:days])
|
264
258
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
next
|
271
|
-
end
|
272
|
-
|
273
|
-
# Remove if older than specified days
|
274
|
-
if File.stat(symlink).mtime < Time.now - (options[:days] * 86_400)
|
275
|
-
File.unlink(symlink)
|
276
|
-
cleaned += 1
|
277
|
-
end
|
278
|
-
rescue StandardError
|
279
|
-
# Skip problematic symlinks
|
280
|
-
end
|
259
|
+
if cleaned_symlinks.positive? || cleaned_worktrees.positive?
|
260
|
+
say "Cleaned #{cleaned_symlinks} stale symlink#{"s" unless cleaned_symlinks == 1}", :green
|
261
|
+
say "Cleaned #{cleaned_worktrees} orphaned worktree#{"s" unless cleaned_worktrees == 1}", :green
|
262
|
+
else
|
263
|
+
say "No cleanup needed", :green
|
281
264
|
end
|
282
|
-
|
283
|
-
say "Cleaned #{cleaned} stale session#{"s" unless cleaned == 1}", :green
|
284
265
|
end
|
285
266
|
|
286
267
|
desc "watch SESSION_ID", "Watch session logs"
|
@@ -477,81 +458,91 @@ module ClaudeSwarm
|
|
477
458
|
nil
|
478
459
|
end
|
479
460
|
|
461
|
+
def clean_stale_symlinks(days)
|
462
|
+
run_dir = File.expand_path("~/.claude-swarm/run")
|
463
|
+
return 0 unless Dir.exist?(run_dir)
|
464
|
+
|
465
|
+
cleaned = 0
|
466
|
+
Dir.glob("#{run_dir}/*").each do |symlink|
|
467
|
+
next unless File.symlink?(symlink)
|
468
|
+
|
469
|
+
begin
|
470
|
+
# Remove if target doesn't exist (stale)
|
471
|
+
unless File.exist?(File.readlink(symlink))
|
472
|
+
File.unlink(symlink)
|
473
|
+
cleaned += 1
|
474
|
+
next
|
475
|
+
end
|
476
|
+
|
477
|
+
# Remove if older than specified days
|
478
|
+
if File.stat(symlink).mtime < Time.now - (days * 86_400)
|
479
|
+
File.unlink(symlink)
|
480
|
+
cleaned += 1
|
481
|
+
end
|
482
|
+
rescue StandardError
|
483
|
+
# Skip problematic symlinks
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
cleaned
|
488
|
+
end
|
489
|
+
|
490
|
+
def clean_orphaned_worktrees(days)
|
491
|
+
worktrees_dir = File.expand_path("~/.claude-swarm/worktrees")
|
492
|
+
return 0 unless Dir.exist?(worktrees_dir)
|
493
|
+
|
494
|
+
sessions_dir = File.expand_path("~/.claude-swarm/sessions")
|
495
|
+
cleaned = 0
|
496
|
+
|
497
|
+
Dir.glob("#{worktrees_dir}/*").each do |session_worktree_dir|
|
498
|
+
session_id = File.basename(session_worktree_dir)
|
499
|
+
|
500
|
+
# Skip if session still exists
|
501
|
+
next if Dir.glob("#{sessions_dir}/*/#{session_id}").any? { |path| File.exist?(File.join(path, "config.yml")) }
|
502
|
+
|
503
|
+
# Check age of worktree directory
|
504
|
+
begin
|
505
|
+
if File.stat(session_worktree_dir).mtime < Time.now - (days * 86_400)
|
506
|
+
# Remove all git worktrees in this session directory
|
507
|
+
Dir.glob("#{session_worktree_dir}/*/*").each do |worktree_path|
|
508
|
+
next unless File.directory?(worktree_path)
|
509
|
+
|
510
|
+
# Try to find the git repo and remove the worktree properly
|
511
|
+
git_dir = File.join(worktree_path, ".git")
|
512
|
+
if File.exist?(git_dir)
|
513
|
+
# Read the gitdir file to find the repo
|
514
|
+
gitdir_content = File.read(git_dir).strip
|
515
|
+
if gitdir_content.start_with?("gitdir:")
|
516
|
+
repo_git_path = gitdir_content.sub("gitdir: ", "")
|
517
|
+
# Extract repo path from .git/worktrees path
|
518
|
+
repo_path = repo_git_path.split("/.git/worktrees/").first
|
519
|
+
|
520
|
+
# Try to remove worktree via git
|
521
|
+
system!("git", "-C", repo_path, "worktree", "remove", worktree_path, "--force",
|
522
|
+
out: File::NULL, err: File::NULL)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
# Force remove directory if it still exists
|
527
|
+
FileUtils.rm_rf(worktree_path)
|
528
|
+
end
|
529
|
+
|
530
|
+
# Remove the session worktree directory
|
531
|
+
FileUtils.rm_rf(session_worktree_dir)
|
532
|
+
cleaned += 1
|
533
|
+
end
|
534
|
+
rescue StandardError => e
|
535
|
+
say "Warning: Failed to clean worktree directory #{session_worktree_dir}: #{e.message}", :yellow if options[:debug]
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
cleaned
|
540
|
+
end
|
541
|
+
|
480
542
|
def build_generation_prompt(readme_content, output_file)
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
## Claude Swarm Overview
|
485
|
-
Claude Swarm is a Ruby gem that 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).
|
486
|
-
|
487
|
-
Key capabilities:
|
488
|
-
- Define multiple AI instances with different roles and specializations
|
489
|
-
- Set up connections between instances for collaboration
|
490
|
-
- Restrict tools based on each instance's responsibilities
|
491
|
-
- Run instances in different directories or Git worktrees
|
492
|
-
- Support for custom system prompts per instance
|
493
|
-
- Choose appropriate models (opus for complex tasks, sonnet for simpler ones)
|
494
|
-
|
495
|
-
## Your Task
|
496
|
-
1. Start by asking about the user's project structure and development needs
|
497
|
-
2. Understand what kind of team they need (roles, specializations)
|
498
|
-
3. Suggest an appropriate swarm topology based on their needs
|
499
|
-
4. Help them refine and customize the configuration
|
500
|
-
5. Generate the final claude-swarm.yml content
|
501
|
-
6. When the configuration is complete, save it to: #{output_file || "a descriptive filename based on the swarm's function"}
|
502
|
-
|
503
|
-
## File Naming Convention
|
504
|
-
#{output_file ? "The user has specified the output file: #{output_file}" : "Since no output file was specified, name the file based on the swarm's function. Examples:\n - web-dev-swarm.yml for full-stack web development teams\n - data-pipeline-swarm.yml for data processing teams\n - microservices-swarm.yml for microservice architectures\n - mobile-app-swarm.yml for mobile development teams\n - ml-research-swarm.yml for machine learning teams\n - devops-swarm.yml for infrastructure and deployment teams\n Use descriptive names that clearly indicate the swarm's purpose."}
|
505
|
-
|
506
|
-
## Configuration Structure
|
507
|
-
```yaml
|
508
|
-
version: 1
|
509
|
-
swarm:
|
510
|
-
name: "Descriptive Swarm Name"
|
511
|
-
main: main_instance_name
|
512
|
-
instances:
|
513
|
-
instance_name:
|
514
|
-
description: "Clear description of role and responsibilities"
|
515
|
-
directory: ./path/to/directory
|
516
|
-
model: sonnet # or opus for complex tasks
|
517
|
-
prompt: "Custom system prompt for specialization"
|
518
|
-
allowed_tools: [Read, Edit, Write, Bash]
|
519
|
-
connections: [other_instance_names] # Optional
|
520
|
-
```
|
521
|
-
|
522
|
-
## Best Practices to Follow
|
523
|
-
- Use descriptive, role-based instance names (e.g., frontend_dev, api_architect)
|
524
|
-
- Write clear descriptions explaining each instance's responsibilities
|
525
|
-
- Choose opus model for complex architectural or algorithmic tasks and routine development.
|
526
|
-
- Choose sonnet model for simpler tasks
|
527
|
-
- Set up logical connections (e.g., lead → team members, architect → implementers), but avoid circular dependencies.
|
528
|
-
- Always add this to the end of every prompt: `For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.`
|
529
|
-
- Select tools based on each instance's actual needs:
|
530
|
-
- Read: For code review and analysis roles
|
531
|
-
- Edit: For active development roles
|
532
|
-
- Write: For creating new files
|
533
|
-
- Bash: For running commands, tests, builds
|
534
|
-
- MultiEdit: For editing multiple files at once
|
535
|
-
- WebFetch: For fetching information from the web
|
536
|
-
- WebSearch: For searching the web
|
537
|
-
- Use custom prompts to specialize each instance's expertise
|
538
|
-
- Organize directories to match project structure
|
539
|
-
|
540
|
-
## Interactive Questions to Ask
|
541
|
-
- What type of project are you working on?
|
542
|
-
- What's your project's directory structure?
|
543
|
-
- What are the main technologies/frameworks you're using?
|
544
|
-
- What development tasks do you need help with?
|
545
|
-
- Do you need specialized roles (testing, DevOps, documentation)?
|
546
|
-
- Are there specific areas that need focused attention?
|
547
|
-
- Do you have multiple repositories or services to coordinate?
|
548
|
-
|
549
|
-
<full_readme>
|
550
|
-
#{readme_content}
|
551
|
-
</full_readme>
|
552
|
-
|
553
|
-
Start the conversation by greeting the user and asking: "What kind of project would you like to create a Claude Swarm for?"
|
554
|
-
PROMPT
|
543
|
+
template_path = File.expand_path("templates/generation_prompt.md.erb", __dir__)
|
544
|
+
template = File.read(template_path)
|
545
|
+
ERB.new(template, trim_mode: "-").result(binding)
|
555
546
|
end
|
556
547
|
end
|
557
548
|
end
|
@@ -4,12 +4,10 @@ require "English"
|
|
4
4
|
require "shellwords"
|
5
5
|
require "json"
|
6
6
|
require "fileutils"
|
7
|
-
require_relative "session_path"
|
8
|
-
require_relative "process_tracker"
|
9
|
-
require_relative "worktree_manager"
|
10
7
|
|
11
8
|
module ClaudeSwarm
|
12
9
|
class Orchestrator
|
10
|
+
include SystemUtils
|
13
11
|
RUN_DIR = File.expand_path("~/.claude-swarm/run")
|
14
12
|
|
15
13
|
def initialize(configuration, mcp_generator, vibe: false, prompt: nil, stream_logs: false, debug: false,
|
@@ -174,7 +172,7 @@ module ClaudeSwarm
|
|
174
172
|
|
175
173
|
command = build_main_command(main_instance)
|
176
174
|
if @debug && !@prompt
|
177
|
-
puts "Running: #{command}"
|
175
|
+
puts "🏃 Running: #{format_command_for_display(command)}"
|
178
176
|
puts
|
179
177
|
end
|
180
178
|
|
@@ -184,7 +182,7 @@ module ClaudeSwarm
|
|
184
182
|
|
185
183
|
# Execute the main instance - this will cascade to other instances via MCP
|
186
184
|
Dir.chdir(main_instance[:directory]) do
|
187
|
-
system(*command)
|
185
|
+
system!(*command)
|
188
186
|
end
|
189
187
|
|
190
188
|
# Clean up log streaming thread
|
@@ -362,6 +360,16 @@ module ClaudeSwarm
|
|
362
360
|
end
|
363
361
|
end
|
364
362
|
|
363
|
+
def format_command_for_display(command)
|
364
|
+
command.map do |part|
|
365
|
+
if part.match?(/\s|'|"/)
|
366
|
+
"'#{part.gsub("'", "'\\\\''")}'"
|
367
|
+
else
|
368
|
+
part
|
369
|
+
end
|
370
|
+
end.join(" ")
|
371
|
+
end
|
372
|
+
|
365
373
|
def build_main_command(instance)
|
366
374
|
parts = [
|
367
375
|
"claude",
|
@@ -456,7 +464,9 @@ module ClaudeSwarm
|
|
456
464
|
end
|
457
465
|
|
458
466
|
# Restore worktrees using the saved configuration
|
459
|
-
|
467
|
+
# Extract session ID from the session path
|
468
|
+
session_id = File.basename(session_path)
|
469
|
+
@worktree_manager = WorktreeManager.new(worktree_data["shared_name"], session_id: session_id)
|
460
470
|
|
461
471
|
# Get all instances and restore their worktree paths
|
462
472
|
all_instances = @config.instances.values
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "English"
|
4
|
+
|
5
|
+
module ClaudeSwarm
|
6
|
+
module SystemUtils
|
7
|
+
def system!(*args)
|
8
|
+
success = system(*args)
|
9
|
+
unless success
|
10
|
+
exit_status = $CHILD_STATUS&.exitstatus || 1
|
11
|
+
command_str = args.size == 1 ? args.first : args.join(" ")
|
12
|
+
warn "❌ Command failed with exit status: #{exit_status}"
|
13
|
+
raise Error, "Command failed with exit status #{exit_status}: #{command_str}"
|
14
|
+
end
|
15
|
+
success
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|