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.
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module ClaudeSwarm
6
+ class ProcessTracker
7
+ PIDS_DIR = "pids"
8
+
9
+ def initialize(session_path)
10
+ @session_path = session_path
11
+ @pids_dir = File.join(@session_path, PIDS_DIR)
12
+ ensure_pids_directory
13
+ end
14
+
15
+ def track_pid(pid, name)
16
+ pid_file = File.join(@pids_dir, pid.to_s)
17
+ File.write(pid_file, name)
18
+ end
19
+
20
+ def cleanup_all
21
+ return unless Dir.exist?(@pids_dir)
22
+
23
+ # Get all PID files
24
+ pid_files = Dir.glob(File.join(@pids_dir, "*"))
25
+
26
+ pid_files.each do |pid_file|
27
+ pid = File.basename(pid_file).to_i
28
+ name = begin
29
+ File.read(pid_file).strip
30
+ rescue StandardError
31
+ "unknown"
32
+ end
33
+
34
+ begin
35
+ # Check if process is still running
36
+ Process.kill(0, pid)
37
+ # If we get here, process is running, so kill it
38
+ Process.kill("TERM", pid)
39
+ puts "✓ Terminated MCP server: #{name} (PID: #{pid})"
40
+
41
+ # Give it a moment to terminate gracefully
42
+ sleep 0.1
43
+
44
+ # Force kill if still running
45
+ begin
46
+ Process.kill(0, pid)
47
+ Process.kill("KILL", pid)
48
+ puts " → Force killed #{name} (PID: #{pid})"
49
+ rescue Errno::ESRCH
50
+ # Process is gone, which is what we want
51
+ end
52
+ rescue Errno::ESRCH
53
+ # Process not found, already terminated
54
+ puts " → MCP server #{name} (PID: #{pid}) already terminated"
55
+ rescue Errno::EPERM
56
+ # Permission denied
57
+ puts " ⚠️ No permission to terminate #{name} (PID: #{pid})"
58
+ end
59
+ end
60
+
61
+ # Clean up the pids directory
62
+ FileUtils.rm_rf(@pids_dir)
63
+ end
64
+
65
+ def self.cleanup_session(session_path)
66
+ return unless Dir.exist?(File.join(session_path, PIDS_DIR))
67
+
68
+ tracker = new(session_path)
69
+ tracker.cleanup_all
70
+ end
71
+
72
+ private
73
+
74
+ def ensure_pids_directory
75
+ FileUtils.mkdir_p(@pids_dir)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module ClaudeSwarm
6
+ module SessionPath
7
+ SESSIONS_DIR = "sessions"
8
+
9
+ class << self
10
+ def swarm_home
11
+ ENV["CLAUDE_SWARM_HOME"] || File.expand_path("~/.claude-swarm")
12
+ end
13
+
14
+ # Convert a directory path to a safe folder name using + as separator
15
+ def project_folder_name(working_dir = Dir.pwd)
16
+ # Don't expand path if it's already expanded (avoids double expansion on Windows)
17
+ path = working_dir.start_with?("/") || working_dir.match?(/^[A-Za-z]:/) ? working_dir : File.expand_path(working_dir)
18
+
19
+ # Handle Windows drive letters (C:\ → C)
20
+ path = path.gsub(/^([A-Za-z]):/, '\1')
21
+
22
+ # Remove leading slash/backslash
23
+ path = path.sub(%r{^[/\\]}, "")
24
+
25
+ # Replace all path separators with +
26
+ path.gsub(%r{[/\\]}, "+")
27
+ end
28
+
29
+ # Generate a full session path for a given directory and timestamp
30
+ def generate(working_dir: Dir.pwd, timestamp: Time.now.strftime("%Y%m%d_%H%M%S"))
31
+ project_name = project_folder_name(working_dir)
32
+ File.join(swarm_home, SESSIONS_DIR, project_name, timestamp)
33
+ end
34
+
35
+ # Ensure the session directory exists
36
+ def ensure_directory(session_path)
37
+ FileUtils.mkdir_p(session_path)
38
+
39
+ # Add .gitignore to swarm home if it doesn't exist
40
+ gitignore_path = File.join(swarm_home, ".gitignore")
41
+ File.write(gitignore_path, "*\n") unless File.exist?(gitignore_path)
42
+ end
43
+
44
+ # Get the session path from environment (required)
45
+ def from_env
46
+ ENV["CLAUDE_SWARM_SESSION_PATH"] or raise "CLAUDE_SWARM_SESSION_PATH not set"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "0.1.11"
4
+ VERSION = "0.1.13"
5
5
  end
data/lib/claude_swarm.rb CHANGED
@@ -2,10 +2,18 @@
2
2
 
3
3
  require_relative "claude_swarm/version"
4
4
  require_relative "claude_swarm/cli"
5
+ require_relative "claude_swarm/configuration"
6
+ require_relative "claude_swarm/mcp_generator"
7
+ require_relative "claude_swarm/orchestrator"
5
8
  require_relative "claude_swarm/claude_code_executor"
6
9
  require_relative "claude_swarm/claude_mcp_server"
7
10
  require_relative "claude_swarm/permission_tool"
8
11
  require_relative "claude_swarm/permission_mcp_server"
12
+ require_relative "claude_swarm/session_path"
13
+ require_relative "claude_swarm/session_info_tool"
14
+ require_relative "claude_swarm/reset_session_tool"
15
+ require_relative "claude_swarm/task_tool"
16
+ require_relative "claude_swarm/process_tracker"
9
17
 
10
18
  module ClaudeSwarm
11
19
  class Error < StandardError; end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda
@@ -58,8 +58,10 @@ files:
58
58
  - README.md
59
59
  - RELEASING.md
60
60
  - Rakefile
61
+ - claude-swarm.yml
61
62
  - example/claude-swarm.yml
62
63
  - example/microservices-team.yml
64
+ - example/session-restoration-demo.yml
63
65
  - example/test-generation.yml
64
66
  - exe/claude-swarm
65
67
  - lib/claude_swarm.rb
@@ -71,8 +73,10 @@ files:
71
73
  - lib/claude_swarm/orchestrator.rb
72
74
  - lib/claude_swarm/permission_mcp_server.rb
73
75
  - lib/claude_swarm/permission_tool.rb
76
+ - lib/claude_swarm/process_tracker.rb
74
77
  - lib/claude_swarm/reset_session_tool.rb
75
78
  - lib/claude_swarm/session_info_tool.rb
79
+ - lib/claude_swarm/session_path.rb
76
80
  - lib/claude_swarm/task_tool.rb
77
81
  - lib/claude_swarm/version.rb
78
82
  - sdk-docs.md