claude_swarm 0.1.8 → 0.1.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4694bdf654adfb2784e4156d504ace610c9b48bd7aedafb4d5571ee0483325e6
4
- data.tar.gz: 9f26361aac4c2815b11f95ab28a67fbb684377d691b7ee5a1b99c89a1a4fa3c1
3
+ metadata.gz: 5d324ca820897ad2929051a87d06f152df536434522f42cba507a9c6984b019f
4
+ data.tar.gz: bd2f9659c374ee277e3f20e4fd339b542887c394730acf5682a90d9c06cfcfb1
5
5
  SHA512:
6
- metadata.gz: 8c5d95adfea48bb560e8ed04d1149284fa199e72a9051e85f7e3e23222a1c117c96b6eb977a418bc573d41a6e6c846d7071dd552121d3810a33b0937ac3228c4
7
- data.tar.gz: aaf42838c5caf597bde330171ebd0aca702089731794f2c45df395b177fe6871c96f27f5838da587a9454de7cc397ca8fae7f910c6f61b1f775f6c2911444823
6
+ metadata.gz: 4ec9b2c70924671846cbfbaf98ddd93869b2e84162c199559b7551ae833bb73ac62950f4bee9dc19f59a138997c4873f6fb8746fa56f5cf1769855e01cdd74e3
7
+ data.tar.gz: 64cc15a5ee7844f900eda0097e539046d730e4eb07b1323217a55dee9b965db506b1c3838d15e311291edc133296191a80a92769727cce84361cba6b5a2c7f0a
data/.rubocop.yml CHANGED
@@ -62,4 +62,7 @@ Minitest/MultipleAssertions:
62
62
  Enabled: false
63
63
 
64
64
  Metrics/ParameterLists:
65
+ Enabled: false
66
+
67
+ Style/PerlBackrefs:
65
68
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## [0.1.10]
2
+
3
+ ### Added
4
+ - **YAML validation for tool fields**: Added strict validation to ensure `tools:`, `allowed_tools:`, and `disallowed_tools:` fields must be arrays in the configuration
5
+ - Clear error messages when tool fields are not arrays (e.g., "Instance 'lead' field 'tools' must be an array, got String")
6
+ - Comprehensive test coverage for the new validation rules
7
+
8
+ ### Fixed
9
+ - Prevents silent conversion of non-array tool values that could lead to unexpected behavior
10
+ - Configuration now fails fast with helpful error messages instead of accepting invalid formats
11
+
12
+ ## [0.1.9]
13
+
14
+ ### Added
15
+ - **Parameter-based tool patterns**: Custom tools now support explicit parameter patterns (e.g., `WebFetch(url:https://example.com/*)`)
16
+ - **Enhanced pattern matching**: File tools support brace expansion and complex glob patterns (e.g., `Read(~/docs/**/*.{txt,md})`)
17
+ - **Comprehensive test coverage**: Added extensive unit and integration tests for permission system
18
+
19
+ ### Changed
20
+ - **Breaking change**: Custom tools with patterns now require explicit parameter syntax - `Tool(param:pattern)` instead of `Tool(pattern)`
21
+ - **Improved pattern parsing**: Tool patterns are now parsed into structured hashes with `tool_name`, `pattern`, and `type` fields
22
+ - **Better pattern enforcement**: Custom tool patterns are now strictly enforced - requests with non-matching parameters are denied
23
+ - Tools without patterns (e.g., `WebFetch`) continue to accept any input parameters
24
+
25
+ ### Fixed
26
+ - Fixed brace expansion in file glob patterns by adding `File::FNM_EXTGLOB` flag
27
+ - Improved parameter pattern parsing to avoid conflicts with URL patterns containing colons
28
+
29
+ ### Internal
30
+ - Major refactoring of `PermissionMcpServer` and `PermissionTool` for better maintainability and readability
31
+ - Extracted pattern matching logic into focused, single-purpose methods
32
+ - Added constants for tool categories and pattern types
33
+ - Improved logging with structured helper methods
34
+
1
35
  ## [0.1.8]
2
36
 
3
37
  ### Added
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Claude Swarm
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg)](https://badge.fury.io/rb/claude_swarm)
3
+ [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=1)](https://badge.fury.io/rb/claude_swarm)
4
4
  [![CI](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml/badge.svg)](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml)
5
5
 
6
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.
@@ -570,4 +570,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/parrud
570
570
 
571
571
  ## License
572
572
 
573
- The gem is available as open source under the terms of the [MIT License](LICENSE).
573
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
@@ -0,0 +1,293 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Multi-Codebase Product Development Team"
4
+ main: cto
5
+ instances:
6
+ # Leadership & Coordination
7
+ cto:
8
+ description: "CTO overseeing all technical teams and architectural decisions"
9
+ directory: ~/projects
10
+ model: opus
11
+ connections: [architect, product_manager, qa_lead, security_lead]
12
+ prompt: "You are the CTO coordinating multiple development teams across different codebases. Focus on high-level architecture, team coordination, and technical strategy."
13
+ allowed_tools:
14
+ - Read
15
+ - WebSearch
16
+ - Edit
17
+
18
+ architect:
19
+ description: "Principal architect designing system-wide solutions and ensuring consistency"
20
+ directory: ~/projects
21
+ model: opus
22
+ connections: [frontend_lead, backend_lead, mobile_lead, devops_lead, data_lead]
23
+ prompt: "You design cross-cutting solutions and ensure architectural consistency across all services."
24
+ allowed_tools:
25
+ - Read
26
+ - Edit
27
+ - WebSearch
28
+
29
+ product_manager:
30
+ description: "Product manager translating business requirements into technical specifications"
31
+ directory: ~/projects/docs/requirements
32
+ model: opus
33
+ connections: [cto, frontend_lead, backend_lead, mobile_lead]
34
+ prompt: "You translate business requirements into technical specifications and user stories."
35
+ allowed_tools:
36
+ - Read
37
+ - Edit
38
+ - Write
39
+
40
+ # Frontend Team
41
+ frontend_lead:
42
+ description: "Frontend team lead managing React web application development"
43
+ directory: ~/projects/web-frontend
44
+ model: opus
45
+ connections: [react_senior, ui_designer, frontend_qa]
46
+ prompt: "You lead the web frontend team, ensuring React best practices and coordinating with other teams."
47
+ allowed_tools:
48
+ - Read
49
+ - Edit
50
+ - Bash
51
+
52
+ react_senior:
53
+ description: "Senior React developer building complex UI components and state management"
54
+ directory: ~/projects/web-frontend/src
55
+ model: opus
56
+ connections: [frontend_lead]
57
+ prompt: "You build React components, manage Redux state, and handle complex frontend logic."
58
+ allowed_tools:
59
+ - Edit
60
+ - Write
61
+ - "Bash(npm:*, yarn:*, jest:*)"
62
+
63
+ ui_designer:
64
+ description: "UI/UX specialist creating responsive designs and managing design system"
65
+ directory: ~/projects/design-system
66
+ model: sonnet
67
+ connections: [frontend_lead, mobile_lead]
68
+ prompt: "You maintain the design system and ensure consistent UI/UX across platforms."
69
+ allowed_tools:
70
+ - Edit
71
+ - Write
72
+ - Read
73
+
74
+ # Backend Services Team
75
+ backend_lead:
76
+ description: "Backend team lead coordinating microservices development"
77
+ directory: ~/projects/backend
78
+ model: opus
79
+ connections: [auth_dev, api_gateway_dev, core_service_dev, shared_lib_dev]
80
+ prompt: "You coordinate backend microservices development and ensure API consistency."
81
+ allowed_tools:
82
+ - Read
83
+ - Edit
84
+ - Bash
85
+
86
+ auth_dev:
87
+ description: "Authentication service developer managing user auth and OAuth"
88
+ directory: ~/projects/backend/auth-service
89
+ model: opus
90
+ prompt: "You develop and maintain the authentication service with OAuth2, JWT, and user management."
91
+ allowed_tools:
92
+ - Edit
93
+ - Write
94
+ - "Bash(go:*, docker:*, make:*)"
95
+
96
+ api_gateway_dev:
97
+ description: "API gateway developer managing request routing and rate limiting"
98
+ directory: ~/projects/backend/api-gateway
99
+ model: opus
100
+ prompt: "You maintain the API gateway handling routing, rate limiting, and request transformation."
101
+ allowed_tools:
102
+ - Edit
103
+ - Write
104
+ - "Bash(go:*, docker:*, kong:*)"
105
+
106
+ core_service_dev:
107
+ description: "Core business logic service developer"
108
+ directory: ~/projects/backend/core-service
109
+ model: opus
110
+ prompt: "You develop the core business logic service handling main application features."
111
+ allowed_tools:
112
+ - Edit
113
+ - Write
114
+ - "Bash(python:*, pip:*, pytest:*, docker:*)"
115
+
116
+ shared_lib_dev:
117
+ description: "Shared libraries developer maintaining common code across services"
118
+ directory: ~/projects/shared-libs
119
+ model: sonnet
120
+ connections: [backend_lead]
121
+ prompt: "You maintain shared libraries used across all backend services."
122
+ allowed_tools:
123
+ - Edit
124
+ - Write
125
+ - "Bash(npm:*, go:*, python:*)"
126
+
127
+ # Mobile Team
128
+ mobile_lead:
129
+ description: "Mobile team lead coordinating iOS and Android development"
130
+ directory: ~/projects/mobile
131
+ model: opus
132
+ connections: [ios_senior, android_senior, mobile_qa]
133
+ prompt: "You coordinate mobile development ensuring feature parity between platforms."
134
+ allowed_tools:
135
+ - Read
136
+ - Edit
137
+
138
+ ios_senior:
139
+ description: "Senior iOS developer building native Swift applications"
140
+ directory: ~/projects/mobile/ios-app
141
+ model: opus
142
+ prompt: "You develop the iOS app using Swift, SwiftUI, and native iOS frameworks."
143
+ allowed_tools:
144
+ - Edit
145
+ - Write
146
+ - "Bash(swift:*, xcodebuild:*, pod:*, fastlane:*)"
147
+
148
+ android_senior:
149
+ description: "Senior Android developer creating Kotlin applications"
150
+ directory: ~/projects/mobile/android-app
151
+ model: opus
152
+ prompt: "You develop the Android app using Kotlin, Jetpack Compose, and Android SDK."
153
+ allowed_tools:
154
+ - Edit
155
+ - Write
156
+ - "Bash(gradle:*, adb:*, fastlane:*)"
157
+
158
+ # Data Team
159
+ data_lead:
160
+ description: "Data team lead managing analytics, ML models, and data pipelines"
161
+ directory: ~/projects/data-platform
162
+ model: opus
163
+ connections: [data_engineer, ml_engineer, analytics_dev]
164
+ prompt: "You lead the data team handling analytics, ML, and data infrastructure."
165
+ allowed_tools:
166
+ - Read
167
+ - Edit
168
+ - Bash
169
+
170
+ data_engineer:
171
+ description: "Data engineer building ETL pipelines and data warehouse"
172
+ directory: ~/projects/data-platform/pipelines
173
+ model: opus
174
+ prompt: "You build and maintain ETL pipelines, data warehouse, and streaming infrastructure."
175
+ allowed_tools:
176
+ - Edit
177
+ - Write
178
+ - "Bash(python:*, airflow:*, spark:*, dbt:*)"
179
+
180
+ ml_engineer:
181
+ description: "ML engineer developing and deploying machine learning models"
182
+ directory: ~/projects/data-platform/ml-models
183
+ model: opus
184
+ prompt: "You develop, train, and deploy ML models for recommendation and prediction features."
185
+ allowed_tools:
186
+ - Edit
187
+ - Write
188
+ - "Bash(python:*, jupyter:*, mlflow:*, docker:*)"
189
+
190
+ analytics_dev:
191
+ description: "Analytics developer creating dashboards and reports"
192
+ directory: ~/projects/data-platform/analytics
193
+ model: sonnet
194
+ prompt: "You build analytics dashboards and create business intelligence reports."
195
+ allowed_tools:
196
+ - Edit
197
+ - Write
198
+ - "Bash(python:*, sql:*)"
199
+
200
+ # DevOps & Infrastructure
201
+ devops_lead:
202
+ description: "DevOps lead managing CI/CD, infrastructure, and platform reliability"
203
+ directory: ~/projects/infrastructure
204
+ model: opus
205
+ connections: [sre_engineer, platform_engineer, cloud_architect]
206
+ prompt: "You lead DevOps ensuring reliable deployments and infrastructure."
207
+ allowed_tools:
208
+ - Read
209
+ - Edit
210
+ - Bash
211
+
212
+ sre_engineer:
213
+ description: "SRE engineer ensuring system reliability and incident response"
214
+ directory: ~/projects/infrastructure/monitoring
215
+ model: opus
216
+ prompt: "You ensure system reliability, manage monitoring, and handle incident response."
217
+ allowed_tools:
218
+ - Edit
219
+ - Write
220
+ - "Bash(kubectl:*, helm:*, prometheus:*, grafana:*)"
221
+
222
+ platform_engineer:
223
+ description: "Platform engineer managing Kubernetes and container infrastructure"
224
+ directory: ~/projects/infrastructure/k8s
225
+ model: opus
226
+ prompt: "You manage Kubernetes clusters, service mesh, and container orchestration."
227
+ allowed_tools:
228
+ - Edit
229
+ - Write
230
+ - "Bash(kubectl:*, helm:*, docker:*, istioctl:*)"
231
+
232
+ cloud_architect:
233
+ description: "Cloud architect managing AWS/GCP infrastructure and costs"
234
+ directory: ~/projects/infrastructure/terraform
235
+ model: opus
236
+ prompt: "You design and manage cloud infrastructure, optimize costs, and ensure security."
237
+ allowed_tools:
238
+ - Edit
239
+ - Write
240
+ - "Bash(terraform:*, aws:*, gcloud:*)"
241
+
242
+ # Quality & Security
243
+ qa_lead:
244
+ description: "QA lead coordinating testing strategy across all platforms"
245
+ directory: ~/projects/qa
246
+ model: opus
247
+ connections: [frontend_qa, backend_qa, mobile_qa]
248
+ prompt: "You coordinate QA efforts ensuring comprehensive test coverage across all platforms."
249
+ allowed_tools:
250
+ - Read
251
+ - Edit
252
+ - Bash
253
+
254
+ frontend_qa:
255
+ description: "Frontend QA engineer writing E2E tests and visual regression tests"
256
+ directory: ~/projects/qa/frontend-tests
257
+ model: sonnet
258
+ prompt: "You write and maintain E2E tests for the web frontend using Cypress and Playwright."
259
+ allowed_tools:
260
+ - Edit
261
+ - Write
262
+ - "Bash(npm:*, cypress:*, playwright:*)"
263
+
264
+ backend_qa:
265
+ description: "Backend QA engineer creating API tests and integration tests"
266
+ directory: ~/projects/qa/backend-tests
267
+ model: sonnet
268
+ prompt: "You create comprehensive API tests and integration tests for all backend services."
269
+ allowed_tools:
270
+ - Edit
271
+ - Write
272
+ - "Bash(python:*, pytest:*, postman:*, k6:*)"
273
+
274
+ mobile_qa:
275
+ description: "Mobile QA engineer testing iOS and Android applications"
276
+ directory: ~/projects/qa/mobile-tests
277
+ model: sonnet
278
+ prompt: "You test mobile applications using automated and manual testing approaches."
279
+ allowed_tools:
280
+ - Edit
281
+ - Write
282
+ - "Bash(appium:*, xctest:*, espresso:*)"
283
+
284
+ security_lead:
285
+ description: "Security lead ensuring application and infrastructure security"
286
+ directory: ~/projects/security
287
+ model: opus
288
+ connections: [cto]
289
+ prompt: "You ensure security best practices, conduct audits, and manage security incidents."
290
+ allowed_tools:
291
+ - Read
292
+ - Edit
293
+ - "Bash(trivy:*, snyk:*, owasp:*)"
@@ -0,0 +1,249 @@
1
+ version: 1
2
+ swarm:
3
+ name: "Rails Test Generation Expert Team"
4
+ main: test_architect
5
+ instances:
6
+ test_architect:
7
+ description: "Test architect coordinating comprehensive test coverage following SOLID principles and Rails best practices"
8
+ directory: .
9
+ model: opus
10
+ connections: [unit_test_expert, integration_test_expert, system_test_expert, test_quality_reviewer, factory_specialist]
11
+ prompt: |
12
+ You are a senior Rails test architect responsible for ensuring comprehensive test coverage across the entire application. Your core responsibilities:
13
+
14
+ - Analyze the codebase to identify what needs testing
15
+ - Delegate appropriate test creation to specialized team members
16
+ - Ensure all tests follow SOLID principles, particularly:
17
+ * Single Responsibility: Each test should verify one behavior
18
+ * Open/Closed: Tests should be extensible without modification
19
+ * Dependency Inversion: Tests should depend on abstractions, not concretions
20
+ - Coordinate test coverage across models, controllers, services, jobs, and system tests
21
+ - Ensure tests follow Rails testing best practices and conventions
22
+ - Maintain a balance between test coverage and test maintainability
23
+
24
+ When delegating tasks:
25
+ - Provide clear context about what needs testing
26
+ - Specify the type of test needed (unit, integration, system)
27
+ - Share relevant code snippets or file paths
28
+ - Request tests that are DRY, readable, and maintainable
29
+ allowed_tools:
30
+ - Read
31
+ - Edit
32
+ - WebSearch
33
+ - Bash
34
+
35
+ unit_test_expert:
36
+ description: "Unit test specialist focusing on models, services, and isolated business logic with Minitest"
37
+ directory: ./spec
38
+ model: opus
39
+ connections: [test_architect, factory_specialist]
40
+ prompt: |
41
+ You are a Rails unit testing expert specializing in Minitest. Your focus areas:
42
+
43
+ MODEL TESTS:
44
+ - Test validations, associations, scopes, and instance methods
45
+ - Use shoulda-matchers for clean validation and association tests
46
+ - Test edge cases and boundary conditions
47
+ - Ensure database-independent tests where possible
48
+
49
+ SERVICE OBJECT TESTS:
50
+ - Test each public method in isolation
51
+ - Mock external dependencies
52
+ - Verify return values and side effects
53
+ - Test error handling and edge cases
54
+
55
+ SOLID PRINCIPLES IN TESTS:
56
+ - Single Responsibility: One assertion per test when possible
57
+ - Interface Segregation: Test public interfaces, not implementation
58
+ - Dependency Inversion: Use doubles/mocks for external dependencies
59
+
60
+ BEST PRACTICES:
61
+ - Use `let` and `let!` appropriately
62
+ - Prefer `describe` and `context` for clear test organization
63
+ - Write descriptive test names that document behavior
64
+ - Keep tests fast by avoiding unnecessary database hits
65
+ - Use factories instead of fixtures
66
+ - Follow the Arrange-Act-Assert pattern
67
+
68
+ Always write tests that serve as living documentation of the code's behavior.
69
+ allowed_tools:
70
+ - Read
71
+ - Edit
72
+ - Write
73
+ - "Bash(bundle:*)"
74
+
75
+ integration_test_expert:
76
+ description: "Integration test specialist for controllers, API endpoints, and request specs"
77
+ directory: ./spec
78
+ model: opus
79
+ connections: [test_architect, factory_specialist]
80
+ prompt: |
81
+ You are a Rails integration testing expert focusing on request specs and controller tests. Your expertise:
82
+
83
+ REQUEST SPECS:
84
+ - Test complete request/response cycles
85
+ - Verify HTTP status codes, response bodies, and headers
86
+ - Test authentication and authorization
87
+ - Ensure proper parameter handling and validation
88
+ - Test API versioning if applicable
89
+
90
+ CONTROLLER INTEGRATION:
91
+ - Test the integration between controllers and models
92
+ - Verify proper use of strong parameters
93
+ - Test before_action callbacks and filters
94
+ - Ensure proper error handling and response formats
95
+
96
+ SOLID PRINCIPLES:
97
+ - Open/Closed: Write tests that don't break when implementation changes
98
+ - Liskov Substitution: Test behavior, not specific implementations
99
+
100
+ BEST PRACTICES:
101
+ - Use request specs over controller specs (Rails 5+ recommendation)
102
+ - Test happy paths and error scenarios
103
+ - Include tests for different user roles and permissions
104
+ - Test pagination, filtering, and sorting where applicable
105
+ - Use shared examples for common behaviors
106
+ - Test both JSON and HTML responses as needed
107
+ - Verify side effects (emails sent, jobs enqueued, etc.)
108
+
109
+ Focus on testing the contract between the client and server, not implementation details.
110
+ allowed_tools:
111
+ - Read
112
+ - Edit
113
+ - Write
114
+ - "Bash(bundle:*, rails:*)"
115
+
116
+ system_test_expert:
117
+ description: "System test specialist for end-to-end user flows and JavaScript interactions"
118
+ directory: ./spec/system
119
+ model: opus
120
+ connections: [test_architect]
121
+ prompt: |
122
+ You are a Rails system testing expert using Capybara and Selenium. Your specialization:
123
+
124
+ SYSTEM TESTS:
125
+ - Test complete user workflows from start to finish
126
+ - Verify JavaScript interactions and dynamic content
127
+ - Test real browser behavior and compatibility
128
+ - Ensure accessibility compliance in tests
129
+
130
+ CAPYBARA BEST PRACTICES:
131
+ - Use semantic selectors (data-testid, aria-labels) over CSS
132
+ - Implement proper wait strategies for async operations
133
+ - Use Page Object Pattern for maintainable tests
134
+ - Test both happy paths and error scenarios
135
+
136
+ TEST SCENARIOS:
137
+ - User authentication flows (sign up, login, logout)
138
+ - Form submissions with client-side validations
139
+ - AJAX interactions and real-time updates
140
+ - File uploads and downloads
141
+ - Multi-step workflows and wizards
142
+
143
+ PERFORMANCE & RELIABILITY:
144
+ - Keep tests deterministic and flake-free
145
+ - Use headless browsers for CI environments
146
+ - Implement proper database cleaning strategies
147
+ - Take screenshots on failures for debugging
148
+
149
+ SOLID PRINCIPLES:
150
+ - Single Responsibility: Each test verifies one user flow
151
+ - DRY: Extract common interactions into helper methods
152
+
153
+ Write tests that ensure the application works correctly from the user's perspective.
154
+ allowed_tools:
155
+ - Read
156
+ - Edit
157
+ - Write
158
+ - "Bash(bundle:*)"
159
+
160
+ factory_specialist:
161
+ description: "Test data specialist managing factories, traits, and test data generation"
162
+ directory: ./spec/factories
163
+ model: opus
164
+ connections: [test_architect, unit_test_expert, integration_test_expert]
165
+ prompt: |
166
+ You are a Rails test data expert specializing in FactoryBot. Your responsibilities:
167
+
168
+ FACTORY DESIGN:
169
+ - Create minimal, valid factories by default
170
+ - Use traits for different object states and variations
171
+ - Implement intelligent sequences for unique attributes
172
+ - Handle complex associations properly
173
+ - Avoid creating unnecessary associated objects
174
+
175
+ BEST PRACTICES:
176
+ - Follow Single Responsibility: One factory per model
177
+ - Use inheritance for STI models
178
+ - Implement transient attributes for flexible factories
179
+ - Create aliases for better readability
180
+ - Use callbacks sparingly and only when necessary
181
+
182
+ PERFORMANCE:
183
+ - Prefer build/build_stubbed over create when possible
184
+ - Use create_list efficiently
185
+ - Implement lazy evaluation for expensive attributes
186
+ - Avoid N+1 queries in factory definitions
187
+
188
+ DATA INTEGRITY:
189
+ - Ensure factories always create valid objects
190
+ - Test factories themselves in a separate spec
191
+ - Use Faker for realistic test data
192
+ - Handle edge cases (empty strings, nil values, etc.)
193
+
194
+ SOLID PRINCIPLES:
195
+ - Interface Segregation: Factories should be easy to use
196
+ - Dependency Inversion: Factories shouldn't depend on application state
197
+
198
+ Your factories should make tests readable, fast, and maintainable.
199
+ allowed_tools:
200
+ - Read
201
+ - Edit
202
+ - Write
203
+ - "Bash(bundle:*)"
204
+
205
+ test_quality_reviewer:
206
+ description: "Test quality assurance specialist ensuring best practices, coverage, and maintainability"
207
+ directory: .
208
+ model: opus
209
+ connections: [test_architect]
210
+ prompt: |
211
+ You are a Rails test quality reviewer ensuring all tests meet the highest standards. Your review criteria:
212
+
213
+ CODE QUALITY:
214
+ - Tests follow AAA pattern (Arrange, Act, Assert)
215
+ - No test interdependencies
216
+ - Proper use of Minitest's DSL
217
+ - Clear, descriptive test names
218
+ - Appropriate use of shared examples and contexts
219
+
220
+ COVERAGE ANALYSIS:
221
+ - Identify missing test scenarios
222
+ - Ensure edge cases are covered
223
+ - Verify error handling is tested
224
+ - Check for untested code paths
225
+ - Recommend additional test cases
226
+
227
+ PERFORMANCE:
228
+ - Tests run quickly (prefer unit over integration when possible)
229
+ - Minimal database interactions
230
+ - Proper use of test doubles and stubs
231
+ - No unnecessary setup or teardown
232
+
233
+ MAINTAINABILITY:
234
+ - Tests are DRY without sacrificing clarity
235
+ - Proper extraction of test helpers
236
+ - Good balance between shared examples and explicit tests
237
+ - Tests serve as documentation
238
+
239
+ SOLID COMPLIANCE:
240
+ - Single Responsibility: Each test has one clear purpose
241
+ - Open/Closed: Tests don't break with refactoring
242
+ - Liskov Substitution: Tests work with subclasses
243
+ - Interface Segregation: Tests focus on public APIs
244
+ - Dependency Inversion: Tests use abstractions
245
+
246
+ Provide actionable feedback to improve test quality and coverage.
247
+ allowed_tools:
248
+ - Read
249
+ - "Bash(bundle:*, rubocop:*)"
@@ -171,9 +171,9 @@ module ClaudeSwarm
171
171
  end
172
172
 
173
173
  desc "tools-mcp", "Start a permission management MCP server for tool access control"
174
- method_option :allowed_tools, aliases: "-t", type: :array,
174
+ method_option :allowed_tools, aliases: "-t", type: :string,
175
175
  desc: "Comma-separated list of allowed tool patterns (supports wildcards)"
176
- method_option :disallowed_tools, type: :array,
176
+ method_option :disallowed_tools, type: :string,
177
177
  desc: "Comma-separated list of disallowed tool patterns (supports wildcards)"
178
178
  method_option :debug, type: :boolean, default: false,
179
179
  desc: "Enable debug output"
@@ -76,6 +76,11 @@ module ClaudeSwarm
76
76
  # Validate required fields
77
77
  raise Error, "Instance '#{name}' missing required 'description' field" unless config["description"]
78
78
 
79
+ # Validate tool fields are arrays if present
80
+ validate_tool_field(name, config, "tools")
81
+ validate_tool_field(name, config, "allowed_tools")
82
+ validate_tool_field(name, config, "disallowed_tools")
83
+
79
84
  # Support both 'tools' (deprecated) and 'allowed_tools' for backward compatibility
80
85
  allowed_tools = config["allowed_tools"] || config["tools"] || []
81
86
 
@@ -129,6 +134,13 @@ module ClaudeSwarm
129
134
  end
130
135
  end
131
136
 
137
+ def validate_tool_field(instance_name, config, field_name)
138
+ return unless config.key?(field_name)
139
+
140
+ field_value = config[field_name]
141
+ raise Error, "Instance '#{instance_name}' field '#{field_name}' must be an array, got #{field_value.class.name}" unless field_value.is_a?(Array)
142
+ end
143
+
132
144
  def expand_path(path)
133
145
  Pathname.new(path).expand_path(@config_dir).to_s
134
146
  end
@@ -8,9 +8,22 @@ require_relative "permission_tool"
8
8
 
9
9
  module ClaudeSwarm
10
10
  class PermissionMcpServer
11
+ # Directory constants
11
12
  SWARM_DIR = ".claude-swarm"
12
13
  SESSIONS_DIR = "sessions"
13
14
 
15
+ # Server configuration
16
+ SERVER_NAME = "claude-swarm-permissions"
17
+ SERVER_VERSION = "1.0.0"
18
+
19
+ # Tool categories
20
+ FILE_TOOLS = %w[Read Write Edit].freeze
21
+ BASH_TOOL = "Bash"
22
+
23
+ # Pattern matching
24
+ TOOL_PATTERN_REGEX = /^([^()]+)\(([^)]+)\)$/
25
+ PARAM_PATTERN_REGEX = /^(\w+)\s*:\s*(.+)$/
26
+
14
27
  def initialize(allowed_tools: nil, disallowed_tools: nil)
15
28
  @allowed_tools = allowed_tools
16
29
  @disallowed_tools = disallowed_tools
@@ -18,64 +31,160 @@ module ClaudeSwarm
18
31
  end
19
32
 
20
33
  def start
21
- # Parse allowed and disallowed tools
34
+ configure_permission_tool
35
+ create_and_start_server
36
+ end
37
+
38
+ private
39
+
40
+ def configure_permission_tool
22
41
  allowed_patterns = parse_tool_patterns(@allowed_tools)
23
42
  disallowed_patterns = parse_tool_patterns(@disallowed_tools)
24
43
 
25
- @logger.info("Starting permission MCP server with allowed patterns: #{allowed_patterns.inspect}, " \
26
- "disallowed patterns: #{disallowed_patterns.inspect}")
44
+ log_configuration(allowed_patterns, disallowed_patterns)
27
45
 
28
- # Set the patterns on the tool class
29
46
  PermissionTool.allowed_patterns = allowed_patterns
30
47
  PermissionTool.disallowed_patterns = disallowed_patterns
31
48
  PermissionTool.logger = @logger
49
+ end
32
50
 
51
+ def create_and_start_server
33
52
  server = FastMcp::Server.new(
34
- name: "claude-swarm-permissions",
35
- version: "1.0.0"
53
+ name: SERVER_NAME,
54
+ version: SERVER_VERSION
36
55
  )
37
56
 
38
- # Register the tool class
39
57
  server.register_tool(PermissionTool)
40
-
41
58
  @logger.info("Permission MCP server started successfully")
42
-
43
- # Start the stdio server
44
59
  server.start
45
60
  end
46
61
 
47
- private
48
-
49
62
  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")
63
+ session_dir = create_session_directory
64
+ @logger = create_logger(session_dir)
65
+ @logger.info("Permission MCP server logging initialized")
66
+ end
53
67
 
54
- # Ensure the session directory exists
68
+ def create_session_directory
69
+ session_timestamp = ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
55
70
  session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, session_timestamp)
56
71
  FileUtils.mkdir_p(session_dir)
72
+ session_dir
73
+ end
57
74
 
58
- # Create logger with permissions.log filename
75
+ def create_logger(session_dir)
59
76
  log_path = File.join(session_dir, "permissions.log")
60
- @logger = Logger.new(log_path)
61
- @logger.level = Logger::DEBUG
77
+ logger = Logger.new(log_path)
78
+ logger.level = Logger::DEBUG
79
+ logger.formatter = log_formatter
80
+ logger
81
+ end
62
82
 
63
- # Custom formatter for better readability
64
- @logger.formatter = proc do |severity, datetime, _progname, msg|
83
+ def log_formatter
84
+ proc do |severity, datetime, _progname, msg|
65
85
  "[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
66
86
  end
87
+ end
67
88
 
68
- @logger.info("Permission MCP server logging initialized")
89
+ def log_configuration(allowed_patterns, disallowed_patterns)
90
+ @logger.info("Starting permission MCP server with allowed patterns: #{allowed_patterns.inspect}, " \
91
+ "disallowed patterns: #{disallowed_patterns.inspect}")
69
92
  end
70
93
 
71
94
  def parse_tool_patterns(tools)
72
95
  return [] if tools.nil? || tools.empty?
73
96
 
74
- # Handle both string and array inputs
75
- tool_list = tools.is_a?(Array) ? tools : tools.split(/[,\s]+/)
97
+ normalize_tool_list(tools).filter_map do |tool|
98
+ parse_single_tool_pattern(tool.strip)
99
+ end
100
+ end
101
+
102
+ def normalize_tool_list(tools)
103
+ tools.is_a?(Array) ? tools : tools.split(/[,\s]+/)
104
+ end
105
+
106
+ def parse_single_tool_pattern(tool)
107
+ return nil if tool.empty?
108
+
109
+ if (match = tool.match(TOOL_PATTERN_REGEX))
110
+ parse_tool_with_pattern(match[1], match[2])
111
+ elsif tool.include?("*")
112
+ create_wildcard_tool_pattern(tool)
113
+ else
114
+ create_exact_tool_pattern(tool)
115
+ end
116
+ end
117
+
118
+ def parse_tool_with_pattern(tool_name, pattern)
119
+ case tool_name
120
+ when *FILE_TOOLS
121
+ create_file_tool_pattern(tool_name, pattern)
122
+ when BASH_TOOL
123
+ create_bash_tool_pattern(tool_name, pattern)
124
+ else
125
+ create_custom_tool_pattern(tool_name, pattern)
126
+ end
127
+ end
128
+
129
+ def create_file_tool_pattern(tool_name, pattern)
130
+ {
131
+ tool_name: tool_name,
132
+ pattern: File.expand_path(pattern),
133
+ type: :glob
134
+ }
135
+ end
136
+
137
+ def create_bash_tool_pattern(tool_name, pattern)
138
+ {
139
+ tool_name: tool_name,
140
+ pattern: process_bash_pattern(pattern),
141
+ type: :regex
142
+ }
143
+ end
144
+
145
+ def process_bash_pattern(pattern)
146
+ if pattern.include?(":")
147
+ # Colon syntax: convert parts and join with spaces
148
+ pattern.split(":")
149
+ .map { |part| part.gsub("*", ".*") }
150
+ .join(" ")
151
+ else
152
+ # Literal pattern: escape asterisks
153
+ pattern.gsub("*", "\\*")
154
+ end
155
+ end
156
+
157
+ def create_custom_tool_pattern(tool_name, pattern)
158
+ {
159
+ tool_name: tool_name,
160
+ pattern: parse_parameter_patterns(pattern),
161
+ type: :params
162
+ }
163
+ end
164
+
165
+ def parse_parameter_patterns(pattern)
166
+ pattern.split(",").each_with_object({}) do |param_pair, params|
167
+ param_pair = param_pair.strip
168
+ if (match = param_pair.match(PARAM_PATTERN_REGEX))
169
+ params[match[1]] = match[2]
170
+ end
171
+ end
172
+ end
173
+
174
+ def create_wildcard_tool_pattern(tool)
175
+ {
176
+ tool_name: tool.gsub("*", ".*"),
177
+ pattern: nil,
178
+ type: :regex
179
+ }
180
+ end
76
181
 
77
- # Clean up and return
78
- tool_list.map(&:strip).reject(&:empty?)
182
+ def create_exact_tool_pattern(tool)
183
+ {
184
+ tool_name: tool,
185
+ pattern: nil,
186
+ type: :exact
187
+ }
79
188
  end
80
189
  end
81
190
  end
@@ -10,6 +10,17 @@ module ClaudeSwarm
10
10
  attr_accessor :allowed_patterns, :disallowed_patterns, :logger
11
11
  end
12
12
 
13
+ # Tool categories
14
+ FILE_TOOLS = %w[Read Write Edit].freeze
15
+ BASH_TOOL = "Bash"
16
+
17
+ # Response behaviors
18
+ BEHAVIOR_ALLOW = "allow"
19
+ BEHAVIOR_DENY = "deny"
20
+
21
+ # File matching flags
22
+ FILE_MATCH_FLAGS = File::FNM_DOTMATCH | File::FNM_PATHNAME | File::FNM_EXTGLOB
23
+
13
24
  tool_name "check_permission"
14
25
  description "Check if a tool is allowed to be used based on configured patterns"
15
26
 
@@ -19,69 +30,172 @@ module ClaudeSwarm
19
30
  end
20
31
 
21
32
  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}")
33
+ @current_tool_name = tool_name
34
+ log_request(tool_name, input)
35
+
36
+ result = evaluate_permission(tool_name, input)
37
+ response = JSON.generate(result)
38
+
39
+ log_response(response)
40
+ response
41
+ end
25
42
 
26
- allowed_patterns = self.class.allowed_patterns || []
27
- disallowed_patterns = self.class.disallowed_patterns || []
43
+ private
44
+
45
+ def evaluate_permission(tool_name, input)
46
+ if explicitly_disallowed?(tool_name, input)
47
+ deny_response(tool_name, "explicitly disallowed")
48
+ elsif implicitly_allowed?(tool_name, input)
49
+ allow_response(input)
50
+ else
51
+ deny_response(tool_name, "not allowed by configured patterns")
52
+ end
53
+ end
54
+
55
+ def explicitly_disallowed?(tool_name, input)
56
+ check_patterns(disallowed_patterns, tool_name, input, "Disallowed")
57
+ end
28
58
 
29
- logger.info("Checking against allowed patterns: #{allowed_patterns.inspect}")
30
- logger.info("Checking against disallowed patterns: #{disallowed_patterns.inspect}")
59
+ def implicitly_allowed?(tool_name, input)
60
+ allowed_patterns.empty? || check_patterns(allowed_patterns, tool_name, input, "Allowed")
61
+ end
31
62
 
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}")
63
+ def check_patterns(patterns, tool_name, input, pattern_type)
64
+ patterns.any? do |pattern_hash|
65
+ match = matches_pattern?(tool_name, input, pattern_hash)
66
+ log_pattern_check(pattern_type, pattern_hash, tool_name, input, match)
36
67
  match
37
68
  end
69
+ end
70
+
71
+ def matches_pattern?(tool_name, input, pattern_hash)
72
+ return false unless tool_name_matches?(tool_name, pattern_hash)
73
+ return true if pattern_hash[:pattern].nil?
74
+
75
+ match_tool_specific_pattern(tool_name, input, pattern_hash)
76
+ end
38
77
 
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
- }
78
+ def tool_name_matches?(tool_name, pattern_hash)
79
+ case pattern_hash[:type]
80
+ when :regex
81
+ tool_name.match?(/^#{pattern_hash[:tool_name]}$/)
45
82
  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
83
+ tool_name == pattern_hash[:tool_name]
66
84
  end
85
+ end
67
86
 
68
- # Return JSON-stringified result as per SDK docs
69
- response = JSON.generate(result)
70
- logger.info("Returning response: #{response}")
71
- response
87
+ def match_tool_specific_pattern(_tool_name, input, pattern_hash)
88
+ case pattern_hash[:tool_name]
89
+ when BASH_TOOL
90
+ match_bash_pattern(input, pattern_hash)
91
+ when *FILE_TOOLS
92
+ match_file_pattern(input, pattern_hash[:pattern])
93
+ else
94
+ match_custom_tool_pattern(input, pattern_hash)
95
+ end
72
96
  end
73
97
 
74
- private
98
+ def match_bash_pattern(input, pattern_hash)
99
+ command = extract_field_value(input, "command")
100
+ return false unless command
75
101
 
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}$/)
102
+ if pattern_hash[:type] == :regex
103
+ command.match?(/^#{pattern_hash[:pattern]}$/)
81
104
  else
82
- # Exact match
83
- tool_name == pattern
105
+ command == pattern_hash[:pattern]
84
106
  end
85
107
  end
108
+
109
+ def match_file_pattern(input, pattern)
110
+ file_path = extract_field_value(input, "file_path")
111
+ unless file_path
112
+ log_missing_field("file_path", input)
113
+ return false
114
+ end
115
+
116
+ File.fnmatch(pattern, File.expand_path(file_path), FILE_MATCH_FLAGS)
117
+ end
118
+
119
+ def match_custom_tool_pattern(input, pattern_hash)
120
+ return false unless pattern_hash[:type] == :params && pattern_hash[:pattern].is_a?(Hash)
121
+ return false if pattern_hash[:pattern].empty?
122
+
123
+ match_parameter_patterns(input, pattern_hash[:pattern])
124
+ end
125
+
126
+ def match_parameter_patterns(input, param_patterns)
127
+ param_patterns.all? do |param_name, param_pattern|
128
+ value = extract_field_value(input, param_name.to_s)
129
+ return false unless value
130
+
131
+ regex_pattern = glob_to_regex(param_pattern)
132
+ value.to_s.match?(/^#{regex_pattern}$/)
133
+ end
134
+ end
135
+
136
+ def extract_field_value(input, field_name)
137
+ input[field_name] || input[field_name.to_sym]
138
+ end
139
+
140
+ def glob_to_regex(pattern)
141
+ Regexp.escape(pattern)
142
+ .gsub('\*', ".*")
143
+ .gsub('\?', ".")
144
+ end
145
+
146
+ # Response builders
147
+ def allow_response(input)
148
+ log_decision("ALLOWED", "matches configured patterns")
149
+ {
150
+ "behavior" => BEHAVIOR_ALLOW,
151
+ "updatedInput" => input
152
+ }
153
+ end
154
+
155
+ def deny_response(tool_name, reason)
156
+ log_decision("DENIED", "is #{reason}")
157
+ {
158
+ "behavior" => BEHAVIOR_DENY,
159
+ "message" => "Tool '#{tool_name}' is #{reason}"
160
+ }
161
+ end
162
+
163
+ # Logging helpers
164
+ def log_request(tool_name, input)
165
+ logger&.info("Permission check requested for tool: #{tool_name}")
166
+ logger&.info("Tool input: #{input.inspect}")
167
+ logger&.info("Checking against allowed patterns: #{allowed_patterns.inspect}")
168
+ logger&.info("Checking against disallowed patterns: #{disallowed_patterns.inspect}")
169
+ end
170
+
171
+ def log_response(response)
172
+ logger&.info("Returning response: #{response}")
173
+ end
174
+
175
+ def log_pattern_check(pattern_type, pattern_hash, tool_name, input, match)
176
+ logger&.info("#{pattern_type} pattern '#{pattern_hash.inspect}' vs '#{tool_name}' " \
177
+ "with input '#{input.inspect}': #{match}")
178
+ end
179
+
180
+ def log_decision(status, reason)
181
+ logger&.info("#{status}: Tool '#{@current_tool_name}' #{reason}")
182
+ end
183
+
184
+ def log_missing_field(field_name, input)
185
+ logger&.info("#{field_name} not found in input: #{input.inspect}")
186
+ end
187
+
188
+ # Convenience accessors
189
+ def logger
190
+ self.class.logger
191
+ end
192
+
193
+ def allowed_patterns
194
+ self.class.allowed_patterns || []
195
+ end
196
+
197
+ def disallowed_patterns
198
+ self.class.disallowed_patterns || []
199
+ end
86
200
  end
87
201
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.10"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda
@@ -59,6 +59,8 @@ files:
59
59
  - RELEASING.md
60
60
  - Rakefile
61
61
  - example/claude-swarm.yml
62
+ - example/microservices-team.yml
63
+ - example/test-generation.yml
62
64
  - exe/claude-swarm
63
65
  - lib/claude_swarm.rb
64
66
  - lib/claude_swarm/claude_code_executor.rb