claude_swarm 0.1.6 → 0.1.8
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 +31 -0
- data/README.md +104 -32
- data/RELEASING.md +110 -0
- data/lib/claude_swarm/claude_code_executor.rb +16 -4
- data/lib/claude_swarm/cli.rb +36 -6
- data/lib/claude_swarm/configuration.rb +8 -2
- data/lib/claude_swarm/mcp_generator.rb +27 -1
- data/lib/claude_swarm/orchestrator.rb +62 -7
- data/lib/claude_swarm/permission_mcp_server.rb +81 -0
- data/lib/claude_swarm/permission_tool.rb +87 -0
- data/lib/claude_swarm/task_tool.rb +3 -0
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm.rb +2 -0
- metadata +7 -5
- data/claude-swarm.yml +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4694bdf654adfb2784e4156d504ace610c9b48bd7aedafb4d5571ee0483325e6
|
4
|
+
data.tar.gz: 9f26361aac4c2815b11f95ab28a67fbb684377d691b7ee5a1b99c89a1a4fa3c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c5d95adfea48bb560e8ed04d1149284fa199e72a9051e85f7e3e23222a1c117c96b6eb977a418bc573d41a6e6c846d7071dd552121d3810a33b0937ac3228c4
|
7
|
+
data.tar.gz: aaf42838c5caf597bde330171ebd0aca702089731794f2c45df395b177fe6871c96f27f5838da587a9454de7cc397ca8fae7f910c6f61b1f775f6c2911444823
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
## [0.1.8]
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- **Disallowed tools support**: New `disallowed_tools` YAML key for explicitly denying specific tools (takes precedence over allowed tools)
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- **Renamed YAML key**: `tools` renamed to `allowed_tools` while maintaining backward compatibility
|
8
|
+
- Tool permissions now support both allow and deny patterns, with deny taking precedence
|
9
|
+
- Both `--allowedTools` and `--disallowedTools` are passed as comma-separated lists to Claude
|
10
|
+
- New CLI option `--stream-logs` - can only be used with `-p`
|
11
|
+
|
12
|
+
## [0.1.7]
|
13
|
+
|
14
|
+
### Added
|
15
|
+
- **Vibe mode support**: Per-instance `vibe: true` configuration to skip all permission checks for specific instances
|
16
|
+
- **Automatic permission management**: Built-in permission MCP server that handles tool authorization without manual approval
|
17
|
+
- **Permission logging**: All permission checks are logged to `.claude-swarm/sessions/{timestamp}/permissions.log`
|
18
|
+
- **Mixed permission modes**: Support for running some instances with full permissions while others remain restricted
|
19
|
+
- **New CLI command**: `claude-swarm tools-mcp` for starting a standalone permission management MCP server
|
20
|
+
- **Permission tool patterns**: Support for wildcard patterns in tool permissions (e.g., `mcp__frontend__*`)
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
- Fixed `--system-prompt` to use `--append-system-prompt` for proper Claude Code integration
|
24
|
+
- Added `--permission-prompt-tool` flag pointing to `mcp__permissions__check_permission` when not in vibe mode
|
25
|
+
- Enhanced MCP generation to include a permission server for each instance (unless in vibe mode)
|
26
|
+
|
27
|
+
### Technical Details
|
28
|
+
- Permission checks use Fast MCP server with pattern matching for tool names
|
29
|
+
- Each instance can have its own permission configuration independent of global settings
|
30
|
+
- Permission decisions are made based on configured tool patterns with wildcard support
|
31
|
+
|
1
32
|
## [0.1.6]
|
2
33
|
- Refactor: move tools out of the ClaudeMcpServer class
|
3
34
|
- Move logging into code executor and save instance interaction streams to session.log
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Claude Swarm
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/claude_swarm)
|
4
|
+
[](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml)
|
5
|
+
|
3
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.
|
4
7
|
|
5
8
|
## Installation
|
@@ -24,7 +27,7 @@ bundle install
|
|
24
27
|
|
25
28
|
## Prerequisites
|
26
29
|
|
27
|
-
- Ruby 3.
|
30
|
+
- Ruby 3.2.0 or higher
|
28
31
|
- Claude CLI installed and configured
|
29
32
|
- Any MCP servers you plan to use (optional)
|
30
33
|
|
@@ -45,7 +48,7 @@ swarm:
|
|
45
48
|
directory: .
|
46
49
|
model: opus
|
47
50
|
connections: [frontend, backend]
|
48
|
-
|
51
|
+
allowed_tools: # Tools aren't required if you run it with `--vibe`
|
49
52
|
- Read
|
50
53
|
- Edit
|
51
54
|
- Bash
|
@@ -53,7 +56,7 @@ swarm:
|
|
53
56
|
description: "Frontend specialist handling UI and user experience"
|
54
57
|
directory: ./frontend
|
55
58
|
model: opus
|
56
|
-
|
59
|
+
allowed_tools:
|
57
60
|
- Edit
|
58
61
|
- Write
|
59
62
|
- Bash
|
@@ -61,7 +64,7 @@ swarm:
|
|
61
64
|
description: "Backend developer managing APIs and data layer"
|
62
65
|
directory: ./backend
|
63
66
|
model: opus
|
64
|
-
|
67
|
+
allowed_tools:
|
65
68
|
- Edit
|
66
69
|
- Write
|
67
70
|
- Bash
|
@@ -98,7 +101,7 @@ swarm:
|
|
98
101
|
model: opus
|
99
102
|
connections: [frontend_lead, backend_lead, mobile_lead, devops]
|
100
103
|
prompt: "You are the system architect coordinating between different service teams"
|
101
|
-
|
104
|
+
allowed_tools: [Read, Edit, WebSearch]
|
102
105
|
|
103
106
|
frontend_lead:
|
104
107
|
description: "Frontend team lead overseeing React development"
|
@@ -106,21 +109,21 @@ swarm:
|
|
106
109
|
model: opus
|
107
110
|
connections: [react_dev, css_expert]
|
108
111
|
prompt: "You lead the web frontend team working with React"
|
109
|
-
|
112
|
+
allowed_tools: [Read, Edit, Bash]
|
110
113
|
|
111
114
|
react_dev:
|
112
115
|
description: "React developer specializing in components and state management"
|
113
116
|
directory: ./web-frontend/src
|
114
117
|
model: opus
|
115
118
|
prompt: "You specialize in React components and state management"
|
116
|
-
|
119
|
+
allowed_tools: [Edit, Write, "Bash(npm:*)"]
|
117
120
|
|
118
121
|
css_expert:
|
119
122
|
description: "CSS specialist handling styling and responsive design"
|
120
123
|
directory: ./web-frontend/styles
|
121
124
|
model: opus
|
122
125
|
prompt: "You handle all CSS and styling concerns"
|
123
|
-
|
126
|
+
allowed_tools: [Edit, Write, Read]
|
124
127
|
|
125
128
|
backend_lead:
|
126
129
|
description: "Backend team lead managing API development"
|
@@ -128,21 +131,21 @@ swarm:
|
|
128
131
|
model: opus
|
129
132
|
connections: [api_dev, database_expert]
|
130
133
|
prompt: "You lead the API backend team"
|
131
|
-
|
134
|
+
allowed_tools: [Read, Edit, Bash]
|
132
135
|
|
133
136
|
api_dev:
|
134
137
|
description: "API developer building REST endpoints"
|
135
138
|
directory: ./api-server/src
|
136
139
|
model: opus
|
137
140
|
prompt: "You develop REST API endpoints"
|
138
|
-
|
141
|
+
allowed_tools: [Edit, Write, Bash]
|
139
142
|
|
140
143
|
database_expert:
|
141
144
|
description: "Database specialist managing schemas and migrations"
|
142
145
|
directory: ./api-server/db
|
143
146
|
model: opus
|
144
147
|
prompt: "You handle database schema and migrations"
|
145
|
-
|
148
|
+
allowed_tools: [Edit, Write, "Bash(psql:*, migrate:*)"]
|
146
149
|
|
147
150
|
mobile_lead:
|
148
151
|
description: "Mobile team lead coordinating cross-platform development"
|
@@ -150,28 +153,28 @@ swarm:
|
|
150
153
|
model: opus
|
151
154
|
connections: [ios_dev, android_dev]
|
152
155
|
prompt: "You coordinate mobile development across platforms"
|
153
|
-
|
156
|
+
allowed_tools: [Read, Edit]
|
154
157
|
|
155
158
|
ios_dev:
|
156
159
|
description: "iOS developer building native Apple applications"
|
157
160
|
directory: ./mobile-app/ios
|
158
161
|
model: opus
|
159
162
|
prompt: "You develop the iOS application"
|
160
|
-
|
163
|
+
allowed_tools: [Edit, Write, "Bash(xcodebuild:*, pod:*)"]
|
161
164
|
|
162
165
|
android_dev:
|
163
166
|
description: "Android developer creating native Android apps"
|
164
167
|
directory: ./mobile-app/android
|
165
168
|
model: opus
|
166
169
|
prompt: "You develop the Android application"
|
167
|
-
|
170
|
+
allowed_tools: [Edit, Write, "Bash(gradle:*, adb:*)"]
|
168
171
|
|
169
172
|
devops:
|
170
173
|
description: "DevOps engineer managing CI/CD and infrastructure"
|
171
174
|
directory: ./infrastructure
|
172
175
|
model: opus
|
173
176
|
prompt: "You handle CI/CD and infrastructure"
|
174
|
-
|
177
|
+
allowed_tools: [Read, Edit, "Bash(docker:*, kubectl:*)"]
|
175
178
|
```
|
176
179
|
|
177
180
|
In this setup:
|
@@ -205,9 +208,11 @@ Each instance can have:
|
|
205
208
|
- **directory**: Working directory for this instance (can use ~ for home)
|
206
209
|
- **model**: Claude model to use (opus, sonnet, haiku)
|
207
210
|
- **connections**: Array of other instances this one can communicate with
|
208
|
-
- **
|
211
|
+
- **allowed_tools**: Array of tools this instance can use (backward compatible with `tools`)
|
212
|
+
- **disallowed_tools**: Array of tools to explicitly deny (takes precedence over allowed_tools)
|
209
213
|
- **mcps**: Array of additional MCP servers to connect
|
210
214
|
- **prompt**: Custom system prompt to append to the instance
|
215
|
+
- **vibe**: Enable vibe mode (--dangerously-skip-permissions) for this instance (default: false)
|
211
216
|
|
212
217
|
```yaml
|
213
218
|
instance_name:
|
@@ -216,13 +221,17 @@ instance_name:
|
|
216
221
|
model: opus
|
217
222
|
connections: [other_instance1, other_instance2]
|
218
223
|
prompt: "You are a specialized agent focused on..."
|
219
|
-
|
224
|
+
vibe: false # Set to true to skip all permission checks for this instance
|
225
|
+
allowed_tools:
|
220
226
|
- Read
|
221
227
|
- Edit
|
222
228
|
- Write
|
223
229
|
- Bash
|
224
230
|
- WebFetch
|
225
231
|
- WebSearch
|
232
|
+
disallowed_tools: # Optional: explicitly deny specific tools
|
233
|
+
- "Write(*.log)"
|
234
|
+
- "Bash(rm:*)"
|
226
235
|
mcps:
|
227
236
|
- name: server_name
|
228
237
|
type: stdio
|
@@ -258,23 +267,27 @@ mcps:
|
|
258
267
|
Specify which tools each instance can use:
|
259
268
|
|
260
269
|
```yaml
|
261
|
-
|
270
|
+
allowed_tools:
|
262
271
|
- Bash # Command execution
|
263
272
|
- Edit # File editing
|
264
273
|
- Write # File creation
|
265
274
|
- Read # File reading
|
266
275
|
- WebFetch # Fetch web content
|
267
276
|
- WebSearch # Search the web
|
277
|
+
|
278
|
+
disallowed_tools: # Optional: explicitly deny specific tools
|
279
|
+
- "Write(*.md)" # Don't allow writing markdown files
|
280
|
+
- "Bash(rm:*)" # Don't allow rm commands
|
268
281
|
```
|
269
282
|
|
270
|
-
Tools are passed to Claude using the `--allowedTools`
|
283
|
+
Tools are passed to Claude using the `--allowedTools` and `--disallowedTools` flags with comma-separated values. Disallowed tools take precedence over allowed tools.
|
271
284
|
|
272
285
|
#### Tool Restrictions
|
273
286
|
|
274
287
|
You can restrict tools with pattern-based filters:
|
275
288
|
|
276
289
|
```yaml
|
277
|
-
|
290
|
+
allowed_tools:
|
278
291
|
- Read # Unrestricted read access
|
279
292
|
- Edit # Unrestricted edit access
|
280
293
|
- "Bash(npm:*)" # Only allow npm commands
|
@@ -297,7 +310,7 @@ swarm:
|
|
297
310
|
model: opus
|
298
311
|
connections: [frontend, backend, devops]
|
299
312
|
prompt: "You are the lead architect responsible for system design and code quality"
|
300
|
-
|
313
|
+
allowed_tools:
|
301
314
|
- Read
|
302
315
|
- Edit
|
303
316
|
- WebSearch
|
@@ -308,7 +321,7 @@ swarm:
|
|
308
321
|
model: opus
|
309
322
|
connections: [architect]
|
310
323
|
prompt: "You specialize in React, TypeScript, and modern frontend development"
|
311
|
-
|
324
|
+
allowed_tools:
|
312
325
|
- Edit
|
313
326
|
- Write
|
314
327
|
- Bash
|
@@ -318,7 +331,7 @@ swarm:
|
|
318
331
|
directory: ./backend
|
319
332
|
model: opus
|
320
333
|
connections: [architect, database]
|
321
|
-
|
334
|
+
allowed_tools:
|
322
335
|
- Edit
|
323
336
|
- Write
|
324
337
|
- Bash
|
@@ -327,7 +340,7 @@ swarm:
|
|
327
340
|
description: "Database administrator managing data persistence"
|
328
341
|
directory: ./db
|
329
342
|
model: haiku
|
330
|
-
|
343
|
+
allowed_tools:
|
331
344
|
- Read
|
332
345
|
- Bash
|
333
346
|
|
@@ -336,7 +349,7 @@ swarm:
|
|
336
349
|
directory: .
|
337
350
|
model: opus
|
338
351
|
connections: [architect]
|
339
|
-
|
352
|
+
allowed_tools:
|
340
353
|
- Read
|
341
354
|
- Edit
|
342
355
|
- Bash
|
@@ -355,7 +368,7 @@ swarm:
|
|
355
368
|
directory: ~/research
|
356
369
|
model: opus
|
357
370
|
connections: [data_analyst, writer]
|
358
|
-
|
371
|
+
allowed_tools:
|
359
372
|
- Read
|
360
373
|
- WebSearch
|
361
374
|
- WebFetch
|
@@ -368,7 +381,7 @@ swarm:
|
|
368
381
|
description: "Data analyst processing research data and statistics"
|
369
382
|
directory: ~/research/data
|
370
383
|
model: opus
|
371
|
-
|
384
|
+
allowed_tools:
|
372
385
|
- Read
|
373
386
|
- Write
|
374
387
|
- Bash
|
@@ -382,12 +395,43 @@ swarm:
|
|
382
395
|
description: "Technical writer preparing research documentation"
|
383
396
|
directory: ~/research/papers
|
384
397
|
model: opus
|
385
|
-
|
398
|
+
allowed_tools:
|
386
399
|
- Edit
|
387
400
|
- Write
|
388
401
|
- Read
|
389
402
|
```
|
390
403
|
|
404
|
+
#### Mixed Permission Modes
|
405
|
+
|
406
|
+
You can have different permission modes for different instances:
|
407
|
+
|
408
|
+
```yaml
|
409
|
+
version: 1
|
410
|
+
swarm:
|
411
|
+
name: "Mixed Mode Team"
|
412
|
+
main: lead
|
413
|
+
instances:
|
414
|
+
lead:
|
415
|
+
description: "Lead with full permissions"
|
416
|
+
directory: .
|
417
|
+
model: opus
|
418
|
+
vibe: true # This instance runs with --dangerously-skip-permissions
|
419
|
+
connections: [restricted_worker, trusted_worker]
|
420
|
+
|
421
|
+
restricted_worker:
|
422
|
+
description: "Worker with restricted permissions"
|
423
|
+
directory: ./sensitive
|
424
|
+
model: sonnet
|
425
|
+
allowed_tools: [Read, "Bash(ls:*)"] # Only allow read and ls commands
|
426
|
+
|
427
|
+
trusted_worker:
|
428
|
+
description: "Trusted worker with more permissions"
|
429
|
+
directory: ./workspace
|
430
|
+
model: sonnet
|
431
|
+
vibe: true # This instance also skips permissions
|
432
|
+
allowed_tools: [] # Tools list ignored when vibe: true
|
433
|
+
```
|
434
|
+
|
391
435
|
### Command Line Options
|
392
436
|
|
393
437
|
```bash
|
@@ -408,6 +452,10 @@ claude-swarm --prompt "Fix the bug in the payment module"
|
|
408
452
|
# Show version
|
409
453
|
claude-swarm version
|
410
454
|
|
455
|
+
# Start permission MCP server (for testing/debugging)
|
456
|
+
claude-swarm tools-mcp --allowed-tools 'mcp__frontend__*,mcp__backend__*'
|
457
|
+
claude-swarm tools-mcp --allowed-tools 'Read,Edit' --disallowed-tools 'Edit(*.log)'
|
458
|
+
|
411
459
|
# Internal command for MCP server (used by connected instances)
|
412
460
|
claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TIMESTAMP
|
413
461
|
```
|
@@ -418,18 +466,28 @@ claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TI
|
|
418
466
|
2. **MCP Generation**: For each instance, it generates an MCP configuration file that includes:
|
419
467
|
- Any explicitly defined MCP servers
|
420
468
|
- MCP servers for each connected instance (using `claude-swarm mcp-serve`)
|
421
|
-
|
469
|
+
- A permission MCP server (unless using `--vibe` mode)
|
470
|
+
3. **Tool Permissions**: Claude Swarm automatically manages tool permissions:
|
471
|
+
- Each instance's configured tools are allowed via the permission MCP
|
472
|
+
- Supports wildcard patterns (e.g., `mcp__frontend__*` allows all frontend MCP tools)
|
473
|
+
- Disallowed tools take precedence over allowed tools for fine-grained control
|
474
|
+
- Eliminates the need to manually accept each tool or use global `--vibe` mode
|
475
|
+
- Per-instance `vibe: true` skips all permission checks for that specific instance
|
476
|
+
- The permission MCP uses `--permission-prompt-tool` to check tool access
|
477
|
+
- Permission decisions are logged to `.claude-swarm/sessions/{timestamp}/permissions.log`
|
478
|
+
4. **Session Management**: Claude Swarm maintains session continuity:
|
422
479
|
- Generates a shared session timestamp for all instances
|
423
480
|
- Each instance can maintain its own Claude session ID
|
424
481
|
- Sessions can be reset via the MCP server interface
|
425
|
-
|
426
|
-
|
482
|
+
5. **Main Instance Launch**: The main instance is launched with its MCP configuration, giving it access to all connected instances
|
483
|
+
6. **Inter-Instance Communication**: Connected instances expose themselves as MCP servers with these tools:
|
427
484
|
- **task**: Execute tasks using Claude Code with configurable tools and return results. The tool description includes the instance name and description (e.g., "Execute a task using Agent frontend_dev. Frontend developer specializing in React and TypeScript")
|
428
485
|
- **session_info**: Get current Claude session information including ID and working directory
|
429
486
|
- **reset_session**: Reset the Claude session for a fresh start
|
430
|
-
|
487
|
+
7. **Session Management**: All session files are organized in `.claude-swarm/sessions/{timestamp}/`:
|
431
488
|
- MCP configuration files: `{instance_name}.mcp.json`
|
432
489
|
- Session log: `session.log` with detailed request/response tracking
|
490
|
+
- Permission log: `permissions.log` with all permission checks and decisions
|
433
491
|
|
434
492
|
## Troubleshooting
|
435
493
|
|
@@ -492,6 +550,20 @@ bundle exec rake release # Release gem to RubyGems.org
|
|
492
550
|
rake # Default: runs both tests and RuboCop
|
493
551
|
```
|
494
552
|
|
553
|
+
### Release Process
|
554
|
+
|
555
|
+
The gem is automatically published to RubyGems when a new release is created on GitHub:
|
556
|
+
|
557
|
+
1. Update the version number in `lib/claude_swarm/version.rb`
|
558
|
+
2. Update `CHANGELOG.md` with the new version's changes
|
559
|
+
3. Commit the changes: `git commit -am "Bump version to x.y.z"`
|
560
|
+
4. Create a version tag: `git tag -a vx.y.z -m "Release version x.y.z"`
|
561
|
+
5. Push the changes and tag: `git push && git push --tags`
|
562
|
+
6. The GitHub workflow will create a draft release - review and publish it
|
563
|
+
7. Once published, the gem will be automatically built and pushed to RubyGems
|
564
|
+
|
565
|
+
**Note**: You need to set up the `RUBYGEMS_AUTH_TOKEN` secret in your GitHub repository settings with your RubyGems API key.
|
566
|
+
|
495
567
|
## Contributing
|
496
568
|
|
497
569
|
Bug reports and pull requests are welcome on GitHub at https://github.com/parruda/claude-swarm.
|
data/RELEASING.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Releasing Claude Swarm
|
2
|
+
|
3
|
+
This guide walks through the process of releasing a new version of Claude Swarm.
|
4
|
+
|
5
|
+
## Prerequisites
|
6
|
+
|
7
|
+
- Ensure you have push access to the repository
|
8
|
+
- Ensure the `RUBYGEMS_AUTH_TOKEN` secret is configured in GitHub repository settings
|
9
|
+
- Ensure all tests are passing on the main branch
|
10
|
+
|
11
|
+
## Release Steps
|
12
|
+
|
13
|
+
### 1. Update Version
|
14
|
+
|
15
|
+
Edit `lib/claude_swarm/version.rb` and update the version number:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
module ClaudeSwarm
|
19
|
+
VERSION = "0.1.8" # Update this
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
### 2. Update Changelog
|
24
|
+
|
25
|
+
Move items from the `[Unreleased]` section to a new version section in `CHANGELOG.md`:
|
26
|
+
|
27
|
+
```markdown
|
28
|
+
## [Unreleased]
|
29
|
+
|
30
|
+
## [0.1.8] - 2025-06-02
|
31
|
+
|
32
|
+
### Added
|
33
|
+
- Feature X
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
- Enhancement Y
|
37
|
+
|
38
|
+
### Fixed
|
39
|
+
- Bug Z
|
40
|
+
```
|
41
|
+
|
42
|
+
### 3. Commit Changes
|
43
|
+
|
44
|
+
```bash
|
45
|
+
git add lib/claude_swarm/version.rb CHANGELOG.md
|
46
|
+
git commit -m "Bump version to 0.1.8"
|
47
|
+
```
|
48
|
+
|
49
|
+
### 4. Create and Push Tag
|
50
|
+
|
51
|
+
```bash
|
52
|
+
git tag -a v0.1.8 -m "Release version 0.1.8"
|
53
|
+
git push origin main
|
54
|
+
git push origin v0.1.8
|
55
|
+
```
|
56
|
+
|
57
|
+
### 5. Review Draft Release
|
58
|
+
|
59
|
+
The GitHub workflow will automatically create a draft release. Review it at:
|
60
|
+
https://github.com/parruda/claude-swarm/releases
|
61
|
+
|
62
|
+
### 6. Publish Release
|
63
|
+
|
64
|
+
Once you're satisfied with the release notes, click "Publish release". This will trigger the workflow to:
|
65
|
+
- Run tests
|
66
|
+
- Build the gem
|
67
|
+
- Publish to RubyGems.org
|
68
|
+
- Publish to GitHub Packages
|
69
|
+
|
70
|
+
### 7. Verify Publication
|
71
|
+
|
72
|
+
Check that the gem was published successfully:
|
73
|
+
- RubyGems: https://rubygems.org/gems/claude_swarm
|
74
|
+
- GitHub Packages: https://github.com/parruda/claude-swarm/packages
|
75
|
+
|
76
|
+
## Troubleshooting
|
77
|
+
|
78
|
+
### Failed Publication
|
79
|
+
|
80
|
+
If the release workflow fails:
|
81
|
+
|
82
|
+
1. Check the workflow logs at: https://github.com/parruda/claude-swarm/actions
|
83
|
+
2. Common issues:
|
84
|
+
- Invalid `RUBYGEMS_AUTH_TOKEN` secret
|
85
|
+
- Test failures
|
86
|
+
- RuboCop violations
|
87
|
+
- Network issues
|
88
|
+
|
89
|
+
### Manual Release
|
90
|
+
|
91
|
+
If you need to release manually:
|
92
|
+
|
93
|
+
```bash
|
94
|
+
# Ensure you're on the correct tag
|
95
|
+
git checkout v0.1.8
|
96
|
+
|
97
|
+
# Build the gem
|
98
|
+
gem build claude_swarm.gemspec
|
99
|
+
|
100
|
+
# Push to RubyGems (requires credentials)
|
101
|
+
gem push claude_swarm-0.1.8.gem
|
102
|
+
```
|
103
|
+
|
104
|
+
## Post-Release
|
105
|
+
|
106
|
+
After a successful release:
|
107
|
+
|
108
|
+
1. Create a new `[Unreleased]` section in `CHANGELOG.md`
|
109
|
+
2. Announce the release (optional)
|
110
|
+
3. Update any dependent projects
|
@@ -191,14 +191,26 @@ module ClaudeSwarm
|
|
191
191
|
cmd_array += ["--print", "-p", prompt]
|
192
192
|
|
193
193
|
# Add any custom system prompt
|
194
|
-
cmd_array += ["--system-prompt", options[:system_prompt]] if options[:system_prompt]
|
194
|
+
cmd_array += ["--append-system-prompt", options[:system_prompt]] if options[:system_prompt]
|
195
195
|
|
196
196
|
# Add any allowed tools or vibe flag
|
197
197
|
if @vibe
|
198
198
|
cmd_array << "--dangerously-skip-permissions"
|
199
|
-
|
200
|
-
tools
|
201
|
-
|
199
|
+
else
|
200
|
+
# Add allowed tools if any
|
201
|
+
if options[:allowed_tools]
|
202
|
+
tools = Array(options[:allowed_tools]).join(",")
|
203
|
+
cmd_array += ["--allowedTools", tools]
|
204
|
+
end
|
205
|
+
|
206
|
+
# Add disallowed tools if any
|
207
|
+
if options[:disallowed_tools]
|
208
|
+
disallowed_tools = Array(options[:disallowed_tools]).join(",")
|
209
|
+
cmd_array += ["--disallowedTools", disallowed_tools]
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add permission prompt tool if not in vibe mode
|
213
|
+
cmd_array += ["--permission-prompt-tool", "mcp__permissions__check_permission"]
|
202
214
|
end
|
203
215
|
|
204
216
|
cmd_array
|
data/lib/claude_swarm/cli.rb
CHANGED
@@ -5,6 +5,7 @@ require_relative "configuration"
|
|
5
5
|
require_relative "mcp_generator"
|
6
6
|
require_relative "orchestrator"
|
7
7
|
require_relative "claude_mcp_server"
|
8
|
+
require_relative "permission_mcp_server"
|
8
9
|
|
9
10
|
module ClaudeSwarm
|
10
11
|
class CLI < Thor
|
@@ -19,6 +20,8 @@ module ClaudeSwarm
|
|
19
20
|
desc: "Run with --dangerously-skip-permissions for all instances"
|
20
21
|
method_option :prompt, aliases: "-p", type: :string,
|
21
22
|
desc: "Prompt to pass to the main Claude instance (non-interactive mode)"
|
23
|
+
method_option :stream_logs, type: :boolean, default: false,
|
24
|
+
desc: "Stream session logs to stdout (only works with -p)"
|
22
25
|
def start(config_file = nil)
|
23
26
|
config_path = config_file || options[:config]
|
24
27
|
unless File.exist?(config_path)
|
@@ -27,6 +30,13 @@ module ClaudeSwarm
|
|
27
30
|
end
|
28
31
|
|
29
32
|
say "Starting Claude Swarm from #{config_path}..." unless options[:prompt]
|
33
|
+
|
34
|
+
# Validate stream_logs option
|
35
|
+
if options[:stream_logs] && !options[:prompt]
|
36
|
+
error "--stream-logs can only be used with -p/--prompt"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
30
40
|
begin
|
31
41
|
config = Configuration.new(config_path)
|
32
42
|
session_timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
@@ -34,7 +44,8 @@ module ClaudeSwarm
|
|
34
44
|
orchestrator = Orchestrator.new(config, generator,
|
35
45
|
vibe: options[:vibe],
|
36
46
|
prompt: options[:prompt],
|
37
|
-
session_timestamp: session_timestamp
|
47
|
+
session_timestamp: session_timestamp,
|
48
|
+
stream_logs: options[:stream_logs])
|
38
49
|
orchestrator.start
|
39
50
|
rescue Error => e
|
40
51
|
error e.message
|
@@ -59,6 +70,8 @@ module ClaudeSwarm
|
|
59
70
|
desc: "Description of the instance's role"
|
60
71
|
method_option :tools, aliases: "-t", type: :array,
|
61
72
|
desc: "Allowed tools for the instance"
|
73
|
+
method_option :disallowed_tools, type: :array,
|
74
|
+
desc: "Disallowed tools for the instance"
|
62
75
|
method_option :mcp_config_path, type: :string,
|
63
76
|
desc: "Path to MCP configuration file"
|
64
77
|
method_option :debug, type: :boolean, default: false,
|
@@ -75,6 +88,7 @@ module ClaudeSwarm
|
|
75
88
|
prompt: options[:prompt],
|
76
89
|
description: options[:description],
|
77
90
|
tools: options[:tools] || [],
|
91
|
+
disallowed_tools: options[:disallowed_tools] || [],
|
78
92
|
mcp_config_path: options[:mcp_config_path],
|
79
93
|
vibe: options[:vibe]
|
80
94
|
}
|
@@ -112,7 +126,7 @@ module ClaudeSwarm
|
|
112
126
|
directory: .
|
113
127
|
model: sonnet
|
114
128
|
prompt: "You are the lead developer coordinating the team"
|
115
|
-
|
129
|
+
allowed_tools: [Read, Edit, Bash, Write]
|
116
130
|
# connections: [frontend_dev, backend_dev]
|
117
131
|
|
118
132
|
# Example instances (uncomment and modify as needed):
|
@@ -122,28 +136,28 @@ module ClaudeSwarm
|
|
122
136
|
# directory: ./frontend
|
123
137
|
# model: sonnet
|
124
138
|
# prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
|
125
|
-
#
|
139
|
+
# allowed_tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
|
126
140
|
|
127
141
|
# backend_dev:
|
128
142
|
# description: "Backend developer focusing on APIs, databases, and server architecture"
|
129
143
|
# directory: ../other-app/backend
|
130
144
|
# model: sonnet
|
131
145
|
# prompt: "You specialize in backend development, APIs, databases, and server architecture"
|
132
|
-
#
|
146
|
+
# allowed_tools: [Read, Edit, Write, Bash]
|
133
147
|
|
134
148
|
# devops_engineer:
|
135
149
|
# description: "DevOps engineer managing infrastructure, CI/CD, and deployments"
|
136
150
|
# directory: .
|
137
151
|
# model: sonnet
|
138
152
|
# prompt: "You specialize in infrastructure, CI/CD, containerization, and deployment"
|
139
|
-
#
|
153
|
+
# allowed_tools: [Read, Edit, Write, "Bash(docker:*)", "Bash(kubectl:*)", "Bash(terraform:*)"]
|
140
154
|
|
141
155
|
# qa_engineer:
|
142
156
|
# description: "QA engineer ensuring quality through comprehensive testing"
|
143
157
|
# directory: ./tests
|
144
158
|
# model: sonnet
|
145
159
|
# prompt: "You specialize in testing, quality assurance, and test automation"
|
146
|
-
#
|
160
|
+
# allowed_tools: [Read, Edit, Write, Bash]
|
147
161
|
YAML
|
148
162
|
|
149
163
|
File.write(config_path, template)
|
@@ -156,6 +170,22 @@ module ClaudeSwarm
|
|
156
170
|
say "Claude Swarm #{VERSION}"
|
157
171
|
end
|
158
172
|
|
173
|
+
desc "tools-mcp", "Start a permission management MCP server for tool access control"
|
174
|
+
method_option :allowed_tools, aliases: "-t", type: :array,
|
175
|
+
desc: "Comma-separated list of allowed tool patterns (supports wildcards)"
|
176
|
+
method_option :disallowed_tools, type: :array,
|
177
|
+
desc: "Comma-separated list of disallowed tool patterns (supports wildcards)"
|
178
|
+
method_option :debug, type: :boolean, default: false,
|
179
|
+
desc: "Enable debug output"
|
180
|
+
def tools_mcp
|
181
|
+
server = PermissionMcpServer.new(allowed_tools: options[:allowed_tools], disallowed_tools: options[:disallowed_tools])
|
182
|
+
server.start
|
183
|
+
rescue StandardError => e
|
184
|
+
error "Error starting permission MCP server: #{e.message}"
|
185
|
+
error e.backtrace.join("\n") if options[:debug]
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
|
159
189
|
default_task :start
|
160
190
|
|
161
191
|
private
|
@@ -76,15 +76,21 @@ module ClaudeSwarm
|
|
76
76
|
# Validate required fields
|
77
77
|
raise Error, "Instance '#{name}' missing required 'description' field" unless config["description"]
|
78
78
|
|
79
|
+
# Support both 'tools' (deprecated) and 'allowed_tools' for backward compatibility
|
80
|
+
allowed_tools = config["allowed_tools"] || config["tools"] || []
|
81
|
+
|
79
82
|
{
|
80
83
|
name: name,
|
81
84
|
directory: expand_path(config["directory"] || "."),
|
82
85
|
model: config["model"] || "sonnet",
|
83
86
|
connections: Array(config["connections"]),
|
84
|
-
tools: Array(
|
87
|
+
tools: Array(allowed_tools), # Keep as 'tools' internally for compatibility
|
88
|
+
allowed_tools: Array(allowed_tools),
|
89
|
+
disallowed_tools: Array(config["disallowed_tools"]),
|
85
90
|
mcps: parse_mcps(config["mcps"] || []),
|
86
91
|
prompt: config["prompt"],
|
87
|
-
description: config["description"]
|
92
|
+
description: config["description"],
|
93
|
+
vibe: config["vibe"] || false
|
88
94
|
}
|
89
95
|
end
|
90
96
|
|
@@ -58,6 +58,9 @@ module ClaudeSwarm
|
|
58
58
|
mcp_servers[connection_name] = build_instance_mcp_config(connection_name, connected_instance, calling_instance: name)
|
59
59
|
end
|
60
60
|
|
61
|
+
# Add permission MCP server if not in vibe mode (global or instance-specific)
|
62
|
+
mcp_servers["permissions"] = build_permission_mcp_config(instance[:tools], instance[:disallowed_tools]) unless @vibe || instance[:vibe]
|
63
|
+
|
61
64
|
config = {
|
62
65
|
"mcpServers" => mcp_servers
|
63
66
|
}
|
@@ -102,11 +105,13 @@ module ClaudeSwarm
|
|
102
105
|
|
103
106
|
args.push("--tools", instance[:tools].join(",")) if instance[:tools] && !instance[:tools].empty?
|
104
107
|
|
108
|
+
args.push("--disallowed-tools", instance[:disallowed_tools].join(",")) if instance[:disallowed_tools] && !instance[:disallowed_tools].empty?
|
109
|
+
|
105
110
|
args.push("--mcp-config-path", mcp_config_path(name))
|
106
111
|
|
107
112
|
args.push("--calling-instance", calling_instance) if calling_instance
|
108
113
|
|
109
|
-
args.push("--vibe") if @vibe
|
114
|
+
args.push("--vibe") if @vibe || instance[:vibe]
|
110
115
|
|
111
116
|
{
|
112
117
|
"type" => "stdio",
|
@@ -114,5 +119,26 @@ module ClaudeSwarm
|
|
114
119
|
"args" => args
|
115
120
|
}
|
116
121
|
end
|
122
|
+
|
123
|
+
def build_permission_mcp_config(allowed_tools, disallowed_tools)
|
124
|
+
exe_path = "claude-swarm"
|
125
|
+
|
126
|
+
args = ["tools-mcp"]
|
127
|
+
|
128
|
+
# Add allowed tools if specified
|
129
|
+
args.push("--allowed-tools", allowed_tools.join(",")) if allowed_tools && !allowed_tools.empty?
|
130
|
+
|
131
|
+
# Add disallowed tools if specified
|
132
|
+
args.push("--disallowed-tools", disallowed_tools.join(",")) if disallowed_tools && !disallowed_tools.empty?
|
133
|
+
|
134
|
+
{
|
135
|
+
"type" => "stdio",
|
136
|
+
"command" => exe_path,
|
137
|
+
"args" => args,
|
138
|
+
"env" => {
|
139
|
+
"CLAUDE_SWARM_SESSION_TIMESTAMP" => @timestamp
|
140
|
+
}
|
141
|
+
}
|
142
|
+
end
|
117
143
|
end
|
118
144
|
end
|
@@ -4,12 +4,13 @@ require "shellwords"
|
|
4
4
|
|
5
5
|
module ClaudeSwarm
|
6
6
|
class Orchestrator
|
7
|
-
def initialize(configuration, mcp_generator, vibe: false, prompt: nil, session_timestamp: nil)
|
7
|
+
def initialize(configuration, mcp_generator, vibe: false, prompt: nil, session_timestamp: nil, stream_logs: false)
|
8
8
|
@config = configuration
|
9
9
|
@generator = mcp_generator
|
10
10
|
@vibe = vibe
|
11
11
|
@prompt = prompt
|
12
12
|
@session_timestamp = session_timestamp || Time.now.strftime("%Y%m%d_%H%M%S")
|
13
|
+
@stream_logs = stream_logs
|
13
14
|
end
|
14
15
|
|
15
16
|
def start
|
@@ -39,8 +40,10 @@ module ClaudeSwarm
|
|
39
40
|
puts "🚀 Launching main instance: #{@config.main_instance}"
|
40
41
|
puts " Model: #{main_instance[:model]}"
|
41
42
|
puts " Directory: #{main_instance[:directory]}"
|
42
|
-
puts "
|
43
|
+
puts " Allowed tools: #{main_instance[:tools].join(", ")}" if main_instance[:tools].any?
|
44
|
+
puts " Disallowed tools: #{main_instance[:disallowed_tools].join(", ")}" if main_instance[:disallowed_tools]&.any?
|
43
45
|
puts " Connections: #{main_instance[:connections].join(", ")}" if main_instance[:connections].any?
|
46
|
+
puts " 😎 Vibe mode ON for this instance" if main_instance[:vibe]
|
44
47
|
puts
|
45
48
|
end
|
46
49
|
|
@@ -50,14 +53,52 @@ module ClaudeSwarm
|
|
50
53
|
puts
|
51
54
|
end
|
52
55
|
|
56
|
+
# Start log streaming thread if in non-interactive mode with --stream-logs
|
57
|
+
log_thread = nil
|
58
|
+
log_thread = start_log_streaming if @prompt && @stream_logs
|
59
|
+
|
53
60
|
# Execute the main instance - this will cascade to other instances via MCP
|
54
61
|
Dir.chdir(main_instance[:directory]) do
|
55
62
|
system(*command)
|
56
63
|
end
|
64
|
+
|
65
|
+
# Clean up log streaming thread
|
66
|
+
return unless log_thread
|
67
|
+
|
68
|
+
log_thread.terminate
|
69
|
+
log_thread.join
|
57
70
|
end
|
58
71
|
|
59
72
|
private
|
60
73
|
|
74
|
+
def start_log_streaming
|
75
|
+
Thread.new do
|
76
|
+
session_log_path = File.join(Dir.pwd, ClaudeSwarm::ClaudeCodeExecutor::SWARM_DIR,
|
77
|
+
ClaudeSwarm::ClaudeCodeExecutor::SESSIONS_DIR,
|
78
|
+
@session_timestamp, "session.log")
|
79
|
+
|
80
|
+
# Wait for log file to be created
|
81
|
+
sleep 0.1 until File.exist?(session_log_path)
|
82
|
+
|
83
|
+
# Open file and seek to end
|
84
|
+
File.open(session_log_path, "r") do |file|
|
85
|
+
file.seek(0, IO::SEEK_END)
|
86
|
+
|
87
|
+
loop do
|
88
|
+
changes = file.read
|
89
|
+
if changes
|
90
|
+
print changes
|
91
|
+
$stdout.flush
|
92
|
+
else
|
93
|
+
sleep 0.1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
rescue StandardError
|
98
|
+
# Silently handle errors (file might be deleted, process might end, etc.)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
61
102
|
def build_main_command(instance)
|
62
103
|
parts = [
|
63
104
|
"claude",
|
@@ -65,12 +106,26 @@ module ClaudeSwarm
|
|
65
106
|
instance[:model]
|
66
107
|
]
|
67
108
|
|
68
|
-
if @vibe
|
109
|
+
if @vibe || instance[:vibe]
|
69
110
|
parts << "--dangerously-skip-permissions"
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
111
|
+
else
|
112
|
+
# Add allowed tools if any
|
113
|
+
if instance[:tools].any?
|
114
|
+
tools_str = instance[:tools].join(",")
|
115
|
+
parts << "--allowedTools"
|
116
|
+
parts << tools_str
|
117
|
+
end
|
118
|
+
|
119
|
+
# Add disallowed tools if any
|
120
|
+
if instance[:disallowed_tools]&.any?
|
121
|
+
disallowed_tools_str = instance[:disallowed_tools].join(",")
|
122
|
+
parts << "--disallowedTools"
|
123
|
+
parts << disallowed_tools_str
|
124
|
+
end
|
125
|
+
|
126
|
+
# Add permission prompt tool unless in vibe mode
|
127
|
+
parts << "--permission-prompt-tool"
|
128
|
+
parts << "mcp__permissions__check_permission"
|
74
129
|
end
|
75
130
|
|
76
131
|
if instance[:prompt]
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "fast_mcp"
|
5
|
+
require "logger"
|
6
|
+
require "fileutils"
|
7
|
+
require_relative "permission_tool"
|
8
|
+
|
9
|
+
module ClaudeSwarm
|
10
|
+
class PermissionMcpServer
|
11
|
+
SWARM_DIR = ".claude-swarm"
|
12
|
+
SESSIONS_DIR = "sessions"
|
13
|
+
|
14
|
+
def initialize(allowed_tools: nil, disallowed_tools: nil)
|
15
|
+
@allowed_tools = allowed_tools
|
16
|
+
@disallowed_tools = disallowed_tools
|
17
|
+
setup_logging
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
# Parse allowed and disallowed tools
|
22
|
+
allowed_patterns = parse_tool_patterns(@allowed_tools)
|
23
|
+
disallowed_patterns = parse_tool_patterns(@disallowed_tools)
|
24
|
+
|
25
|
+
@logger.info("Starting permission MCP server with allowed patterns: #{allowed_patterns.inspect}, " \
|
26
|
+
"disallowed patterns: #{disallowed_patterns.inspect}")
|
27
|
+
|
28
|
+
# Set the patterns on the tool class
|
29
|
+
PermissionTool.allowed_patterns = allowed_patterns
|
30
|
+
PermissionTool.disallowed_patterns = disallowed_patterns
|
31
|
+
PermissionTool.logger = @logger
|
32
|
+
|
33
|
+
server = FastMcp::Server.new(
|
34
|
+
name: "claude-swarm-permissions",
|
35
|
+
version: "1.0.0"
|
36
|
+
)
|
37
|
+
|
38
|
+
# Register the tool class
|
39
|
+
server.register_tool(PermissionTool)
|
40
|
+
|
41
|
+
@logger.info("Permission MCP server started successfully")
|
42
|
+
|
43
|
+
# Start the stdio server
|
44
|
+
server.start
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def setup_logging
|
50
|
+
# Use environment variable for session timestamp if available
|
51
|
+
# Otherwise create a new timestamp
|
52
|
+
session_timestamp = ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
|
53
|
+
|
54
|
+
# Ensure the session directory exists
|
55
|
+
session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, session_timestamp)
|
56
|
+
FileUtils.mkdir_p(session_dir)
|
57
|
+
|
58
|
+
# Create logger with permissions.log filename
|
59
|
+
log_path = File.join(session_dir, "permissions.log")
|
60
|
+
@logger = Logger.new(log_path)
|
61
|
+
@logger.level = Logger::DEBUG
|
62
|
+
|
63
|
+
# Custom formatter for better readability
|
64
|
+
@logger.formatter = proc do |severity, datetime, _progname, msg|
|
65
|
+
"[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
@logger.info("Permission MCP server logging initialized")
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_tool_patterns(tools)
|
72
|
+
return [] if tools.nil? || tools.empty?
|
73
|
+
|
74
|
+
# Handle both string and array inputs
|
75
|
+
tool_list = tools.is_a?(Array) ? tools : tools.split(/[,\s]+/)
|
76
|
+
|
77
|
+
# Clean up and return
|
78
|
+
tool_list.map(&:strip).reject(&:empty?)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "fast_mcp"
|
5
|
+
|
6
|
+
module ClaudeSwarm
|
7
|
+
class PermissionTool < FastMcp::Tool
|
8
|
+
# Class variables to store allowed/disallowed patterns and logger
|
9
|
+
class << self
|
10
|
+
attr_accessor :allowed_patterns, :disallowed_patterns, :logger
|
11
|
+
end
|
12
|
+
|
13
|
+
tool_name "check_permission"
|
14
|
+
description "Check if a tool is allowed to be used based on configured patterns"
|
15
|
+
|
16
|
+
arguments do
|
17
|
+
required(:tool_name).filled(:string).description("The tool requesting permission")
|
18
|
+
required(:input).value(:hash).description("The input for the tool")
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(tool_name:, input:)
|
22
|
+
logger = self.class.logger
|
23
|
+
logger.info("Permission check requested for tool: #{tool_name}")
|
24
|
+
logger.info("Tool input: #{input.inspect}")
|
25
|
+
|
26
|
+
allowed_patterns = self.class.allowed_patterns || []
|
27
|
+
disallowed_patterns = self.class.disallowed_patterns || []
|
28
|
+
|
29
|
+
logger.info("Checking against allowed patterns: #{allowed_patterns.inspect}")
|
30
|
+
logger.info("Checking against disallowed patterns: #{disallowed_patterns.inspect}")
|
31
|
+
|
32
|
+
# Check if tool matches any disallowed pattern first (takes precedence)
|
33
|
+
disallowed = disallowed_patterns.any? do |pattern|
|
34
|
+
match = matches_pattern?(tool_name, pattern)
|
35
|
+
logger.info("Disallowed pattern '#{pattern}' vs '#{tool_name}': #{match}")
|
36
|
+
match
|
37
|
+
end
|
38
|
+
|
39
|
+
if disallowed
|
40
|
+
logger.info("DENIED: Tool '#{tool_name}' matches disallowed pattern")
|
41
|
+
result = {
|
42
|
+
"behavior" => "deny",
|
43
|
+
"message" => "Tool '#{tool_name}' is explicitly disallowed"
|
44
|
+
}
|
45
|
+
else
|
46
|
+
# Check if the tool matches any allowed pattern
|
47
|
+
allowed = allowed_patterns.empty? || allowed_patterns.any? do |pattern|
|
48
|
+
match = matches_pattern?(tool_name, pattern)
|
49
|
+
logger.info("Allowed pattern '#{pattern}' vs '#{tool_name}': #{match}")
|
50
|
+
match
|
51
|
+
end
|
52
|
+
|
53
|
+
result = if allowed
|
54
|
+
logger.info("ALLOWED: Tool '#{tool_name}' matches configured patterns")
|
55
|
+
{
|
56
|
+
"behavior" => "allow",
|
57
|
+
"updatedInput" => input
|
58
|
+
}
|
59
|
+
else
|
60
|
+
logger.info("DENIED: Tool '#{tool_name}' does not match any allowed patterns")
|
61
|
+
{
|
62
|
+
"behavior" => "deny",
|
63
|
+
"message" => "Tool '#{tool_name}' is not allowed by configured patterns"
|
64
|
+
}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return JSON-stringified result as per SDK docs
|
69
|
+
response = JSON.generate(result)
|
70
|
+
logger.info("Returning response: #{response}")
|
71
|
+
response
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def matches_pattern?(tool_name, pattern)
|
77
|
+
if pattern.include?("*")
|
78
|
+
# Convert wildcard pattern to regex
|
79
|
+
regex_pattern = pattern.gsub("*", ".*")
|
80
|
+
tool_name.match?(/^#{regex_pattern}$/)
|
81
|
+
else
|
82
|
+
# Exact match
|
83
|
+
tool_name == pattern
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -23,6 +23,9 @@ module ClaudeSwarm
|
|
23
23
|
# Add allowed tools from instance config
|
24
24
|
options[:allowed_tools] = instance_config[:tools] if instance_config[:tools]&.any?
|
25
25
|
|
26
|
+
# Add disallowed tools from instance config
|
27
|
+
options[:disallowed_tools] = instance_config[:disallowed_tools] if instance_config[:disallowed_tools]&.any?
|
28
|
+
|
26
29
|
response = executor.execute(prompt, options)
|
27
30
|
|
28
31
|
# Return just the result text as expected by MCP
|
data/lib/claude_swarm/version.rb
CHANGED
data/lib/claude_swarm.rb
CHANGED
@@ -4,6 +4,8 @@ require_relative "claude_swarm/version"
|
|
4
4
|
require_relative "claude_swarm/cli"
|
5
5
|
require_relative "claude_swarm/claude_code_executor"
|
6
6
|
require_relative "claude_swarm/claude_mcp_server"
|
7
|
+
require_relative "claude_swarm/permission_tool"
|
8
|
+
require_relative "claude_swarm/permission_mcp_server"
|
7
9
|
|
8
10
|
module ClaudeSwarm
|
9
11
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
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.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paulo Arruda
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: thor
|
@@ -56,8 +56,8 @@ files:
|
|
56
56
|
- CLAUDE.md
|
57
57
|
- LICENSE
|
58
58
|
- README.md
|
59
|
+
- RELEASING.md
|
59
60
|
- Rakefile
|
60
|
-
- claude-swarm.yml
|
61
61
|
- example/claude-swarm.yml
|
62
62
|
- exe/claude-swarm
|
63
63
|
- lib/claude_swarm.rb
|
@@ -67,6 +67,8 @@ files:
|
|
67
67
|
- lib/claude_swarm/configuration.rb
|
68
68
|
- lib/claude_swarm/mcp_generator.rb
|
69
69
|
- lib/claude_swarm/orchestrator.rb
|
70
|
+
- lib/claude_swarm/permission_mcp_server.rb
|
71
|
+
- lib/claude_swarm/permission_tool.rb
|
70
72
|
- lib/claude_swarm/reset_session_tool.rb
|
71
73
|
- lib/claude_swarm/session_info_tool.rb
|
72
74
|
- lib/claude_swarm/task_tool.rb
|
@@ -85,14 +87,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
87
|
requirements:
|
86
88
|
- - ">="
|
87
89
|
- !ruby/object:Gem::Version
|
88
|
-
version: 3.
|
90
|
+
version: 3.2.0
|
89
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
92
|
requirements:
|
91
93
|
- - ">="
|
92
94
|
- !ruby/object:Gem::Version
|
93
95
|
version: '0'
|
94
96
|
requirements: []
|
95
|
-
rubygems_version: 3.6.
|
97
|
+
rubygems_version: 3.6.7
|
96
98
|
specification_version: 4
|
97
99
|
summary: Orchestrate multiple Claude Code instances as a collaborative AI development
|
98
100
|
team
|
data/claude-swarm.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
version: 1
|
2
|
-
swarm:
|
3
|
-
name: "Swarm Name"
|
4
|
-
main: lead_developer
|
5
|
-
instances:
|
6
|
-
lead_developer:
|
7
|
-
description: "Lead developer coordinating the team and making architectural decisions"
|
8
|
-
directory: .
|
9
|
-
model: opus
|
10
|
-
prompt: "You are the lead developer coordinating the team"
|
11
|
-
tools: [Read, Edit, Bash, Write]
|
12
|
-
connections: [frontend_dev]
|
13
|
-
|
14
|
-
# Example instances (uncomment and modify as needed):
|
15
|
-
|
16
|
-
frontend_dev:
|
17
|
-
description: "Frontend developer specializing in React and modern web technologies"
|
18
|
-
directory: .
|
19
|
-
model: opus
|
20
|
-
prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
|
21
|
-
tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
|