claude_swarm 0.3.2 → 1.0.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.
- checksums.yaml +4 -4
- data/.claude/commands/release.md +27 -0
- data/CHANGELOG.md +174 -0
- data/CLAUDE.md +62 -3
- data/README.md +131 -5
- data/examples/simple-session-hook-swarm.yml +37 -0
- data/lib/claude_swarm/base_executor.rb +133 -0
- data/lib/claude_swarm/claude_code_executor.rb +245 -210
- data/lib/claude_swarm/claude_mcp_server.rb +3 -2
- data/lib/claude_swarm/cli.rb +27 -20
- data/lib/claude_swarm/commands/ps.rb +29 -10
- data/lib/claude_swarm/commands/show.rb +4 -5
- data/lib/claude_swarm/configuration.rb +19 -12
- data/lib/claude_swarm/hooks/session_start_hook.rb +42 -0
- data/lib/claude_swarm/json_handler.rb +91 -0
- data/lib/claude_swarm/mcp_generator.rb +7 -5
- data/lib/claude_swarm/openai/chat_completion.rb +16 -16
- data/lib/claude_swarm/openai/executor.rb +155 -209
- data/lib/claude_swarm/openai/responses.rb +29 -29
- data/lib/claude_swarm/orchestrator.rb +452 -257
- data/lib/claude_swarm/session_cost_calculator.rb +130 -14
- data/lib/claude_swarm/session_path.rb +2 -8
- data/lib/claude_swarm/settings_generator.rb +77 -0
- data/lib/claude_swarm/system_utils.rb +6 -2
- data/lib/claude_swarm/templates/generation_prompt.md.erb +6 -6
- data/lib/claude_swarm/tools/task_tool.rb +13 -1
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +2 -2
- data/lib/claude_swarm.rb +23 -2
- data/team.yml +75 -3
- data/team_v2.yml +367 -0
- metadata +54 -5
|
@@ -4,26 +4,118 @@ module ClaudeSwarm
|
|
|
4
4
|
module SessionCostCalculator
|
|
5
5
|
extend self
|
|
6
6
|
|
|
7
|
+
# Model pricing in dollars per million tokens
|
|
8
|
+
MODEL_PRICING = {
|
|
9
|
+
opus: {
|
|
10
|
+
input: 15.0,
|
|
11
|
+
output: 75.0,
|
|
12
|
+
cache_write: 18.75,
|
|
13
|
+
cache_read: 1.50,
|
|
14
|
+
},
|
|
15
|
+
sonnet: {
|
|
16
|
+
input: 3.0,
|
|
17
|
+
output: 15.0,
|
|
18
|
+
cache_write: 3.75,
|
|
19
|
+
cache_read: 0.30,
|
|
20
|
+
},
|
|
21
|
+
haiku: {
|
|
22
|
+
input: 0.80,
|
|
23
|
+
output: 4.0,
|
|
24
|
+
cache_write: 1.0,
|
|
25
|
+
cache_read: 0.08,
|
|
26
|
+
},
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Determine model type from model name
|
|
30
|
+
def model_type_from_name(model_name)
|
|
31
|
+
return unless model_name
|
|
32
|
+
|
|
33
|
+
model_name_lower = model_name.downcase
|
|
34
|
+
if model_name_lower.include?("opus")
|
|
35
|
+
:opus
|
|
36
|
+
elsif model_name_lower.include?("sonnet")
|
|
37
|
+
:sonnet
|
|
38
|
+
elsif model_name_lower.include?("haiku")
|
|
39
|
+
:haiku
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Calculate cost from token usage
|
|
44
|
+
def calculate_token_cost(usage, model_name)
|
|
45
|
+
model_type = model_type_from_name(model_name)
|
|
46
|
+
return 0.0 unless model_type && usage
|
|
47
|
+
|
|
48
|
+
pricing = MODEL_PRICING[model_type]
|
|
49
|
+
return 0.0 unless pricing
|
|
50
|
+
|
|
51
|
+
cost = 0.0
|
|
52
|
+
|
|
53
|
+
# Regular input tokens
|
|
54
|
+
if usage["input_tokens"]
|
|
55
|
+
cost += (usage["input_tokens"] / 1_000_000.0) * pricing[:input]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Output tokens
|
|
59
|
+
if usage["output_tokens"]
|
|
60
|
+
cost += (usage["output_tokens"] / 1_000_000.0) * pricing[:output]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Cache creation tokens (write)
|
|
64
|
+
if usage["cache_creation_input_tokens"]
|
|
65
|
+
cost += (usage["cache_creation_input_tokens"] / 1_000_000.0) * pricing[:cache_write]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Cache read tokens
|
|
69
|
+
if usage["cache_read_input_tokens"]
|
|
70
|
+
cost += (usage["cache_read_input_tokens"] / 1_000_000.0) * pricing[:cache_read]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
cost
|
|
74
|
+
end
|
|
75
|
+
|
|
7
76
|
# Calculate total cost from session log file
|
|
8
77
|
# Returns a hash with:
|
|
9
|
-
# - total_cost: Total cost in USD
|
|
78
|
+
# - total_cost: Total cost in USD (sum of cost_usd for instances, token costs for main)
|
|
10
79
|
# - instances_with_cost: Set of instance names that have cost data
|
|
11
80
|
def calculate_total_cost(session_log_path)
|
|
12
81
|
return { total_cost: 0.0, instances_with_cost: Set.new } unless File.exist?(session_log_path)
|
|
13
82
|
|
|
14
|
-
|
|
83
|
+
# Track costs per instance - simple sum of cost_usd
|
|
84
|
+
instance_costs = {}
|
|
15
85
|
instances_with_cost = Set.new
|
|
86
|
+
main_instance_cost = 0.0
|
|
16
87
|
|
|
17
88
|
File.foreach(session_log_path) do |line|
|
|
18
|
-
data =
|
|
19
|
-
if data
|
|
20
|
-
|
|
21
|
-
|
|
89
|
+
data = JsonHandler.parse(line)
|
|
90
|
+
next if data == line # Skip unparseable lines
|
|
91
|
+
|
|
92
|
+
instance_name = data["instance"]
|
|
93
|
+
instance_id = data["instance_id"]
|
|
94
|
+
|
|
95
|
+
# Handle main instance token-based costs
|
|
96
|
+
if instance_id == "main" && data.dig("event", "type") == "assistant"
|
|
97
|
+
usage = data.dig("event", "message", "usage")
|
|
98
|
+
model = data.dig("event", "message", "model")
|
|
99
|
+
if usage && model
|
|
100
|
+
token_cost = calculate_token_cost(usage, model)
|
|
101
|
+
main_instance_cost += token_cost
|
|
102
|
+
instances_with_cost << instance_name if token_cost > 0
|
|
103
|
+
end
|
|
104
|
+
# Handle other instances with cost_usd (non-cumulative)
|
|
105
|
+
elsif instance_id != "main" && data.dig("event", "type") == "result"
|
|
106
|
+
# Use cost_usd (non-cumulative) instead of total_cost_usd (cumulative)
|
|
107
|
+
if (cost = data.dig("event", "cost_usd"))
|
|
108
|
+
instances_with_cost << instance_name
|
|
109
|
+
instance_costs[instance_name] ||= 0.0
|
|
110
|
+
instance_costs[instance_name] += cost
|
|
111
|
+
end
|
|
22
112
|
end
|
|
23
|
-
rescue JSON::ParserError
|
|
24
|
-
next
|
|
25
113
|
end
|
|
26
114
|
|
|
115
|
+
# Calculate total: sum of all instance costs + main instance token costs
|
|
116
|
+
other_instances_cost = instance_costs.values.sum
|
|
117
|
+
total_cost = other_instances_cost + main_instance_cost
|
|
118
|
+
|
|
27
119
|
{
|
|
28
120
|
total_cost: total_cost,
|
|
29
121
|
instances_with_cost: instances_with_cost,
|
|
@@ -39,11 +131,15 @@ module ClaudeSwarm
|
|
|
39
131
|
# Returns a hash of instances with their cost data and relationships
|
|
40
132
|
def parse_instance_hierarchy(session_log_path)
|
|
41
133
|
instances = {}
|
|
134
|
+
# Track main instance token costs
|
|
135
|
+
main_instance_costs = {}
|
|
42
136
|
|
|
43
137
|
return instances unless File.exist?(session_log_path)
|
|
44
138
|
|
|
45
139
|
File.foreach(session_log_path) do |line|
|
|
46
|
-
data =
|
|
140
|
+
data = JsonHandler.parse(line)
|
|
141
|
+
next if data == line # Skip unparseable lines
|
|
142
|
+
|
|
47
143
|
instance_name = data["instance"]
|
|
48
144
|
instance_id = data["instance_id"]
|
|
49
145
|
calling_instance = data["calling_instance"]
|
|
@@ -75,16 +171,36 @@ module ClaudeSwarm
|
|
|
75
171
|
instances[calling_instance][:calls_to] << instance_name
|
|
76
172
|
end
|
|
77
173
|
|
|
78
|
-
#
|
|
79
|
-
if data.dig("event", "type") == "
|
|
174
|
+
# Handle main instance token-based costs
|
|
175
|
+
if instance_id == "main" && data.dig("event", "type") == "assistant"
|
|
176
|
+
usage = data.dig("event", "message", "usage")
|
|
177
|
+
model = data.dig("event", "message", "model")
|
|
178
|
+
if usage && model
|
|
179
|
+
token_cost = calculate_token_cost(usage, model)
|
|
180
|
+
if token_cost > 0
|
|
181
|
+
main_instance_costs[instance_name] ||= 0.0
|
|
182
|
+
main_instance_costs[instance_name] += token_cost
|
|
183
|
+
instances[instance_name][:has_cost_data] = true
|
|
184
|
+
instances[instance_name][:calls] += 1
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
# Track costs and calls for non-main instances using cost_usd
|
|
188
|
+
elsif data.dig("event", "type") == "result" && instance_id != "main"
|
|
80
189
|
instances[instance_name][:calls] += 1
|
|
81
|
-
|
|
190
|
+
# Use cost_usd (non-cumulative) instead of total_cost_usd
|
|
191
|
+
if (cost = data.dig("event", "cost_usd"))
|
|
82
192
|
instances[instance_name][:cost] += cost
|
|
83
193
|
instances[instance_name][:has_cost_data] = true
|
|
84
194
|
end
|
|
85
195
|
end
|
|
86
|
-
|
|
87
|
-
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Set main instance costs (replace, don't add)
|
|
199
|
+
main_instance_costs.each do |name, cost|
|
|
200
|
+
if instances[name]
|
|
201
|
+
# For main instances, use ONLY token costs, not cumulative costs
|
|
202
|
+
instances[name][:cost] = cost
|
|
203
|
+
end
|
|
88
204
|
end
|
|
89
205
|
|
|
90
206
|
instances
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ClaudeSwarm
|
|
4
4
|
module SessionPath
|
|
5
|
-
SESSIONS_DIR = "sessions"
|
|
6
|
-
|
|
7
5
|
class << self
|
|
8
|
-
def swarm_home
|
|
9
|
-
ENV["CLAUDE_SWARM_HOME"] || File.expand_path("~/.claude-swarm")
|
|
10
|
-
end
|
|
11
|
-
|
|
12
6
|
# Convert a directory path to a safe folder name using + as separator
|
|
13
7
|
def project_folder_name(working_dir = Dir.pwd)
|
|
14
8
|
# Don't expand path if it's already expanded (avoids double expansion on Windows)
|
|
@@ -27,7 +21,7 @@ module ClaudeSwarm
|
|
|
27
21
|
# Generate a full session path for a given directory and session ID
|
|
28
22
|
def generate(working_dir: Dir.pwd, session_id: SecureRandom.uuid)
|
|
29
23
|
project_name = project_folder_name(working_dir)
|
|
30
|
-
|
|
24
|
+
ClaudeSwarm.joined_sessions_dir(project_name, session_id)
|
|
31
25
|
end
|
|
32
26
|
|
|
33
27
|
# Ensure the session directory exists
|
|
@@ -35,7 +29,7 @@ module ClaudeSwarm
|
|
|
35
29
|
FileUtils.mkdir_p(session_path)
|
|
36
30
|
|
|
37
31
|
# Add .gitignore to swarm home if it doesn't exist
|
|
38
|
-
gitignore_path =
|
|
32
|
+
gitignore_path = ClaudeSwarm.joined_home_dir(".gitignore")
|
|
39
33
|
File.write(gitignore_path, "*\n") unless File.exist?(gitignore_path)
|
|
40
34
|
end
|
|
41
35
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeSwarm
|
|
4
|
+
class SettingsGenerator
|
|
5
|
+
def initialize(configuration)
|
|
6
|
+
@config = configuration
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def generate_all
|
|
10
|
+
ensure_session_directory
|
|
11
|
+
|
|
12
|
+
@config.instances.each do |name, instance|
|
|
13
|
+
generate_settings(name, instance)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def settings_path(instance_name)
|
|
18
|
+
File.join(session_path, "#{instance_name}_settings.json")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def session_path
|
|
24
|
+
@session_path ||= SessionPath.from_env
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ensure_session_directory
|
|
28
|
+
# Session directory is already created by orchestrator
|
|
29
|
+
# Just ensure it exists
|
|
30
|
+
SessionPath.ensure_directory(session_path)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_settings(name, instance)
|
|
34
|
+
settings = {}
|
|
35
|
+
|
|
36
|
+
# Add hooks if configured
|
|
37
|
+
if instance[:hooks] && !instance[:hooks].empty?
|
|
38
|
+
settings["hooks"] = instance[:hooks]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Add SessionStart hook for main instance to capture transcript path
|
|
42
|
+
if name == @config.main_instance
|
|
43
|
+
session_start_hook = build_session_start_hook
|
|
44
|
+
|
|
45
|
+
# Initialize hooks if not present
|
|
46
|
+
settings["hooks"] ||= {}
|
|
47
|
+
settings["hooks"]["SessionStart"] ||= []
|
|
48
|
+
|
|
49
|
+
# Add our hook to the SessionStart hooks
|
|
50
|
+
settings["hooks"]["SessionStart"] << session_start_hook
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Only write settings file if there are settings to write
|
|
54
|
+
return if settings.empty?
|
|
55
|
+
|
|
56
|
+
# Write settings file
|
|
57
|
+
JsonHandler.write_file!(settings_path(name), settings)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def build_session_start_hook
|
|
61
|
+
hook_script_path = File.expand_path("hooks/session_start_hook.rb", __dir__)
|
|
62
|
+
# Pass session path as an argument since ENV may not be inherited
|
|
63
|
+
session_path_arg = session_path
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
"matcher" => "startup",
|
|
67
|
+
"hooks" => [
|
|
68
|
+
{
|
|
69
|
+
"type" => "command",
|
|
70
|
+
"command" => "ruby #{hook_script_path} '#{session_path_arg}'",
|
|
71
|
+
"timeout" => 5,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -7,8 +7,12 @@ module ClaudeSwarm
|
|
|
7
7
|
unless success
|
|
8
8
|
exit_status = $CHILD_STATUS&.exitstatus || 1
|
|
9
9
|
command_str = args.size == 1 ? args.first : args.join(" ")
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if exit_status == 143 # timeout command exit status = 128 + 15 (SIGTERM)
|
|
11
|
+
warn("⏱️ Command timeout: #{command_str}")
|
|
12
|
+
else
|
|
13
|
+
warn("❌ Command failed with exit status: #{exit_status}")
|
|
14
|
+
raise Error, "Command failed with exit status #{exit_status}: #{command_str}"
|
|
15
|
+
end
|
|
12
16
|
end
|
|
13
17
|
success
|
|
14
18
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
You are a Claude Swarm configuration generator assistant. Your role is to help the user create a well-structured
|
|
1
|
+
You are a Claude Swarm configuration generator assistant. Your role is to help the user create a well-structured swarm YAML file through an interactive conversation.
|
|
2
2
|
|
|
3
3
|
## Claude Swarm Overview
|
|
4
4
|
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).
|
|
@@ -10,6 +10,7 @@ Key capabilities:
|
|
|
10
10
|
- Run instances in different directories or Git worktrees
|
|
11
11
|
- Support for custom system prompts per instance
|
|
12
12
|
- Choose appropriate models (opus for complex tasks, sonnet for simpler ones)
|
|
13
|
+
- Support for OpenAI instances
|
|
13
14
|
|
|
14
15
|
## Your Task
|
|
15
16
|
1. Start by asking about the user's project structure and development needs
|
|
@@ -43,7 +44,7 @@ swarm:
|
|
|
43
44
|
instance_name:
|
|
44
45
|
description: "Clear description of role and responsibilities"
|
|
45
46
|
directory: ./path/to/directory
|
|
46
|
-
model:
|
|
47
|
+
model: opus
|
|
47
48
|
allowed_tools: [Read, Edit, Write, Bash]
|
|
48
49
|
connections: [other_instance_names] # Optional
|
|
49
50
|
prompt: |
|
|
@@ -57,7 +58,7 @@ swarm:
|
|
|
57
58
|
- Write clear descriptions explaining each instance's responsibilities
|
|
58
59
|
- Choose opus model for complex architectural or algorithmic tasks and routine development.
|
|
59
60
|
- Choose sonnet model for simpler tasks
|
|
60
|
-
- Set up logical connections (e.g., lead → team members, architect → implementers), but
|
|
61
|
+
- Set up logical connections (e.g., lead → team members, architect → implementers), but do not create circular dependencies.
|
|
61
62
|
- 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.`
|
|
62
63
|
- Select tools based on each instance's actual needs:
|
|
63
64
|
- Read: For code review and analysis roles
|
|
@@ -72,7 +73,6 @@ swarm:
|
|
|
72
73
|
|
|
73
74
|
## Interactive Questions to Ask
|
|
74
75
|
- What type of project are you working on?
|
|
75
|
-
- What's your project's directory structure?
|
|
76
76
|
- What are the main technologies/frameworks you're using?
|
|
77
77
|
- What development tasks do you need help with?
|
|
78
78
|
- Do you need specialized roles (testing, DevOps, documentation)?
|
|
@@ -226,5 +226,5 @@ The more precisely you explain what you want, the better Claude's response will
|
|
|
226
226
|
|
|
227
227
|
</prompt_best_practices>
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
|
|
230
|
+
IMPORTANT: Do not generate swarms with circular dependencies. For example, instance A connections to instance B, and instance B connections to instance A.
|
|
@@ -43,8 +43,20 @@ module ClaudeSwarm
|
|
|
43
43
|
|
|
44
44
|
response = executor.execute(final_prompt, options)
|
|
45
45
|
|
|
46
|
+
# Validate the response has a result
|
|
47
|
+
unless response.is_a?(Hash) && response.key?("result")
|
|
48
|
+
raise "Invalid response from executor: missing 'result' field. Response structure: #{response.keys.join(", ")}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
result = response["result"]
|
|
52
|
+
|
|
53
|
+
# Validate the result is not empty
|
|
54
|
+
if result.nil? || (result.is_a?(String) && result.strip.empty?)
|
|
55
|
+
raise "Agent #{instance_config[:name]} returned an empty response. The task was executed but no content was provided."
|
|
56
|
+
end
|
|
57
|
+
|
|
46
58
|
# Return just the result text as expected by MCP
|
|
47
|
-
|
|
59
|
+
result
|
|
48
60
|
end
|
|
49
61
|
end
|
|
50
62
|
end
|
data/lib/claude_swarm/version.rb
CHANGED
|
@@ -158,7 +158,7 @@ module ClaudeSwarm
|
|
|
158
158
|
# Remove session-specific worktree directory if it exists and is empty
|
|
159
159
|
return unless @session_id
|
|
160
160
|
|
|
161
|
-
session_worktree_dir =
|
|
161
|
+
session_worktree_dir = ClaudeSwarm.joined_worktrees_dir(@session_id)
|
|
162
162
|
return unless File.exist?(session_worktree_dir)
|
|
163
163
|
|
|
164
164
|
# Try to remove the directory tree
|
|
@@ -214,7 +214,7 @@ module ClaudeSwarm
|
|
|
214
214
|
unique_repo_name = "#{repo_name}-#{path_hash}"
|
|
215
215
|
|
|
216
216
|
# Build external path: ~/.claude-swarm/worktrees/[session_id]/[repo_name-hash]/[worktree_name]
|
|
217
|
-
base_dir =
|
|
217
|
+
base_dir = ClaudeSwarm.joined_worktrees_dir
|
|
218
218
|
|
|
219
219
|
# Validate base directory is accessible
|
|
220
220
|
begin
|
data/lib/claude_swarm.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Standard library dependencies
|
|
4
|
+
require "bundler"
|
|
4
5
|
require "digest"
|
|
5
6
|
require "English"
|
|
6
7
|
require "erb"
|
|
@@ -20,9 +21,9 @@ require "tmpdir"
|
|
|
20
21
|
require "yaml"
|
|
21
22
|
|
|
22
23
|
# External dependencies
|
|
24
|
+
require "claude_sdk"
|
|
23
25
|
require "fast_mcp_annotations"
|
|
24
26
|
require "mcp_client"
|
|
25
|
-
require "openai"
|
|
26
27
|
require "thor"
|
|
27
28
|
|
|
28
29
|
# Zeitwerk setup
|
|
@@ -40,7 +41,27 @@ module ClaudeSwarm
|
|
|
40
41
|
|
|
41
42
|
class << self
|
|
42
43
|
def root_dir
|
|
43
|
-
ENV.fetch("CLAUDE_SWARM_ROOT_DIR"
|
|
44
|
+
ENV.fetch("CLAUDE_SWARM_ROOT_DIR") { Dir.pwd }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def home_dir
|
|
48
|
+
ENV.fetch("CLAUDE_SWARM_HOME") { File.expand_path("~/.claude-swarm") }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def joined_home_dir(*strings)
|
|
52
|
+
File.join(home_dir, *strings)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def joined_run_dir(*strings)
|
|
56
|
+
joined_home_dir("run", *strings)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def joined_sessions_dir(*strings)
|
|
60
|
+
joined_home_dir("sessions", *strings)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def joined_worktrees_dir(*strings)
|
|
64
|
+
joined_home_dir("worktrees", *strings)
|
|
44
65
|
end
|
|
45
66
|
end
|
|
46
67
|
end
|
data/team.yml
CHANGED
|
@@ -8,10 +8,20 @@ swarm:
|
|
|
8
8
|
directory: .
|
|
9
9
|
model: opus
|
|
10
10
|
vibe: true
|
|
11
|
-
connections: [github_expert, fast_mcp_expert, ruby_mcp_client_expert, openai_api_expert]
|
|
11
|
+
connections: [github_expert, fast_mcp_expert, ruby_mcp_client_expert, openai_api_expert, claude_code_sdk_expert]
|
|
12
12
|
prompt: |
|
|
13
13
|
You are the lead developer of Claude Swarm, a Ruby gem that orchestrates multiple Claude Code instances as a collaborative AI development team. The gem enables running AI agents with specialized roles, tools, and directory contexts, communicating via MCP (Model Context Protocol) in a tree-like hierarchy.
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
IMPORTANT: Use your specialized team members for their areas of expertise. Each team member has deep knowledge in their domain:
|
|
16
|
+
|
|
17
|
+
Team Member Usage Guide:
|
|
18
|
+
- **github_expert**: Use for all Git and GitHub operations including creating issues, PRs, managing releases, checking CI/CD workflows, and repository management
|
|
19
|
+
- **fast_mcp_expert**: Use for MCP server development, tool creation, resource management, and any FastMCP-related architecture decisions
|
|
20
|
+
- **ruby_mcp_client_expert**: Use for MCP client integration, multi-transport connectivity, authentication flows, and ruby-mcp-client library guidance
|
|
21
|
+
- **openai_api_expert**: Use for OpenAI API integration, ruby-openai gem usage, model configuration, and OpenAI provider support in Claude Swarm
|
|
22
|
+
- **claude_code_sdk_expert**: Use for Claude Code SDK integration, programmatic Claude Code usage, client configuration, and SDK development patterns
|
|
23
|
+
|
|
24
|
+
Always delegate specialized tasks to the appropriate team member rather than handling everything yourself. This ensures the highest quality solutions and leverages each expert's deep domain knowledge.
|
|
15
25
|
|
|
16
26
|
Your responsibilities include:
|
|
17
27
|
- Developing new features and improvements for the Claude Swarm gem
|
|
@@ -254,4 +264,66 @@ swarm:
|
|
|
254
264
|
- Set up CI to run code_quality checks
|
|
255
265
|
- Document Raix integration in wiki/docs
|
|
256
266
|
|
|
257
|
-
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
|
267
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
|
268
|
+
|
|
269
|
+
claude_code_sdk_expert:
|
|
270
|
+
description: "Expert in Claude Code SDK for Ruby, specializing in client integration and API usage patterns"
|
|
271
|
+
directory: ~/src/github.com/parruda/claude-code-sdk-ruby
|
|
272
|
+
model: opus
|
|
273
|
+
vibe: true
|
|
274
|
+
prompt: |
|
|
275
|
+
You are an expert in the Claude Code SDK for Ruby, specializing in client integration, API usage patterns, and SDK development best practices.
|
|
276
|
+
|
|
277
|
+
Your expertise covers:
|
|
278
|
+
- Claude Code SDK architecture and client configuration
|
|
279
|
+
- API authentication and session management
|
|
280
|
+
- Request/response handling and error management
|
|
281
|
+
- Code generation and analysis capabilities
|
|
282
|
+
- SDK integration patterns and best practices
|
|
283
|
+
- Tool usage and permission management
|
|
284
|
+
- Streaming responses and real-time interactions
|
|
285
|
+
- Multi-modal capabilities (text, code, images)
|
|
286
|
+
- Rate limiting and cost optimization
|
|
287
|
+
- Testing and debugging SDK integrations
|
|
288
|
+
|
|
289
|
+
Key responsibilities:
|
|
290
|
+
- Analyze Claude Code SDK codebase for implementation patterns
|
|
291
|
+
- Provide guidance on proper SDK usage and integration
|
|
292
|
+
- Design robust client configurations and authentication flows
|
|
293
|
+
- Implement efficient request handling and error recovery
|
|
294
|
+
- Optimize SDK performance and resource usage
|
|
295
|
+
- Create comprehensive examples and documentation
|
|
296
|
+
- Troubleshoot integration issues and API errors
|
|
297
|
+
- Ensure proper handling of streaming and async operations
|
|
298
|
+
|
|
299
|
+
Technical focus areas:
|
|
300
|
+
- Client initialization and configuration options
|
|
301
|
+
- Authentication flows and token management
|
|
302
|
+
- Request builders and parameter validation
|
|
303
|
+
- Response parsing and error handling
|
|
304
|
+
- Streaming implementations and chunk processing
|
|
305
|
+
- Tool integration and permission management
|
|
306
|
+
- Session management and state persistence
|
|
307
|
+
- Performance optimization and caching strategies
|
|
308
|
+
- Testing patterns and mock configurations
|
|
309
|
+
|
|
310
|
+
When providing guidance:
|
|
311
|
+
- Reference specific SDK methods and classes
|
|
312
|
+
- Include practical code examples and usage patterns
|
|
313
|
+
- Explain both SDK abstractions and underlying API details
|
|
314
|
+
- Highlight important configuration options and their implications
|
|
315
|
+
- Warn about common pitfalls and API limitations
|
|
316
|
+
- Suggest performance optimizations and cost-saving strategies
|
|
317
|
+
- Provide context on when to use different SDK features
|
|
318
|
+
- Demonstrate proper error handling and retry strategies
|
|
319
|
+
|
|
320
|
+
Integration with Claude Swarm development:
|
|
321
|
+
- Understand how Claude Code SDK can enhance swarm capabilities
|
|
322
|
+
- Provide insights on programmatic Claude Code integration
|
|
323
|
+
- Design patterns for automated swarm management
|
|
324
|
+
- Optimize SDK usage within swarm orchestration
|
|
325
|
+
- Ensure compatibility with swarm session management
|
|
326
|
+
|
|
327
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
|
328
|
+
|
|
329
|
+
Help developers integrate Claude Code SDK effectively with confidence, best practices, and optimal performance.
|