claude_swarm 0.1.17 → 0.1.19
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/CHANGELOG.md +50 -0
- data/CLAUDE.md +48 -0
- data/README.md +143 -2
- data/claude-swarm.yml +54 -16
- data/examples/with-before-commands.yml +30 -0
- data/lib/claude_swarm/cli.rb +131 -3
- data/lib/claude_swarm/commands/ps.rb +53 -1
- data/lib/claude_swarm/configuration.rb +14 -1
- data/lib/claude_swarm/orchestrator.rb +165 -2
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm/worktree_manager.rb +353 -0
- data/llms.txt +2 -2
- data/single.yml +92 -0
- metadata +4 -1
@@ -0,0 +1,353 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
require "fileutils"
|
5
|
+
require "json"
|
6
|
+
require "pathname"
|
7
|
+
require "securerandom"
|
8
|
+
|
9
|
+
module ClaudeSwarm
|
10
|
+
class WorktreeManager
|
11
|
+
attr_reader :shared_worktree_name, :created_worktrees
|
12
|
+
|
13
|
+
def initialize(cli_worktree_option = nil, session_id: nil)
|
14
|
+
@cli_worktree_option = cli_worktree_option
|
15
|
+
@session_id = session_id
|
16
|
+
# Generate a name based on session ID if no option given, empty string, or default "worktree" from Thor
|
17
|
+
@shared_worktree_name = if cli_worktree_option.nil? || cli_worktree_option.empty? || cli_worktree_option == "worktree"
|
18
|
+
generate_worktree_name
|
19
|
+
else
|
20
|
+
cli_worktree_option
|
21
|
+
end
|
22
|
+
@created_worktrees = {} # Maps "repo_root:worktree_name" to worktree_path
|
23
|
+
@instance_worktree_configs = {} # Stores per-instance worktree settings
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup_worktrees(instances)
|
27
|
+
# First pass: determine worktree configuration for each instance
|
28
|
+
instances.each do |instance|
|
29
|
+
worktree_config = determine_worktree_config(instance)
|
30
|
+
@instance_worktree_configs[instance[:name]] = worktree_config
|
31
|
+
end
|
32
|
+
|
33
|
+
# Second pass: create necessary worktrees
|
34
|
+
worktrees_to_create = collect_worktrees_to_create(instances)
|
35
|
+
worktrees_to_create.each do |repo_root, worktree_name|
|
36
|
+
create_worktree(repo_root, worktree_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Third pass: map instance directories to worktree paths
|
40
|
+
instances.each do |instance|
|
41
|
+
worktree_config = @instance_worktree_configs[instance[:name]]
|
42
|
+
|
43
|
+
if ENV["CLAUDE_SWARM_DEBUG"]
|
44
|
+
puts "Debug [WorktreeManager]: Processing instance #{instance[:name]}"
|
45
|
+
puts "Debug [WorktreeManager]: Worktree config: #{worktree_config.inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
next if worktree_config[:skip]
|
49
|
+
|
50
|
+
worktree_name = worktree_config[:name]
|
51
|
+
original_dirs = instance[:directories] || [instance[:directory]]
|
52
|
+
mapped_dirs = original_dirs.map { |dir| map_to_worktree_path(dir, worktree_name) }
|
53
|
+
|
54
|
+
if ENV["CLAUDE_SWARM_DEBUG"]
|
55
|
+
puts "Debug [WorktreeManager]: Original dirs: #{original_dirs.inspect}"
|
56
|
+
puts "Debug [WorktreeManager]: Mapped dirs: #{mapped_dirs.inspect}"
|
57
|
+
end
|
58
|
+
|
59
|
+
if instance[:directories]
|
60
|
+
instance[:directories] = mapped_dirs
|
61
|
+
# Also update the single directory field for backward compatibility
|
62
|
+
instance[:directory] = mapped_dirs.first
|
63
|
+
else
|
64
|
+
instance[:directory] = mapped_dirs.first
|
65
|
+
end
|
66
|
+
|
67
|
+
puts "Debug [WorktreeManager]: Updated instance[:directory] to: #{instance[:directory]}" if ENV["CLAUDE_SWARM_DEBUG"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def map_to_worktree_path(original_path, worktree_name)
|
72
|
+
return original_path unless original_path
|
73
|
+
|
74
|
+
expanded_path = File.expand_path(original_path)
|
75
|
+
repo_root = find_git_root(expanded_path)
|
76
|
+
|
77
|
+
if ENV["CLAUDE_SWARM_DEBUG"]
|
78
|
+
puts "Debug [map_to_worktree_path]: Original path: #{original_path}"
|
79
|
+
puts "Debug [map_to_worktree_path]: Expanded path: #{expanded_path}"
|
80
|
+
puts "Debug [map_to_worktree_path]: Repo root: #{repo_root}"
|
81
|
+
end
|
82
|
+
|
83
|
+
return original_path unless repo_root
|
84
|
+
|
85
|
+
# Check if we have a worktree for this repo and name
|
86
|
+
worktree_key = "#{repo_root}:#{worktree_name}"
|
87
|
+
worktree_path = @created_worktrees[worktree_key]
|
88
|
+
|
89
|
+
if ENV["CLAUDE_SWARM_DEBUG"]
|
90
|
+
puts "Debug [map_to_worktree_path]: Worktree key: #{worktree_key}"
|
91
|
+
puts "Debug [map_to_worktree_path]: Worktree path: #{worktree_path}"
|
92
|
+
puts "Debug [map_to_worktree_path]: Created worktrees: #{@created_worktrees.inspect}"
|
93
|
+
end
|
94
|
+
|
95
|
+
return original_path unless worktree_path
|
96
|
+
|
97
|
+
# Calculate relative path from repo root
|
98
|
+
relative_path = Pathname.new(expanded_path).relative_path_from(Pathname.new(repo_root)).to_s
|
99
|
+
|
100
|
+
# Return the equivalent path in the worktree
|
101
|
+
result = if relative_path == "."
|
102
|
+
worktree_path
|
103
|
+
else
|
104
|
+
File.join(worktree_path, relative_path)
|
105
|
+
end
|
106
|
+
|
107
|
+
puts "Debug [map_to_worktree_path]: Result: #{result}" if ENV["CLAUDE_SWARM_DEBUG"]
|
108
|
+
|
109
|
+
result
|
110
|
+
end
|
111
|
+
|
112
|
+
def cleanup_worktrees
|
113
|
+
@created_worktrees.each do |worktree_key, worktree_path|
|
114
|
+
repo_root = worktree_key.split(":", 2).first
|
115
|
+
next unless File.exist?(worktree_path)
|
116
|
+
|
117
|
+
# Check for uncommitted changes
|
118
|
+
if has_uncommitted_changes?(worktree_path)
|
119
|
+
puts "⚠️ Warning: Worktree has uncommitted changes, skipping cleanup: #{worktree_path}" unless ENV["CLAUDE_SWARM_PROMPT"]
|
120
|
+
next
|
121
|
+
end
|
122
|
+
|
123
|
+
# Check for unpushed commits
|
124
|
+
if has_unpushed_commits?(worktree_path)
|
125
|
+
puts "⚠️ Warning: Worktree has unpushed commits, skipping cleanup: #{worktree_path}" unless ENV["CLAUDE_SWARM_PROMPT"]
|
126
|
+
next
|
127
|
+
end
|
128
|
+
|
129
|
+
puts "Removing worktree: #{worktree_path}" unless ENV["CLAUDE_SWARM_PROMPT"]
|
130
|
+
|
131
|
+
# Remove the worktree
|
132
|
+
output, status = Open3.capture2e("git", "-C", repo_root, "worktree", "remove", worktree_path)
|
133
|
+
next if status.success?
|
134
|
+
|
135
|
+
puts "Warning: Failed to remove worktree: #{output}"
|
136
|
+
# Try force remove
|
137
|
+
output, status = Open3.capture2e("git", "-C", repo_root, "worktree", "remove", "--force", worktree_path)
|
138
|
+
puts "Force remove result: #{output}" unless status.success?
|
139
|
+
end
|
140
|
+
rescue StandardError => e
|
141
|
+
puts "Error during worktree cleanup: #{e.message}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def session_metadata
|
145
|
+
{
|
146
|
+
enabled: true,
|
147
|
+
shared_name: @shared_worktree_name,
|
148
|
+
created_paths: @created_worktrees.dup,
|
149
|
+
instance_configs: @instance_worktree_configs.dup
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
# Deprecated method for backward compatibility
|
154
|
+
def worktree_name
|
155
|
+
@shared_worktree_name
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def generate_worktree_name
|
161
|
+
# Use session ID if available, otherwise generate a random suffix
|
162
|
+
if @session_id
|
163
|
+
"worktree-#{@session_id}"
|
164
|
+
else
|
165
|
+
# Fallback to random suffix for tests or when session ID is not available
|
166
|
+
random_suffix = SecureRandom.alphanumeric(5).downcase
|
167
|
+
"worktree-#{random_suffix}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def determine_worktree_config(instance)
|
172
|
+
# Check instance-level worktree setting
|
173
|
+
instance_worktree = instance[:worktree]
|
174
|
+
|
175
|
+
if instance_worktree.nil?
|
176
|
+
# No instance-level setting, follow CLI behavior
|
177
|
+
if @cli_worktree_option.nil?
|
178
|
+
{ skip: true }
|
179
|
+
else
|
180
|
+
{ skip: false, name: @shared_worktree_name }
|
181
|
+
end
|
182
|
+
elsif instance_worktree == false
|
183
|
+
# Explicitly disabled for this instance
|
184
|
+
{ skip: true }
|
185
|
+
elsif instance_worktree == true
|
186
|
+
# Use shared worktree (either from CLI or auto-generated)
|
187
|
+
{ skip: false, name: @shared_worktree_name }
|
188
|
+
elsif instance_worktree.is_a?(String)
|
189
|
+
# Use custom worktree name
|
190
|
+
{ skip: false, name: instance_worktree }
|
191
|
+
else
|
192
|
+
raise Error, "Invalid worktree configuration for instance '#{instance[:name]}': #{instance_worktree.inspect}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def collect_worktrees_to_create(instances)
|
197
|
+
worktrees_needed = {}
|
198
|
+
|
199
|
+
instances.each do |instance|
|
200
|
+
worktree_config = @instance_worktree_configs[instance[:name]]
|
201
|
+
next if worktree_config[:skip]
|
202
|
+
|
203
|
+
worktree_name = worktree_config[:name]
|
204
|
+
directories = instance[:directories] || [instance[:directory]]
|
205
|
+
|
206
|
+
directories.each do |dir|
|
207
|
+
next unless dir
|
208
|
+
|
209
|
+
expanded_dir = File.expand_path(dir)
|
210
|
+
repo_root = find_git_root(expanded_dir)
|
211
|
+
next unless repo_root
|
212
|
+
|
213
|
+
# Track unique repo_root:worktree_name combinations
|
214
|
+
worktrees_needed[repo_root] ||= Set.new
|
215
|
+
worktrees_needed[repo_root].add(worktree_name)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Convert to array of [repo_root, worktree_name] pairs
|
220
|
+
result = []
|
221
|
+
worktrees_needed.each do |repo_root, worktree_names|
|
222
|
+
worktree_names.each do |worktree_name|
|
223
|
+
result << [repo_root, worktree_name]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
result
|
227
|
+
end
|
228
|
+
|
229
|
+
def find_git_root(path)
|
230
|
+
current = File.expand_path(path)
|
231
|
+
|
232
|
+
while current != "/"
|
233
|
+
return current if File.exist?(File.join(current, ".git"))
|
234
|
+
|
235
|
+
current = File.dirname(current)
|
236
|
+
end
|
237
|
+
|
238
|
+
nil
|
239
|
+
end
|
240
|
+
|
241
|
+
def create_worktree(repo_root, worktree_name)
|
242
|
+
worktree_key = "#{repo_root}:#{worktree_name}"
|
243
|
+
# Create worktrees inside the repository in a .worktrees directory
|
244
|
+
worktree_base_dir = File.join(repo_root, ".worktrees")
|
245
|
+
worktree_path = File.join(worktree_base_dir, worktree_name)
|
246
|
+
|
247
|
+
# Check if worktree already exists
|
248
|
+
if File.exist?(worktree_path)
|
249
|
+
puts "Using existing worktree: #{worktree_path}" unless ENV["CLAUDE_SWARM_PROMPT"]
|
250
|
+
@created_worktrees[worktree_key] = worktree_path
|
251
|
+
return
|
252
|
+
end
|
253
|
+
|
254
|
+
# Ensure .worktrees directory exists
|
255
|
+
FileUtils.mkdir_p(worktree_base_dir)
|
256
|
+
|
257
|
+
# Create .gitignore inside .worktrees to ignore all contents
|
258
|
+
gitignore_path = File.join(worktree_base_dir, ".gitignore")
|
259
|
+
File.write(gitignore_path, "# Ignore all worktree contents\n*\n") unless File.exist?(gitignore_path)
|
260
|
+
|
261
|
+
# Get current branch
|
262
|
+
output, status = Open3.capture2e("git", "-C", repo_root, "rev-parse", "--abbrev-ref", "HEAD")
|
263
|
+
raise Error, "Failed to get current branch in #{repo_root}: #{output}" unless status.success?
|
264
|
+
|
265
|
+
current_branch = output.strip
|
266
|
+
|
267
|
+
# Create worktree with a new branch based on current branch
|
268
|
+
branch_name = worktree_name
|
269
|
+
puts "Creating worktree: #{worktree_path} with branch: #{branch_name}" unless ENV["CLAUDE_SWARM_PROMPT"]
|
270
|
+
|
271
|
+
# Create worktree with a new branch
|
272
|
+
output, status = Open3.capture2e("git", "-C", repo_root, "worktree", "add", "-b", branch_name, worktree_path, current_branch)
|
273
|
+
|
274
|
+
# If branch already exists, try without -b flag
|
275
|
+
if !status.success? && output.include?("already exists")
|
276
|
+
puts "Branch #{branch_name} already exists, using existing branch" unless ENV["CLAUDE_SWARM_PROMPT"]
|
277
|
+
output, status = Open3.capture2e("git", "-C", repo_root, "worktree", "add", worktree_path, branch_name)
|
278
|
+
end
|
279
|
+
|
280
|
+
raise Error, "Failed to create worktree: #{output}" unless status.success?
|
281
|
+
|
282
|
+
@created_worktrees[worktree_key] = worktree_path
|
283
|
+
end
|
284
|
+
|
285
|
+
def has_uncommitted_changes?(worktree_path)
|
286
|
+
# Check if there are any uncommitted changes (staged or unstaged)
|
287
|
+
output, status = Open3.capture2e("git", "-C", worktree_path, "status", "--porcelain")
|
288
|
+
return false unless status.success?
|
289
|
+
|
290
|
+
# If output is not empty, there are changes
|
291
|
+
!output.strip.empty?
|
292
|
+
end
|
293
|
+
|
294
|
+
def has_unpushed_commits?(worktree_path)
|
295
|
+
# Get the current branch
|
296
|
+
branch_output, branch_status = Open3.capture2e("git", "-C", worktree_path, "rev-parse", "--abbrev-ref", "HEAD")
|
297
|
+
return false unless branch_status.success?
|
298
|
+
|
299
|
+
current_branch = branch_output.strip
|
300
|
+
|
301
|
+
# Check if the branch has an upstream
|
302
|
+
_, upstream_status = Open3.capture2e("git", "-C", worktree_path, "rev-parse", "--abbrev-ref", "#{current_branch}@{upstream}")
|
303
|
+
|
304
|
+
# If no upstream, check if there are any commits on this branch
|
305
|
+
unless upstream_status.success?
|
306
|
+
# Get the base branch (usually main or master)
|
307
|
+
base_branch = find_base_branch(worktree_path)
|
308
|
+
|
309
|
+
# If we can't find a base branch or this IS the base branch, check if there are any commits at all
|
310
|
+
if base_branch.nil? || current_branch == base_branch
|
311
|
+
# Check if this branch has any commits
|
312
|
+
commits_output, commits_status = Open3.capture2e("git", "-C", worktree_path, "rev-list", "--count", "HEAD")
|
313
|
+
return false unless commits_status.success?
|
314
|
+
|
315
|
+
# If there's more than 0 commits and no upstream, they're unpushed
|
316
|
+
return commits_output.strip.to_i.positive?
|
317
|
+
end
|
318
|
+
|
319
|
+
# Check if this branch has any commits not on the base branch
|
320
|
+
commits_output, commits_status = Open3.capture2e("git", "-C", worktree_path, "rev-list", "HEAD", "^#{base_branch}")
|
321
|
+
return false unless commits_status.success?
|
322
|
+
|
323
|
+
# If there are commits, they're unpushed (no upstream set)
|
324
|
+
return !commits_output.strip.empty?
|
325
|
+
end
|
326
|
+
|
327
|
+
# Check for unpushed commits
|
328
|
+
unpushed_output, unpushed_status = Open3.capture2e("git", "-C", worktree_path, "rev-list", "HEAD", "^#{current_branch}@{upstream}")
|
329
|
+
return false unless unpushed_status.success?
|
330
|
+
|
331
|
+
# If output is not empty, there are unpushed commits
|
332
|
+
!unpushed_output.strip.empty?
|
333
|
+
end
|
334
|
+
|
335
|
+
def find_base_branch(repo_path)
|
336
|
+
# Try to find the base branch - check for main, master, or the default branch
|
337
|
+
%w[main master].each do |branch|
|
338
|
+
_, status = Open3.capture2e("git", "-C", repo_path, "rev-parse", "--verify", "refs/heads/#{branch}")
|
339
|
+
return branch if status.success?
|
340
|
+
end
|
341
|
+
|
342
|
+
# Try to get the default branch from HEAD
|
343
|
+
output, status = Open3.capture2e("git", "-C", repo_path, "symbolic-ref", "refs/remotes/origin/HEAD")
|
344
|
+
if status.success?
|
345
|
+
# Extract branch name from refs/remotes/origin/main
|
346
|
+
branch_match = output.strip.match(%r{refs/remotes/origin/(.+)$})
|
347
|
+
return branch_match[1] if branch_match
|
348
|
+
end
|
349
|
+
|
350
|
+
nil
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
data/llms.txt
CHANGED
@@ -57,7 +57,7 @@ A collection of Claude instances (agents) working together. One instance is desi
|
|
57
57
|
An individual Claude Code agent with:
|
58
58
|
- **description** (required): Role and responsibilities
|
59
59
|
- **directory**: Working directory context
|
60
|
-
- **model**: Claude model (opus/sonnet/haiku)
|
60
|
+
- **model**: Claude model (opus/sonnet/claude-3-5-haiku-20241022)
|
61
61
|
- **connections**: Other instances it can delegate to
|
62
62
|
- **allowed_tools**: Tools this instance can use
|
63
63
|
- **disallowed_tools**: Explicitly denied tools (override allowed)
|
@@ -79,7 +79,7 @@ swarm:
|
|
79
79
|
instance_name:
|
80
80
|
description: "Agent role description" # REQUIRED
|
81
81
|
directory: ~/path/to/dir # Working directory
|
82
|
-
model: opus # opus/sonnet/haiku
|
82
|
+
model: opus # opus/sonnet/claude-3-5-haiku-20241022
|
83
83
|
connections: [other1, other2] # Connected instances
|
84
84
|
prompt: "Custom system prompt" # Additional instructions
|
85
85
|
vibe: false # Skip permissions (default: false)
|
data/single.yml
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
version: 1
|
2
|
+
swarm:
|
3
|
+
name: "Claude Swarm Development"
|
4
|
+
main: lead_developer
|
5
|
+
instances:
|
6
|
+
lead_developer:
|
7
|
+
description: "Lead developer responsible for developing and maintaining the Claude Swarm gem"
|
8
|
+
directory: .
|
9
|
+
model: opus
|
10
|
+
vibe: true
|
11
|
+
connections: [github_expert]
|
12
|
+
prompt: |
|
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
|
+
Use the github_expert to help you with git and github related tasks.
|
15
|
+
|
16
|
+
Your responsibilities include:
|
17
|
+
- Developing new features and improvements for the Claude Swarm gem
|
18
|
+
- Writing clean, maintainable Ruby code following best practices
|
19
|
+
- Creating and updating tests using RSpec or similar testing frameworks
|
20
|
+
- Maintaining comprehensive documentation in README.md and code comments
|
21
|
+
- Managing the gem's dependencies and version compatibility
|
22
|
+
- Implementing robust error handling and validation
|
23
|
+
- Optimizing performance and resource usage
|
24
|
+
- Ensuring the CLI interface is intuitive and user-friendly
|
25
|
+
- Debugging issues and fixing bugs reported by users
|
26
|
+
- Reviewing and refactoring existing code for better maintainability
|
27
|
+
|
28
|
+
Key technical areas to focus on:
|
29
|
+
- YAML configuration parsing and validation
|
30
|
+
- MCP (Model Context Protocol) server implementation
|
31
|
+
- Session management and persistence
|
32
|
+
- Inter-instance communication mechanisms
|
33
|
+
- CLI command handling and option parsing
|
34
|
+
- Git worktree integration
|
35
|
+
- Cost tracking and monitoring features
|
36
|
+
- Process management and cleanup
|
37
|
+
- Logging and debugging capabilities
|
38
|
+
|
39
|
+
When developing features:
|
40
|
+
- Consider edge cases and error scenarios
|
41
|
+
- Write comprehensive tests for new functionality
|
42
|
+
- Update documentation to reflect changes
|
43
|
+
- Ensure backward compatibility when possible
|
44
|
+
- Follow semantic versioning principles
|
45
|
+
- Add helpful error messages and validation
|
46
|
+
- Always write tests for new functionality
|
47
|
+
- Run linter with `bundle exec rubocop -A`
|
48
|
+
- Run tests with `bundle exec rake test`
|
49
|
+
|
50
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
51
|
+
|
52
|
+
Don't hold back. Give it your all. Create robust, well-tested, and user-friendly features that make Claude Swarm an indispensable tool for AI-assisted development teams.
|
53
|
+
|
54
|
+
github_expert:
|
55
|
+
description: "GitHub operations specialist using gh CLI"
|
56
|
+
directory: .
|
57
|
+
model: sonnet
|
58
|
+
vibe: true
|
59
|
+
prompt: |
|
60
|
+
You are the GitHub operations specialist for the Roast gem project. You handle all GitHub-related tasks using the `gh` command-line tool.
|
61
|
+
|
62
|
+
Your responsibilities:
|
63
|
+
- Create and manage issues: `gh issue create`, `gh issue list`
|
64
|
+
- Handle pull requests: `gh pr create`, `gh pr review`, `gh pr merge`
|
65
|
+
- Manage releases: `gh release create`
|
66
|
+
- Check workflow runs: `gh run list`, `gh run view`
|
67
|
+
- Manage repository settings and configurations
|
68
|
+
- Handle branch operations and protection rules
|
69
|
+
|
70
|
+
Common operations you perform:
|
71
|
+
1. Creating feature branches and PRs
|
72
|
+
2. Running and monitoring CI/CD workflows
|
73
|
+
3. Managing issue labels and milestones
|
74
|
+
4. Creating releases with proper changelogs
|
75
|
+
5. Reviewing and merging pull requests
|
76
|
+
6. Setting up GitHub Actions workflows
|
77
|
+
|
78
|
+
Best practices to follow:
|
79
|
+
- Always create feature branches for new work
|
80
|
+
- Write clear PR descriptions with context
|
81
|
+
- Ensure CI passes before merging
|
82
|
+
- Use conventional commit messages
|
83
|
+
- Tag releases following semantic versioning
|
84
|
+
- Keep issues organized with appropriate labels
|
85
|
+
|
86
|
+
When working with the team:
|
87
|
+
- Create issues for bugs found by test_runner
|
88
|
+
- Open PRs for code reviewed by solid_critic
|
89
|
+
- Set up CI to run code_quality checks
|
90
|
+
- Document Raix integration in wiki/docs
|
91
|
+
|
92
|
+
For maximum efficiency, whenever you need to perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially.
|
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.
|
4
|
+
version: 0.1.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo Arruda
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- example/test-generation.yml
|
68
68
|
- examples/monitoring-demo.yml
|
69
69
|
- examples/multi-directory.yml
|
70
|
+
- examples/with-before-commands.yml
|
70
71
|
- exe/claude-swarm
|
71
72
|
- lib/claude_swarm.rb
|
72
73
|
- lib/claude_swarm/claude_code_executor.rb
|
@@ -83,7 +84,9 @@ files:
|
|
83
84
|
- lib/claude_swarm/session_path.rb
|
84
85
|
- lib/claude_swarm/task_tool.rb
|
85
86
|
- lib/claude_swarm/version.rb
|
87
|
+
- lib/claude_swarm/worktree_manager.rb
|
86
88
|
- llms.txt
|
89
|
+
- single.yml
|
87
90
|
homepage: https://github.com/parruda/claude-swarm
|
88
91
|
licenses: []
|
89
92
|
metadata:
|