Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:42:29 +08:00
commit bece5178ef
31 changed files with 9410 additions and 0 deletions

View File

@@ -0,0 +1,352 @@
---
name: agent-coordination-patterns
description: Optimal multi-agent coordination strategies for the rails-architect
auto_invoke: true
trigger_for: [rails-architect]
tags: [coordination, orchestration, multi-agent, workflow, parallel, sequential]
priority: 4
version: 2.1
---
# Agent Coordination Patterns Skill
Provides optimal coordination strategies for multi-agent Rails workflows.
## What This Skill Does
**For Rails Architect:**
- Parallel vs sequential execution decisions
- Dependency management between agents
- Error recovery across agent handoffs
- State management in multi-agent workflows
**Auto-invokes:** Only for rails-architect agent
**Purpose:** Optimize agent coordination for efficiency
## Coordination Strategies
### 1. Parallel Execution
**When to use:**
- Tasks are independent
- No shared dependencies
- Can execute simultaneously
**Example:**
```
Feature: Blog with posts and comments
Parallel execution:
├── Agent 1: Create Post model
└── Agent 2: Create Comment model
Both can start immediately (independent models)
```
**Pattern:**
```markdown
Task tool invocations:
1. Invoke rails-model-specialist (Post) - don't wait
2. Invoke rails-model-specialist (Comment) - don't wait
3. Wait for both completions
4. Proceed with next phase
```
**Benefits:**
- 2x faster for 2 independent tasks
- Better resource utilization
- Faster user feedback
### 2. Sequential Execution
**When to use:**
- Task B depends on Task A output
- Shared state modification
- Order matters for correctness
**Example:**
```
Feature: API endpoint for posts
Sequential execution:
1. Create Post model (needed by controller)
2. Wait for completion
3. Create PostsController (uses Post model)
4. Wait for completion
5. Create tests (test both model + controller)
```
**Pattern:**
```markdown
Task tool invocations:
1. Invoke rails-model-specialist (Post)
2. Wait for completion
3. Invoke rails-controller-specialist (uses Post)
4. Wait for completion
5. Invoke rails-test-specialist
```
**Benefits:**
- Ensures correct dependency order
- Avoids race conditions
- Clearer error attribution
### 3. Hybrid Execution
**When to use:**
- Mix of dependent and independent tasks
- Optimize for maximum parallelism
**Example:**
```
Feature: Complete e-commerce order flow
Phase 1 (Parallel):
├── Create Order model
├── Create OrderItem model
└── Create Product model (if needed)
Phase 2 (Sequential after Phase 1):
├── Create OrderProcessingService (needs Order, OrderItem, Product)
Phase 3 (Parallel):
├── Create OrdersController
└── Create API serializers
Phase 4 (Sequential):
└── Create comprehensive tests
```
**Pattern:**
```markdown
# Phase 1
parallel_invoke([
rails-model-specialist(Order),
rails-model-specialist(OrderItem),
rails-model-specialist(Product)
])
wait_all()
# Phase 2
invoke(rails-service-specialist(OrderProcessingService))
wait()
# Phase 3
parallel_invoke([
rails-controller-specialist(OrdersController),
rails-view-specialist(Serializers)
])
wait_all()
# Phase 4
invoke(rails-test-specialist(ComprehensiveTests))
```
**Benefits:**
- Maximizes parallelism
- Respects dependencies
- Optimal execution time
## Dependency Analysis
### Dependency Types
**1. Model Dependencies:**
```
User model → Post model (belongs_to :user)
Post model → Comment model (has_many :comments)
Order: User → Post → Comment (sequential)
```
**2. Controller Dependencies:**
```
Model exists → Controller can be created
Routes defined → Controller actions valid
Order: Model → Controller (sequential)
```
**3. View Dependencies:**
```
Controller exists → Views can reference actions
Model exists → Views can access attributes
Order: Model + Controller → Views (sequential after both)
```
**4. Test Dependencies:**
```
Implementation exists → Tests can be written
Order: Feature implementation → Tests (sequential)
Exception: TDD approach inverts this (tests first)
```
### Dependency Detection
**Automatic detection:**
```ruby
# Code analysis
class Post < ApplicationRecord
belongs_to :user # Depends on User model
end
# Architect detects:
# - User model must exist before Post
# - Sequential: User → Post
```
**Manual specification:**
```markdown
User specifies: "Create Order with OrderItems"
Architect infers:
- OrderItem references Order (foreign key)
- Order must be created first
- Sequential: Order → OrderItem
```
## Error Recovery Patterns
### Pattern 1: Retry with Fix
**Scenario:** Agent fails with correctable error
**Strategy:**
```
1. Agent fails (e.g., missing gem)
2. Analyze error message
3. Apply fix (add gem to Gemfile)
4. Retry same agent
5. Success
```
**Max retries:** 3 per agent
### Pattern 2: Alternative Approach
**Scenario:** Agent fails, different approach needed
**Strategy:**
```
1. Agent fails (e.g., complex service object too ambitious)
2. Analyze failure reason
3. Switch to simpler pattern (extract to concern instead)
4. Invoke different agent or modify parameters
5. Success
```
### Pattern 3: Graceful Degradation
**Scenario:** Agent fails, feature can be partial
**Strategy:**
```
1. Core agent succeeds (Model created)
2. Enhancement agent fails (Serializer generation)
3. Decision: Ship core functionality
4. Log TODO for enhancement
5. Continue with partial implementation
```
## State Management
### Passing Context Between Agents
**Problem:** Agent B needs info from Agent A
**Solution 1: File system state**
```
1. Agent A creates Model file
2. Agent B reads Model file
3. Agent B uses Model info (class name, associations)
```
**Solution 2: Explicit parameter passing**
```
1. Agent A returns: { model_name: "Post", attributes: [...] }
2. Architect stores in context
3. Agent B receives context: create_controller(context[:model_name])
```
### Shared State Conflicts
**Problem:** Two agents modify same file
**Solution: Sequential execution**
```
Scenario: Two agents both need to modify routes.rb
Wrong (parallel):
├── Agent A: adds posts routes
└── Agent B: adds comments routes
Result: Race condition, lost changes
Correct (sequential):
1. Agent A: adds posts routes
2. Wait for completion
3. Agent B: adds comments routes (reads latest routes.rb)
Result: Both changes preserved
```
## Performance Optimization
### Parallelism Limits
**Don't over-parallelize:**
```
Bad: Spawn 10 agents simultaneously
- Resource contention
- Harder to debug
- Diminishing returns
Good: Spawn 2-3 agents per phase
- Manageable
- Clear progress
- Easier error tracking
```
### Execution Time Estimates
**Sequential baseline:**
```
7 agents × 5 min each = 35 min total
```
**With optimal parallelism:**
```
Phase 1: 2 agents parallel = 5 min
Phase 2: 3 agents parallel = 5 min
Phase 3: 2 agents parallel = 5 min
Total: 15 min (2.3x faster)
```
## Configuration
```yaml
# .agent-coordination.yml
execution:
max_parallel_agents: 3
retry_limit: 3
timeout_per_agent: 300 # 5 min
dependencies:
auto_detect: true
strict_ordering: false
error_recovery:
retry_on_failure: true
alternative_approaches: true
graceful_degradation: true
```
## References
- **Orchestration Pattern**: Used by rails-architect agent
- **Task Tool**: Native Claude Code multi-agent support
- **Pattern Library**: /patterns/api-patterns.md for feature patterns
---
**This skill helps the architect coordinate agents efficiently for fast, reliable implementations.**

View File

@@ -0,0 +1,330 @@
# Rails API Lookup
---
name: rails-api-lookup
description: Looks up Rails API documentation for specific classes/methods using Ref (primary) or WebFetch (fallback) with API reference
version: 1.2.0
author: Rails Workflow Team
tags: [rails, api, documentation, reference, ref]
priority: 3
---
## Purpose
Fetches precise API documentation from official Rails API docs for specific classes, modules, and methods. Returns signatures, parameters, return values, and usage examples.
**Replaces**: `mcp__rails__search_docs` MCP tool (API-specific queries)
## Usage
**Auto-invoked** when agents need API details:
```
Agent: "What parameters does validates :email accept?"
*invokes rails-api-lookup class="ActiveModel::Validations" method="validates"*
```
**Manual invocation**:
```
@rails-api-lookup class="ActiveRecord::Base" method="where"
@rails-api-lookup class="ActionController::Base"
@rails-api-lookup module="ActiveSupport::Concern"
```
## Supported Lookups
See `reference.md` for complete class/module list. Common APIs:
### Active Record
- `ActiveRecord::Base` - Model base class
- `ActiveRecord::Relation` - Query interface (where, joins, etc.)
- `ActiveRecord::Associations` - Association methods
- `ActiveRecord::Validations` - Validation methods
- `ActiveRecord::Callbacks` - Callback methods
### Action Controller
- `ActionController::Base` - Controller base class
- `ActionController::Metal` - Minimal controller
- `ActionController::API` - API controller
### Action View
- `ActionView::Base` - View rendering
- `ActionView::Helpers` - View helpers
- `ActionView::Template` - Template handling
### Active Support
- `ActiveSupport::Concern` - Module mixins
- `ActiveSupport::Callbacks` - Callback framework
- `ActiveSupport::TimeWithZone` - Time handling
## Search Process
### Step 1: Version Detection
```
Invokes: @rails-version-detector
Result: Rails 7.1.3
Maps to: https://api.rubyonrails.org/v7.1/
```
### Step 2: Class/Method Mapping
```
Input: class="ActiveRecord::Base" method="where"
Lookup: reference.md → "ActiveRecord/QueryMethods.html#method-i-where"
URL: https://api.rubyonrails.org/v7.1/ActiveRecord/QueryMethods.html#method-i-where
```
### Step 3: Content Fetch
**Method 1: Context7 (Fastest)**:
```
Tool: context7_fetch
Query: "Rails [version] [class] [method] API"
```
**Method 2: Ref (Token-Efficient)**:
```
Tool: ref_search_documentation
Query: "Rails [version] [class] [method] API documentation"
Then: ref_read_url
```
**Method 3: Tavily (Search)**:
```
Tool: tavily_search
Query: "Rails [version] [class] [method] API documentation"
```
**Method 4: WebFetch (Fallback)**:
```
Tool: WebFetch
URL: [constructed URL from reference.md]
Prompt: "Extract method signature, parameters, and examples for [method]"
```
### Step 4: Response Formatting
```ruby
## ActiveRecord::QueryMethods#where (v7.1)
**Signature**: `where(**opts)`
**Parameters**:
- `opts` (Hash) - Conditions as key-value pairs
- `opts` (String) - Raw SQL conditions
- `opts` (Array) - SQL with placeholders
**Returns**: `ActiveRecord::Relation`
**Examples**:
User.where(name: 'Alice')
User.where("age > ?", 18)
User.where(age: 18..65)
**Source**: https://api.rubyonrails.org/v7.1/ActiveRecord/QueryMethods.html#method-i-where
```
## Reference Lookup
**Class/Method → API URL mapping** in `reference.md`:
```yaml
ActiveRecord::Base:
url_path: "ActiveRecord/Base.html"
common_methods:
- save: "method-i-save"
- update: "method-i-update"
- destroy: "method-i-destroy"
ActiveRecord::QueryMethods:
url_path: "ActiveRecord/QueryMethods.html"
common_methods:
- where: "method-i-where"
- joins: "method-i-joins"
- includes: "method-i-includes"
```
## Output Format
### Class Overview
```ruby
## ActiveRecord::Base (v7.1)
Active Record base class for all models.
**Inherits from**: Object
**Includes**: ActiveModel::Validations, ActiveRecord::Persistence
**Common Methods**:
- `.create` - Creates and saves record
- `#save` - Saves record to database
- `#update` - Updates attributes and saves
- `#destroy` - Deletes record from database
**Source**: https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html
```
### Method Details
```ruby
## ActiveRecord::Base#save (v7.1)
**Signature**: `save(options = {})`
**Parameters**:
- `validate` (Boolean, default: true) - Run validations before saving
- `context` (Symbol) - Validation context
- `touch` (Boolean, default: true) - Update timestamps
**Returns**:
- `true` if saved successfully
- `false` if validation failed
**Raises**:
- `ActiveRecord::RecordInvalid` if `save!` and validation fails
**Examples**:
```ruby
user.save # => true/false
user.save(validate: false) # skip validations
user.save! # raises on failure
```
**Source**: [full URL]
```
### Not Found Response
```markdown
## Class/Method Not Found: [class]#[method]
Searched in: Rails [version] API docs
Suggestions:
- Check spelling: "ActiveRecord::Base" (not "ActiveRecords::Base")
- Try class overview: @rails-api-lookup class="ActiveRecord::Base"
- Search guides instead: @rails-docs-search topic="active_record_basics"
Common classes:
- ActiveRecord::Base
- ActionController::Base
- ActiveSupport::Concern
```
## Implementation Details
**Tools used** (in order of preference):
1. **@rails-version-detector** - Get project Rails version
2. **Read** - Load `reference.md` API mappings
3. **context7_fetch** (primary) - Fetch API docs via Context7 MCP
4. **ref_search_documentation** (secondary) - Search Rails API docs via Ref MCP
5. **tavily_search** (tertiary) - Optimized search via Tavily MCP
6. **WebFetch** (fallback) - Fetch API docs if MCPs not available
7. **Grep** (optional) - Search for method names in cached docs
**Optional dependencies**:
- **context7-mcp**: Fastest API documentation
- **ref-tools-mcp**: Token-efficient API doc fetching
- **tavily-mcp**: Optimized search for LLMs
- If neither installed: Falls back to WebFetch (still works!)
**URL construction**:
```
Base: https://api.rubyonrails.org/
Versioned: https://api.rubyonrails.org/v7.1/
Class: https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html
Method: https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html#method-i-save
```
**Version handling**:
- Rails 8.x → `/v8.0/`
- Rails 7.1.x → `/v7.1/`
- Rails 7.0.x → `/v7.0/`
- Rails 6.1.x → `/v6.1/`
**Caching strategy**:
- Cache API documentation for session
- Re-fetch if version changes
- Cache key: `{class}:{method}:{version}`
**Prompt Caching (Opus 4.5 Optimized)**:
- Use 1-hour cache duration for extended thinking tasks
- API signatures rarely change - maximize cache reuse
- Cache prefix: Include Rails version + common class list in system prompt
- Reduces token costs significantly for repeated API lookups
## Error Handling
**Network failure**:
```markdown
⚠️ Failed to fetch API docs from api.rubyonrails.org
Fallback: Use Claude's knowledge (may be less accurate)
URL attempted: [URL]
```
**Invalid class name**:
```markdown
❌ Unknown class: "[class]"
Did you mean: [closest match from reference.md]?
Tip: Use full module path (e.g., "ActiveRecord::Base", not "Base")
```
**Method not found**:
```markdown
⚠️ Method "[method]" not found in [class]
Class exists, but method not documented or name incorrect.
Available methods in [class]:
- [method1]
- [method2]
[...from reference.md...]
```
## Integration
**Auto-invoked by**:
- All 7 Rails agents when they need precise API signatures
- @rails-model-specialist for ActiveRecord methods
- @rails-controller-specialist for ActionController methods
- @rails-view-specialist for ActionView helpers
**Complements**:
- @rails-docs-search (concepts vs API details)
- @rails-pattern-finder (API usage vs code patterns)
## Special Features
### Multiple methods lookup
```
@rails-api-lookup class="ActiveRecord::Base" method="save,update,destroy"
→ Returns all three method signatures
```
### Inheritance chain
```
@rails-api-lookup class="User" inherit=true
→ Shows methods from User, ApplicationRecord, ActiveRecord::Base
```
### Version comparison
```
@rails-api-lookup class="ActiveRecord::Base" method="save" compare="7.0,7.1"
→ Shows differences between versions
```
## Testing
**Test cases**:
1. class="ActiveRecord::Base" method="save" → Exact signature
2. class="UnknownClass" → Error with suggestions
3. Network down → Graceful fallback
4. Rails 8.0 method lookup → Uses v8.0 docs
5. Method with multiple signatures → Lists all variants
## Notes
- API docs fetched live (not stored in plugin)
- Reference mappings maintained in `reference.md`
- Version-appropriate URLs ensure API accuracy
- WebFetch tool handles HTML parsing
- This skill focuses on **API signatures**, use @rails-docs-search for **concepts**
- Method anchors use Rails convention: `#method-i-name` (instance), `#method-c-name` (class)

View File

@@ -0,0 +1,451 @@
# Rails API Reference Mappings
**Purpose**: Maps Rails classes/modules to API documentation URLs
**Version**: 1.0.0 (supports Rails 6.1 - 8.0)
---
## Active Record Classes
### Core Classes
```yaml
ActiveRecord::Base:
url_path: "ActiveRecord/Base.html"
description: "Base class for all models"
common_methods:
save: "method-i-save"
save!: "method-i-save-21"
update: "method-i-update"
update!: "method-i-update-21"
destroy: "method-i-destroy"
destroy!: "method-i-destroy-21"
reload: "method-i-reload"
persisted?: "method-i-persisted-3F"
new_record?: "method-i-new_record-3F"
ActiveRecord::Relation:
url_path: "ActiveRecord/Relation.html"
description: "Query result collection"
common_methods:
to_a: "method-i-to_a"
each: "method-i-each"
map: "method-i-map"
pluck: "method-i-pluck"
ids: "method-i-ids"
```
### Query Interface
```yaml
ActiveRecord::QueryMethods:
url_path: "ActiveRecord/QueryMethods.html"
description: "Query building methods"
common_methods:
where: "method-i-where"
not: "method-i-not"
order: "method-i-order"
limit: "method-i-limit"
offset: "method-i-offset"
joins: "method-i-joins"
left_joins: "method-i-left_joins"
includes: "method-i-includes"
eager_load: "method-i-eager_load"
preload: "method-i-preload"
references: "method-i-references"
group: "method-i-group"
having: "method-i-having"
distinct: "method-i-distinct"
select: "method-i-select"
reorder: "method-i-reorder"
reverse_order: "method-i-reverse_order"
```
### Associations
```yaml
ActiveRecord::Associations::ClassMethods:
url_path: "ActiveRecord/Associations/ClassMethods.html"
description: "Association declarations"
common_methods:
belongs_to: "method-i-belongs_to"
has_one: "method-i-has_one"
has_many: "method-i-has_many"
has_and_belongs_to_many: "method-i-has_and_belongs_to_many"
has_one_attached: "method-i-has_one_attached"
has_many_attached: "method-i-has_many_attached"
```
### Validations
```yaml
ActiveModel::Validations::ClassMethods:
url_path: "ActiveModel/Validations/ClassMethods.html"
description: "Validation declarations"
common_methods:
validates: "method-i-validates"
validates_each: "method-i-validates_each"
validates_with: "method-i-validates_with"
validate: "method-i-validate"
ActiveModel::Validations::HelperMethods:
url_path: "ActiveModel/Validations/HelperMethods.html"
description: "Built-in validators"
common_methods:
validates_presence_of: "method-i-validates_presence_of"
validates_absence_of: "method-i-validates_absence_of"
validates_length_of: "method-i-validates_length_of"
validates_size_of: "method-i-validates_size_of"
validates_numericality_of: "method-i-validates_numericality_of"
validates_inclusion_of: "method-i-validates_inclusion_of"
validates_exclusion_of: "method-i-validates_exclusion_of"
validates_format_of: "method-i-validates_format_of"
validates_uniqueness_of: "method-i-validates_uniqueness_of"
```
### Callbacks
```yaml
ActiveRecord::Callbacks:
url_path: "ActiveRecord/Callbacks.html"
description: "Model lifecycle callbacks"
common_methods:
after_initialize: "method-i-after_initialize"
after_find: "method-i-after_find"
before_validation: "method-i-before_validation"
after_validation: "method-i-after_validation"
before_save: "method-i-before_save"
around_save: "method-i-around_save"
after_save: "method-i-after_save"
before_create: "method-i-before_create"
around_create: "method-i-around_create"
after_create: "method-i-after_create"
before_update: "method-i-before_update"
around_update: "method-i-around_update"
after_update: "method-i-after_update"
before_destroy: "method-i-before_destroy"
around_destroy: "method-i-around_destroy"
after_destroy: "method-i-after_destroy"
after_commit: "method-i-after_commit"
after_rollback: "method-i-after_rollback"
```
### Migrations
```yaml
ActiveRecord::Migration:
url_path: "ActiveRecord/Migration.html"
description: "Database migration base class"
common_methods:
change: "method-i-change"
up: "method-i-up"
down: "method-i-down"
reversible: "method-i-reversible"
ActiveRecord::ConnectionAdapters::SchemaStatements:
url_path: "ActiveRecord/ConnectionAdapters/SchemaStatements.html"
description: "Schema manipulation methods"
common_methods:
create_table: "method-i-create_table"
drop_table: "method-i-drop_table"
rename_table: "method-i-rename_table"
add_column: "method-i-add_column"
remove_column: "method-i-remove_column"
rename_column: "method-i-rename_column"
change_column: "method-i-change_column"
add_index: "method-i-add_index"
remove_index: "method-i-remove_index"
add_foreign_key: "method-i-add_foreign_key"
remove_foreign_key: "method-i-remove_foreign_key"
add_reference: "method-i-add_reference"
remove_reference: "method-i-remove_reference"
```
---
## Action Controller Classes
### Core Classes
```yaml
ActionController::Base:
url_path: "ActionController/Base.html"
description: "Base controller class"
common_methods:
render: "method-i-render"
redirect_to: "method-i-redirect_to"
head: "method-i-head"
ActionController::API:
url_path: "ActionController/API.html"
description: "API-only controller base"
version_support: "5.0+"
ActionController::Metal:
url_path: "ActionController/Metal.html"
description: "Minimal controller implementation"
```
### Controller Features
```yaml
ActionController::StrongParameters:
url_path: "ActionController/StrongParameters.html"
description: "Parameter filtering"
common_methods:
params: "method-i-params"
permit: "method-i-permit"
require: "method-i-require"
ActionController::Helpers:
url_path: "ActionController/Helpers.html"
description: "Helper method declarations"
common_methods:
helper_method: "method-i-helper_method"
ActionController::Cookies:
url_path: "ActionController/Cookies.html"
description: "Cookie handling"
common_methods:
cookies: "method-i-cookies"
ActionController::Flash:
url_path: "ActionController/Flash.html"
description: "Flash message handling"
common_methods:
flash: "method-i-flash"
```
---
## Action View Classes
### Core Classes
```yaml
ActionView::Base:
url_path: "ActionView/Base.html"
description: "View rendering base class"
ActionView::Helpers:
url_path: "ActionView/Helpers.html"
description: "View helper modules"
```
### View Helpers
```yaml
ActionView::Helpers::FormHelper:
url_path: "ActionView/Helpers/FormHelper.html"
description: "Form building helpers"
common_methods:
form_with: "method-i-form_with"
form_for: "method-i-form_for"
text_field: "method-i-text_field"
text_area: "method-i-text_area"
select: "method-i-select"
check_box: "method-i-check_box"
radio_button: "method-i-radio_button"
ActionView::Helpers::UrlHelper:
url_path: "ActionView/Helpers/UrlHelper.html"
description: "URL generation helpers"
common_methods:
link_to: "method-i-link_to"
button_to: "method-i-button_to"
url_for: "method-i-url_for"
ActionView::Helpers::TagHelper:
url_path: "ActionView/Helpers/TagHelper.html"
description: "HTML tag helpers"
common_methods:
content_tag: "method-i-content_tag"
tag: "method-i-tag"
ActionView::Helpers::AssetTagHelper:
url_path: "ActionView/Helpers/AssetTagHelper.html"
description: "Asset inclusion helpers"
common_methods:
javascript_include_tag: "method-i-javascript_include_tag"
stylesheet_link_tag: "method-i-stylesheet_link_tag"
image_tag: "method-i-image_tag"
```
---
## Active Support Classes
### Core Extensions
```yaml
ActiveSupport::Concern:
url_path: "ActiveSupport/Concern.html"
description: "Module mixin pattern"
common_methods:
included: "method-i-included"
class_methods: "method-i-class_methods"
ActiveSupport::Callbacks:
url_path: "ActiveSupport/Callbacks.html"
description: "Callback framework"
common_methods:
define_callbacks: "method-i-define_callbacks"
set_callback: "method-i-set_callback"
skip_callback: "method-i-skip_callback"
run_callbacks: "method-i-run_callbacks"
```
### Time & Date
```yaml
ActiveSupport::TimeWithZone:
url_path: "ActiveSupport/TimeWithZone.html"
description: "Timezone-aware time"
common_methods:
in_time_zone: "method-i-in_time_zone"
utc: "method-i-utc"
local: "method-i-local"
ActiveSupport::Duration:
url_path: "ActiveSupport/Duration.html"
description: "Time duration"
common_methods:
ago: "method-i-ago"
since: "method-i-since"
from_now: "method-i-from_now"
```
---
## Action Mailer Classes
```yaml
ActionMailer::Base:
url_path: "ActionMailer/Base.html"
description: "Mailer base class"
common_methods:
mail: "method-i-mail"
deliver_now: "method-i-deliver_now"
deliver_later: "method-i-deliver_later"
```
---
## Action Cable Classes
```yaml
ActionCable::Channel::Base:
url_path: "ActionCable/Channel/Base.html"
description: "Cable channel base class"
common_methods:
stream_from: "method-i-stream_from"
stream_for: "method-i-stream_for"
transmit: "method-i-transmit"
ActionCable::Connection::Base:
url_path: "ActionCable/Connection/Base.html"
description: "Cable connection base class"
```
---
## Active Job Classes
```yaml
ActiveJob::Base:
url_path: "ActiveJob/Base.html"
description: "Background job base class"
common_methods:
perform_later: "method-i-perform_later"
perform_now: "method-i-perform_now"
set: "method-i-set"
```
---
## Active Storage Classes
```yaml
ActiveStorage::Attached::One:
url_path: "ActiveStorage/Attached/One.html"
description: "Single file attachment"
version_support: "5.2+"
common_methods:
attach: "method-i-attach"
attached?: "method-i-attached-3F"
purge: "method-i-purge"
ActiveStorage::Attached::Many:
url_path: "ActiveStorage/Attached/Many.html"
description: "Multiple file attachments"
version_support: "5.2+"
common_methods:
attach: "method-i-attach"
attached?: "method-i-attached-3F"
purge: "method-i-purge"
```
---
## URL Construction
**Pattern**: `https://api.rubyonrails.org/v{MAJOR.MINOR}/{url_path}#{method_anchor}`
**Examples**:
**Class page**:
```
https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html
```
**Instance method**:
```
https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html#method-i-save
```
**Class method**:
```
https://api.rubyonrails.org/v7.1/ActiveRecord/Base.html#method-c-create
```
**Special characters in anchor**:
- `?``-3F`
- `!``-21`
- `=``-3D`
**Examples**:
- `persisted?``#method-i-persisted-3F`
- `save!``#method-i-save-21`
- `==``#method-i--3D-3D`
---
## Usage in rails-api-lookup
```ruby
# Pseudocode for API lookup
class_name = "ActiveRecord::Base"
method_name = "save"
mapping = reference[class_name]
version = detect_rails_version() # e.g., "7.1"
anchor = mapping.common_methods[method_name] # e.g., "method-i-save"
url = "https://api.rubyonrails.org/v#{version}/#{mapping.url_path}##{anchor}"
content = WebFetch(url, prompt: "Extract method signature and documentation")
```
---
## Maintenance
**Update frequency**: Quarterly or when new Rails version released
**Adding new APIs**:
1. Check official Rails API docs index
2. Add mapping with url_path and common_methods
3. Test URL accessibility
4. Update this file
**Version-specific APIs**:
- Mark with `version_support`
- Skill should gracefully handle unavailable APIs for older Rails versions

View File

@@ -0,0 +1,212 @@
---
name: rails-conventions
description: Automatically enforces Rails naming conventions, MVC separation, and RESTful patterns
auto_invoke: true
trigger_on: [file_create, file_modify]
file_patterns: ["*.rb", "*.erb", "config/routes.rb"]
tags: [rails, conventions, patterns, rest, mvc]
priority: 1
version: 2.0
---
# Rails Conventions Skill
Auto-validates and enforces Rails conventions across all code changes.
## What This Skill Does
**Automatic Enforcement:**
- Model naming: PascalCase, singular (User, not Users)
- Controller naming: PascalCase, plural, ends with Controller (UsersController)
- File paths: snake_case matching class names (users_controller.rb)
- RESTful routes: Standard 7 actions (index, show, new, create, edit, update, destroy)
- MVC separation: No business logic in views, no SQL in controllers
**When It Activates:**
- Every time a .rb file is created or modified
- When routes.rb is changed
- When view files are created
**What It Checks:**
1. **Naming Conventions**
- Class names follow Rails conventions
- File names match class names (snake_case ↔ PascalCase)
- Variable names descriptive and snake_case
- Constants in SCREAMING_SNAKE_CASE
2. **MVC Separation**
- Controllers: Thin, delegate to models/services
- Models: Business logic, no rendering
- Views: Presentation only, minimal logic
3. **RESTful Patterns**
- Routes follow REST conventions
- Custom actions documented and justified
- Nested routes limited to 2 levels
4. **Rails Idioms**
- Use scopes over class methods for queries
- Use concerns for shared behavior
- Use before_action for controller filters
## Validation Examples
### Example 1: Model Naming
**Input:**
```ruby
# app/models/users.rb
class Users < ApplicationRecord
end
```
**Skill Output:**
```
❌ Convention Violation: Model class name should be singular
Expected: User
Found: Users
Fix:
- Rename class to "User"
- Rename file to "user.rb"
```
### Example 2: Controller Naming
**Input:**
```ruby
# app/controllers/user.rb
class User < ApplicationController
end
```
**Skill Output:**
```
❌ Convention Violation: Controller class name should be plural and end with 'Controller'
Expected: UsersController
Found: User
❌ File Naming: Controller file should match class name
Expected: users_controller.rb
Found: user.rb
```
### Example 3: MVC Separation
**Input:**
```ruby
# app/controllers/posts_controller.rb
def index
@posts = Post.where("created_at > ?", 1.week.ago)
.joins(:author)
.order(created_at: :desc)
end
```
**Skill Output:**
```
⚠️ MVC Separation: Complex query logic in controller
Recommendation: Extract to model scope
Suggested refactoring:
# app/models/post.rb
scope :recent, -> { where("created_at > ?", 1.week.ago) }
scope :with_author, -> { joins(:author) }
scope :newest_first, -> { order(created_at: :desc) }
# app/controllers/posts_controller.rb
def index
@posts = Post.recent.with_author.newest_first
end
```
### Example 4: RESTful Routes
**Input:**
```ruby
# config/routes.rb
resources :posts do
get 'activate'
get 'deactivate'
get 'toggle_status'
get 'publish'
end
```
**Skill Output:**
```
⚠️ RESTful Pattern: Multiple custom actions detected
Current: 4 custom actions (activate, deactivate, toggle_status, publish)
Recommendation: Consider consolidating to single update action
Suggested refactoring:
resources :posts do
patch 'status', to: 'posts#update_status'
end
# Controller:
def update_status
action = params[:action_type] # 'activate', 'deactivate', 'publish'
@post.update!(status: action)
end
```
## Configuration
Skill can be configured via `.rails-conventions.yml`:
```yaml
# .rails-conventions.yml
naming:
enforce_singular_models: true
enforce_plural_controllers: true
mvc_separation:
max_controller_lines: 100
warn_on_complex_queries: true
restful:
max_custom_actions: 2
max_nesting_depth: 2
```
## Auto-Fix Capability
This skill can automatically fix simple violations:
**Auto-fixable:**
- File renaming to match class names
- Converting class method queries to scopes
- Extracting inline queries to model scopes
**Manual fix required:**
- Class name changes (impacts migrations, associations)
- MVC layer violations (requires architectural decisions)
- Custom route consolidation (business logic dependent)
## Integration with Agents
This skill enhances all agents:
- **@rails-model-specialist**: Validates model naming and scope usage
- **@rails-controller-specialist**: Enforces RESTful patterns and thin controllers
- **@rails-view-specialist**: Validates view logic separation
- **@rails-architect**: Provides convention checks during coordination
## Severity Levels
- **❌ Error**: Blocks commit (via pre-commit hook) - naming violations, missing strong params
- **⚠️ Warning**: Suggests improvement - complex queries, non-RESTful routes
- ** Info**: Best practice suggestion - use of concerns, scope opportunities
## Performance
- Activates on: File save
- Execution time: < 100ms per file
- No network calls
- Works offline
---
**This skill runs automatically - no invocation needed. It keeps your Rails code conventional and maintainable.**

View File

@@ -0,0 +1,291 @@
# Rails Docs Search
---
name: rails-docs-search
description: Searches Rails Guides for conceptual documentation using Ref (primary) or WebFetch (fallback) with reference mappings
version: 1.2.0
author: Rails Workflow Team
tags: [rails, documentation, guides, search, ref]
priority: 3
---
## Purpose
Fetches conceptual documentation from official Rails Guides based on topics. Returns relevant guide sections with version-appropriate URLs.
**Replaces**: `mcp__rails__search_docs` MCP tool
## Usage
**Auto-invoked** when agents need Rails concepts:
```
Agent: "How do I implement Action Cable subscriptions?"
*invokes rails-docs-search topic="action_cable"*
```
**Manual invocation**:
```
@rails-docs-search topic="active_record_associations"
@rails-docs-search topic="routing"
```
## Supported Topics
See `reference.md` for complete topic list. Common topics:
### Core Concepts
- `getting_started` - Rails basics and first app
- `active_record_basics` - ORM fundamentals
- `routing` - URL patterns and routes
- `controllers` - Request/response handling
- `views` - Templates and rendering
### Advanced Features
- `active_record_associations` - Relationships (has_many, belongs_to)
- `active_record_validations` - Data validation
- `active_record_callbacks` - Lifecycle hooks
- `action_mailer` - Email sending
- `action_cable` - WebSockets
### Testing & Security
- `testing` - Rails testing guide
- `security` - Security best practices
- `debugging` - Debugging techniques
### Deployment & Configuration
- `configuring` - Application configuration
- `asset_pipeline` - Asset management
- `caching` - Performance caching
## Search Process
### Step 1: Version Detection
```
Invokes: @rails-version-detector
Result: Rails 7.1.3
Maps to: https://guides.rubyonrails.org/v7.1/
```
### Step 2: Topic Mapping
```
Input: topic="active_record_associations"
Lookup: reference.md → "association_basics.html"
URL: https://guides.rubyonrails.org/v7.1/association_basics.html
```
### Step 3: Content Fetch
**Method 1: Context7 (Fastest)**:
```
Tool: context7_fetch
Query: "Rails [version] [topic]"
```
**Method 2: Ref (Token-Efficient)**:
```
Tool: ref_search_documentation
Query: "Rails [version] [topic] documentation"
Then: ref_read_url
```
**Method 3: Tavily (Search)**:
```
Tool: tavily_search
Query: "Rails [version] [topic] guide"
```
**Method 4: WebFetch (Fallback)**:
```
Tool: WebFetch
URL: [constructed URL from reference.md]
Prompt: "Extract sections about [specific query]"
Note: WebFetch has a built-in 15-minute cache for faster responses
when repeatedly accessing the same URL within a session.
```
### Step 4: Response Formatting
```markdown
## Rails Guide: Active Record Associations (v7.1)
### belongs_to
A `belongs_to` association sets up a one-to-one connection...
### has_many
A `has_many` association indicates a one-to-many connection...
Source: https://guides.rubyonrails.org/v7.1/association_basics.html
```
## Reference Lookup
**Topic → Guide URL mapping** in `reference.md`:
```yaml
active_record_associations:
title: "Active Record Associations"
url_path: "association_basics.html"
version_support: "all"
keywords: [has_many, belongs_to, has_one, through]
routing:
title: "Rails Routing"
url_path: "routing.html"
version_support: "all"
keywords: [routes, resources, namespace]
```
## Output Format
### Success Response
```markdown
## Rails Guide: [Topic Title] (v[X.Y])
[Fetched content from guide...]
### Key Points
- [Summary point 1]
- [Summary point 2]
**Source**: [full URL]
**Version**: [Rails version]
```
### Not Found Response
```markdown
## Topic Not Found: [topic]
Available topics:
- getting_started
- active_record_basics
- routing
[...more topics...]
Try: @rails-docs-search topic="[one of above]"
```
### Version Mismatch Warning
```markdown
## Rails Guide: [Topic] (v7.1)
⚠️ **Note**: Guide is for Rails 7.1, but project uses Rails 6.1.
Some features may not be available in your version.
[Content...]
```
## Implementation Details
**Tools used** (in order of preference):
1. **@rails-version-detector** - Get project Rails version
2. **Read** - Load `reference.md` topic mappings
3. **context7_fetch** (primary) - Fetch curated docs via Context7 MCP
4. **ref_search_documentation** (secondary) - Search Rails docs via Ref MCP
5. **tavily_search** (tertiary) - Optimized search via Tavily MCP
6. **WebFetch** (fallback) - Fetch guide content if MCPs not available
7. **Grep** (optional) - Search local cached guides if available
**Optional dependencies**:
- **context7-mcp**: Fastest, curated documentation
- **ref-tools-mcp**: Token-efficient documentation search
- **tavily-mcp**: Optimized search for LLMs
- If neither installed: Falls back to WebFetch (still works!)
**URL construction**:
```
Base: https://guides.rubyonrails.org/
Versioned: https://guides.rubyonrails.org/v7.1/
Guide: https://guides.rubyonrails.org/v7.1/routing.html
```
**Version handling**:
- Rails 8.x → `/v8.0/` (or latest if 8.0 not published)
- Rails 7.1.x → `/v7.1/`
- Rails 7.0.x → `/v7.0/`
- Rails 6.1.x → `/v6.1/`
**Caching strategy**:
- Cache fetched guide content for session
- Re-fetch if version changes
- Cache key: `{topic}:{version}`
**Prompt Caching (Opus 4.5 Optimized)**:
- Use 1-hour cache duration for extended thinking tasks
- Documentation content is stable - leverage longer cache windows
- Cache prefix: Include Rails version in system prompt for cache reuse
- Reduces token costs significantly for repeated lookups
## Error Handling
**Network failure**:
```markdown
⚠️ Failed to fetch guide from rubyonrails.org
Fallback: Check local knowledge or ask user for clarification.
URL attempted: [URL]
```
**Invalid topic**:
```markdown
❌ Unknown topic: "[topic]"
Did you mean: [closest match from reference.md]?
See available topics: @rails-docs-search list
```
**Version not supported**:
```markdown
⚠️ Rails [version] guides not available.
Using closest version: [fallback version]
Some information may differ from your Rails version.
```
## Integration
**Auto-invoked by**:
- All 7 Rails agents when they need conceptual information
- @rails-architect for architecture decisions
- User questions about "How do I..." or "What is..."
**Complements**:
- @rails-api-lookup (this skill = concepts, that skill = API details)
- @rails-pattern-finder (this skill = theory, that skill = code examples)
## Special Features
### Multi-topic search
```
@rails-docs-search topic="routing,controllers"
→ Fetches both guides and combines relevant sections
```
### Keyword search within topic
```
@rails-docs-search topic="active_record_associations" keyword="polymorphic"
→ Focuses on polymorphic association sections only
```
### List available topics
```
@rails-docs-search list
→ Returns all available topics from reference.md
```
## Testing
**Test cases**:
1. Topic="routing" + Rails 7.1 → Fetches v7.1 routing guide
2. Topic="unknown" → Returns error with suggestions
3. Network down → Graceful fallback
4. Rails 8.0 (future) → Uses latest available version
5. Multiple topics → Combines results
## Notes
- Guides content fetched live (not stored in plugin)
- Reference mappings maintained in `reference.md`
- Version-appropriate URLs ensure accuracy
- WebFetch tool handles HTML → Markdown conversion
- This skill focuses on **concepts**, use @rails-api-lookup for **API signatures**

View File

@@ -0,0 +1,322 @@
# Rails Guides Reference Mappings
**Purpose**: Maps topic names to official Rails Guides URLs
**Version**: 1.0.0 (supports Rails 6.1 - 8.0)
---
## Topic Mappings
### Getting Started
```yaml
getting_started:
title: "Getting Started with Rails"
url_path: "getting_started.html"
version_support: "all"
keywords: [tutorial, first app, setup, install]
initialization:
title: "The Rails Initialization Process"
url_path: "initialization.html"
version_support: "all"
keywords: [boot, startup, initialization]
```
### Active Record
```yaml
active_record_basics:
title: "Active Record Basics"
url_path: "active_record_basics.html"
version_support: "all"
keywords: [orm, models, database, CRUD]
active_record_migrations:
title: "Active Record Migrations"
url_path: "active_record_migrations.html"
version_support: "all"
keywords: [migrations, schema, database changes]
active_record_validations:
title: "Active Record Validations"
url_path: "active_record_validations.html"
version_support: "all"
keywords: [validation, validates, presence, format]
active_record_callbacks:
title: "Active Record Callbacks"
url_path: "active_record_callbacks.html"
version_support: "all"
keywords: [callbacks, before_save, after_create, lifecycle]
active_record_associations:
title: "Active Record Associations"
url_path: "association_basics.html"
version_support: "all"
keywords: [has_many, belongs_to, has_one, through, polymorphic]
active_record_querying:
title: "Active Record Query Interface"
url_path: "active_record_querying.html"
version_support: "all"
keywords: [query, where, joins, includes, eager loading]
```
### Action Controller
```yaml
action_controller_overview:
title: "Action Controller Overview"
url_path: "action_controller_overview.html"
version_support: "all"
keywords: [controllers, requests, responses, filters]
routing:
title: "Rails Routing from the Outside In"
url_path: "routing.html"
version_support: "all"
keywords: [routes, resources, namespace, scope, member, collection]
```
### Action View
```yaml
action_view_overview:
title: "Action View Overview"
url_path: "action_view_overview.html"
version_support: "all"
keywords: [views, templates, rendering, partials]
layouts_and_rendering:
title: "Layouts and Rendering in Rails"
url_path: "layouts_and_rendering.html"
version_support: "all"
keywords: [layouts, render, yield, content_for]
form_helpers:
title: "Action View Form Helpers"
url_path: "form_helpers.html"
version_support: "all"
keywords: [forms, form_with, form_for, input fields]
```
### Action Mailer
```yaml
action_mailer_basics:
title: "Action Mailer Basics"
url_path: "action_mailer_basics.html"
version_support: "all"
keywords: [email, mailer, deliver, smtp]
```
### Action Cable
```yaml
action_cable_overview:
title: "Action Cable Overview"
url_path: "action_cable_overview.html"
version_support: "all"
keywords: [websockets, channels, subscriptions, broadcasting]
```
### Active Job
```yaml
active_job_basics:
title: "Active Job Basics"
url_path: "active_job_basics.html"
version_support: "all"
keywords: [jobs, background, queues, sidekiq, delayed_job]
```
### Active Storage
```yaml
active_storage_overview:
title: "Active Storage Overview"
url_path: "active_storage_overview.html"
version_support: "6.0+"
keywords: [uploads, files, attachments, S3, cloud storage]
```
### Testing
```yaml
testing:
title: "Testing Rails Applications"
url_path: "testing.html"
version_support: "all"
keywords: [tests, minitest, rspec, fixtures, factories]
```
### Security
```yaml
security:
title: "Securing Rails Applications"
url_path: "security.html"
version_support: "all"
keywords: [security, CSRF, XSS, SQL injection, authentication]
```
### Debugging
```yaml
debugging_rails_applications:
title: "Debugging Rails Applications"
url_path: "debugging_rails_applications.html"
version_support: "all"
keywords: [debug, byebug, pry, logs, debugging]
```
### Configuration
```yaml
configuring:
title: "Configuring Rails Applications"
url_path: "configuring.html"
version_support: "all"
keywords: [config, environment, settings, credentials]
rails_application_templates:
title: "Rails Application Templates"
url_path: "rails_application_templates.html"
version_support: "all"
keywords: [templates, generators, app templates]
```
### Performance
```yaml
caching_with_rails:
title: "Caching with Rails"
url_path: "caching_with_rails.html"
version_support: "all"
keywords: [cache, caching, fragment cache, low-level cache]
asset_pipeline:
title: "The Asset Pipeline"
url_path: "asset_pipeline.html"
version_support: "all"
keywords: [assets, sprockets, javascript, css, images]
```
### Internationalization
```yaml
i18n:
title: "Rails Internationalization (I18n) API"
url_path: "i18n.html"
version_support: "all"
keywords: [i18n, translations, locales, internationalization]
```
### Action Mailbox
```yaml
action_mailbox_basics:
title: "Action Mailbox Basics"
url_path: "action_mailbox_basics.html"
version_support: "6.0+"
keywords: [incoming email, mailbox, inbound email]
```
### Action Text
```yaml
action_text_overview:
title: "Action Text Overview"
url_path: "action_text_overview.html"
version_support: "6.0+"
keywords: [rich text, trix, wysiwyg, text editor]
```
### Rails 7+ Specific
```yaml
autoloading_and_reloading_constants:
title: "Autoloading and Reloading Constants"
url_path: "autoloading_and_reloading_constants.html"
version_support: "all"
keywords: [autoload, zeitwerk, eager loading]
engines:
title: "Getting Started with Engines"
url_path: "engines.html"
version_support: "all"
keywords: [engines, plugins, mountable]
api_app:
title: "Using Rails for API-only Applications"
url_path: "api_app.html"
version_support: "5.0+"
keywords: [api, json, api-only, rest]
```
### Rails 8+ Specific
```yaml
solid_cache:
title: "Solid Cache"
url_path: "solid_cache.html"
version_support: "8.0+"
keywords: [solid cache, caching, database cache]
solid_queue:
title: "Solid Queue"
url_path: "solid_queue.html"
version_support: "8.0+"
keywords: [solid queue, jobs, background jobs]
solid_cable:
title: "Solid Cable"
url_path: "solid_cable.html"
version_support: "8.0+"
keywords: [solid cable, websockets, action cable]
```
---
## Version Support Legend
- `all` - Available in Rails 3.0+
- `5.0+` - Available from Rails 5.0 onwards
- `6.0+` - Available from Rails 6.0 onwards
- `7.0+` - Available from Rails 7.0 onwards
- `8.0+` - Available from Rails 8.0 onwards
## URL Construction
**Pattern**: `https://guides.rubyonrails.org/v{MAJOR.MINOR}/{url_path}`
**Examples**:
- Rails 7.1: `https://guides.rubyonrails.org/v7.1/routing.html`
- Rails 8.0: `https://guides.rubyonrails.org/v8.0/routing.html`
- Latest: `https://guides.rubyonrails.org/routing.html` (edge)
## Usage in rails-docs-search
```ruby
# Pseudocode for topic lookup
topic = "active_record_associations"
mapping = reference[topic]
version = detect_rails_version() # e.g., "7.1"
url = "https://guides.rubyonrails.org/v#{version}/#{mapping.url_path}"
content = WebFetch(url, prompt: "Extract information about associations")
```
## Maintenance
**Update frequency**: Quarterly or when new Rails version released
**Adding new topics**:
1. Check official Rails Guides index
2. Add mapping with all fields
3. Test URL accessibility
4. Update this file
**Version-specific topics**:
- Mark with `version_support`
- Skill should gracefully handle unavailable guides for older Rails versions

View File

@@ -0,0 +1,374 @@
# Rails Pattern Finder
---
name: rails-pattern-finder
description: Finds Rails code patterns and best practices using Ref (primary), Grep, and reference patterns with WebFetch fallback
version: 1.2.0
author: Rails Workflow Team
tags: [rails, patterns, best-practices, code-examples, ref]
priority: 3
---
## Purpose
Searches the current Rails codebase for existing patterns and provides best-practice code examples. Helps agents write code consistent with project conventions.
**Replaces**: Manual codebase exploration and pattern recognition
## Usage
**Auto-invoked** when agents need code examples:
```
Agent: "How is authentication implemented in this project?"
*invokes rails-pattern-finder pattern="authentication"*
```
**Manual invocation**:
```
@rails-pattern-finder pattern="service_objects"
@rails-pattern-finder pattern="api_serialization"
@rails-pattern-finder pattern="background_jobs"
```
## Supported Patterns
See `reference.md` for complete pattern list. Common patterns:
### Architectural Patterns
- `service_objects` - Service layer implementation
- `form_objects` - Form object pattern
- `query_objects` - Complex query encapsulation
- `decorators` - Decorator/presenter pattern
- `policies` - Authorization policies (Pundit)
### API Patterns
- `api_versioning` - API version management
- `api_serialization` - JSON response formatting
- `api_authentication` - Token/JWT authentication
- `api_error_handling` - Error response patterns
### Database Patterns
- `scopes` - Named scope usage
- `concerns` - Model concern organization
- `polymorphic_associations` - Polymorphic pattern
- `sti` - Single Table Inheritance
- `database_views` - Database view usage
### Testing Patterns
- `factory_usage` - FactoryBot patterns
- `request_specs` - API request testing
- `system_specs` - System/feature testing
- `shared_examples` - RSpec shared examples
## Search Process
### Step 1: Pattern Lookup
```
Input: pattern="service_objects"
Lookup: reference.md → search_paths, file_patterns, code_patterns
```
### Step 2: Codebase Search
```
Tool: Grep
Pattern: "class.*Service$"
Glob: "app/services/**/*.rb"
Output: List of matching files
```
### Step 3: Example Extraction
```
Tool: Read
Files: [top 3 matches by relevance]
Extract: Class structure, method signatures, usage patterns
```
### Step 4: Response Formatting
```ruby
## Pattern: Service Objects
### Found in Project (3 examples):
**1. UserRegistrationService** (app/services/user_registration_service.rb)
```ruby
class UserRegistrationService
def initialize(params)
@params = params
end
def call
user = User.create!(@params)
send_welcome_email(user)
user
end
private
def send_welcome_email(user)
UserMailer.welcome(user).deliver_later
end
end
```
**2. PaymentProcessingService** (app/services/payment_processing_service.rb)
[Example code...]
### Best Practice from Rails Community:
[Fetch from reference.md or WebSearch]
**Source**: Project codebase + Rails best practices
```
## Reference Lookup
**Pattern → Search Strategy mapping** in `reference.md`:
```yaml
service_objects:
title: "Service Objects"
search_paths: ["app/services/**/*.rb"]
file_patterns: ["*_service.rb"]
code_patterns:
- "class \\w+Service"
- "def call"
best_practice_url: "https://example.com/rails-service-objects"
keywords: [service, business logic, call method]
api_serialization:
title: "API Serialization"
search_paths: ["app/serializers/**/*.rb", "app/blueprints/**/*.rb"]
file_patterns: ["*_serializer.rb", "*_blueprint.rb"]
code_patterns:
- "class \\w+Serializer"
- "ActiveModel::Serializer"
- "Blueprinter::Base"
keywords: [json, serializer, blueprint, jbuilder]
```
## Output Format
### Pattern Found in Project
```markdown
## Pattern: [Pattern Name]
### Found in Project ([N] examples):
**File**: [path/to/file.rb]
**Purpose**: [What this implementation does]
```ruby
[Code example from project]
```
**Key characteristics**:
- [Feature 1]
- [Feature 2]
**Usage in project**:
[How this pattern is used - grep for usage examples]
```
### Pattern Not Found
```markdown
## Pattern: [Pattern Name] - Not found in project
**Searched**:
- app/services/**/*.rb
- app/lib/**/*.rb
**Best Practice Implementation**:
```ruby
[Example from reference.md or external source]
```
**To implement in this project**:
1. Create: app/services/[name]_service.rb
2. Follow structure above
3. Test in: spec/services/[name]_service_spec.rb
**Similar patterns in project**:
- [Alternative pattern found]
```
### Multiple Variants Found
```markdown
## Pattern: [Pattern Name] - Multiple variants found
This project uses [N] different approaches:
### Variant 1: [Approach Name] ([N] files)
[Example code...]
### Variant 2: [Approach Name] ([N] files)
[Example code...]
**Recommendation**: [Which variant to use for consistency]
```
## Implementation Details
**Tools used** (in order of preference):
1. **Read** - Load `reference.md` pattern definitions
2. **context7_fetch** (primary) - Fetch curated patterns via Context7 MCP
3. **ref_search_documentation** (secondary) - Search Rails pattern docs via Ref MCP
4. **ref_read_url** (secondary) - Fetch specific pattern guides via Ref MCP
5. **tavily_search** (tertiary) - Optimized search via Tavily MCP
6. **Grep** - Search codebase for pattern matches
7. **Read** - Extract code examples from matching files
8. **WebFetch** (fallback) - Fetch pattern docs if MCPs not available
9. **WebSearch** (optional) - Fetch external best practices
10. **Glob** - Find files matching pattern
**Optional dependencies**:
- **context7-mcp**: Fastest pattern documentation
- **ref-tools-mcp**: Token-efficient pattern search
- **tavily-mcp**: Optimized search for LLMs
- If neither installed: Falls back to WebFetch and local searches (still works!)
**Search strategies**:
**File-based search**:
```bash
# Find files matching naming convention
glob: "app/services/*_service.rb"
```
**Content-based search**:
```bash
# Find class definitions
grep: "class \\w+Service$"
path: "app/services"
```
**Usage search**:
```bash
# Find where pattern is used
grep: "UserRegistrationService\\.new"
path: "app/controllers"
```
**Relevance ranking**:
1. Most recently modified files (likely current patterns)
2. Files with most references (commonly used)
3. Files with comprehensive examples (best for learning)
## Error Handling
**Pattern not found**:
```markdown
⚠️ Pattern "[pattern]" not found in project
**Searched**:
- [paths searched]
**Options**:
1. View best practice example (from reference.md)
2. Search for similar pattern: [suggestions]
3. Implement from scratch using best practices
```
**Invalid pattern name**:
```markdown
❌ Unknown pattern: "[pattern]"
Available patterns:
- service_objects
- form_objects
- query_objects
[...from reference.md...]
Try: @rails-pattern-finder pattern="[one of above]"
```
**Ambiguous results**:
```markdown
⚠️ Multiple pattern variants found for "[pattern]"
Please review all variants and choose one for consistency.
[List all variants found...]
```
## Integration
**Auto-invoked by**:
- All 7 Rails agents when generating new code
- @rails-architect for architectural decisions
- Agents adapting to project conventions
**Workflow**:
1. Agent needs to generate code (e.g., new service)
2. Invokes @rails-pattern-finder pattern="service_objects"
3. Receives project-specific examples
4. Generates code matching project style
## Special Features
### Pattern comparison
```
@rails-pattern-finder pattern="service_objects" compare_with="form_objects"
→ Shows differences and use cases for each
```
### Project convention detection
```
@rails-pattern-finder detect_conventions
→ Analyzes codebase and reports common patterns
```
### Anti-pattern detection
```
@rails-pattern-finder anti_patterns
→ Searches for common Rails anti-patterns in codebase
```
### Test pattern search
```
@rails-pattern-finder pattern="service_objects" include_tests=true
→ Shows both implementation and test examples
```
## Pattern Categories
### Creation Patterns
- `service_objects` - Business logic encapsulation
- `form_objects` - Form handling
- `query_objects` - Query encapsulation
- `builder_pattern` - Object construction
### Structural Patterns
- `concerns` - Module organization
- `decorators` - Presentation logic
- `adapters` - External API integration
- `repositories` - Data access layer
### Behavioral Patterns
- `observers` - Event handling
- `state_machines` - State management
- `strategies` - Algorithm selection
- `commands` - Action encapsulation
### Rails-Specific
- `sti` - Single Table Inheritance
- `polymorphic_associations` - Flexible relationships
- `custom_validators` - Validation logic
- `background_jobs` - Async processing
## Testing
**Test cases**:
1. Pattern exists in project → Returns project examples
2. Pattern doesn't exist → Returns best practice example
3. Multiple variants → Lists all with recommendation
4. Invalid pattern → Error with suggestions
5. Empty project → All patterns return best practices
## Notes
- This skill searches **current project codebase**, not external repos
- Pattern definitions in `reference.md` include search strategies
- Results prioritize recent, commonly-used code
- Helps maintain consistency across large teams
- Adapts to project-specific conventions automatically
- Use with @rails-docs-search (theory) and @rails-api-lookup (APIs)

View File

@@ -0,0 +1,861 @@
# Rails Pattern Reference
**Purpose**: Defines search strategies for common Rails patterns
**Version**: 1.0.0
---
## Service Layer Patterns
### Service Objects
```yaml
service_objects:
title: "Service Objects"
description: "Encapsulate business logic outside of models/controllers"
search_paths:
- "app/services/**/*.rb"
- "app/lib/services/**/*.rb"
file_patterns:
- "*_service.rb"
code_patterns:
- "class \\w+Service"
- "def call"
- "def initialize"
usage_patterns:
- "\\w+Service\\.new"
- "\\w+Service\\.call"
test_paths:
- "spec/services/**/*_spec.rb"
- "test/services/**/*_test.rb"
keywords: [service, business logic, use case, interactor]
best_practice: |
class UserRegistrationService
def initialize(params)
@params = params
end
def call
ActiveRecord::Base.transaction do
user = create_user
send_welcome_email(user)
notify_admin(user)
user
end
end
private
def create_user
User.create!(@params)
end
def send_welcome_email(user)
UserMailer.welcome(user).deliver_later
end
def notify_admin(user)
AdminMailer.new_user(user).deliver_later
end
end
```
### Form Objects
```yaml
form_objects:
title: "Form Objects"
description: "Handle complex form logic with multiple models"
search_paths:
- "app/forms/**/*.rb"
- "app/lib/forms/**/*.rb"
file_patterns:
- "*_form.rb"
code_patterns:
- "class \\w+Form"
- "include ActiveModel::Model"
- "attr_accessor"
- "validate"
keywords: [form, multi-model, virtual attributes]
best_practice: |
class RegistrationForm
include ActiveModel::Model
attr_accessor :email, :password, :company_name
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, presence: true, length: { minimum: 8 }
validates :company_name, presence: true
def save
return false unless valid?
ActiveRecord::Base.transaction do
user = User.create!(email: email, password: password)
Company.create!(name: company_name, owner: user)
end
end
end
```
### Query Objects
```yaml
query_objects:
title: "Query Objects"
description: "Encapsulate complex database queries"
search_paths:
- "app/queries/**/*.rb"
- "app/lib/queries/**/*.rb"
file_patterns:
- "*_query.rb"
code_patterns:
- "class \\w+Query"
- "def initialize.*relation"
- "def call"
- "def resolve"
keywords: [query, scope, filtering, search]
best_practice: |
class ActiveUsersQuery
def initialize(relation = User.all)
@relation = relation
end
def call
@relation
.where(active: true)
.where("last_login_at > ?", 30.days.ago)
.order(created_at: :desc)
end
end
# Usage:
ActiveUsersQuery.new.call
ActiveUsersQuery.new(User.where(role: 'admin')).call
```
---
## Model Patterns
### Concerns
```yaml
concerns:
title: "Model Concerns"
description: "Shared behavior across models"
search_paths:
- "app/models/concerns/**/*.rb"
file_patterns:
- "*.rb"
code_patterns:
- "module \\w+"
- "extend ActiveSupport::Concern"
- "included do"
- "class_methods do"
keywords: [concern, mixin, shared behavior]
best_practice: |
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
scope :tagged_with, ->(tag_name) {
joins(:tags).where(tags: { name: tag_name })
}
end
def tag_list
tags.pluck(:name).join(', ')
end
class_methods do
def most_tagged
joins(:taggings).group('id').order('COUNT(taggings.id) DESC')
end
end
end
```
### Scopes
```yaml
scopes:
title: "Named Scopes"
description: "Reusable query methods"
search_paths:
- "app/models/**/*.rb"
code_patterns:
- "scope :\\w+,"
- "scope :\\w+, ->"
keywords: [scope, query, filter]
best_practice: |
class User < ApplicationRecord
scope :active, -> { where(active: true) }
scope :admin, -> { where(role: 'admin') }
scope :recent, -> { where("created_at > ?", 30.days.ago) }
scope :search, ->(query) { where("name ILIKE ?", "%#{query}%") }
# Chainable scopes
scope :with_posts, -> { joins(:posts).distinct }
scope :popular, -> { where("followers_count > ?", 100) }
end
# Usage:
User.active.admin.recent
```
### Polymorphic Associations
```yaml
polymorphic_associations:
title: "Polymorphic Associations"
description: "Flexible belongs_to relationships"
search_paths:
- "app/models/**/*.rb"
code_patterns:
- "belongs_to :\\w+, polymorphic: true"
- "has_many :\\w+, as:"
keywords: [polymorphic, flexible association]
best_practice: |
# Polymorphic model
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
# Models that can have comments
class Post < ApplicationRecord
has_many :comments, as: :commentable
end
class Photo < ApplicationRecord
has_many :comments, as: :commentable
end
# Migration
create_table :comments do |t|
t.text :body
t.references :commentable, polymorphic: true
t.timestamps
end
```
### Single Table Inheritance
```yaml
sti:
title: "Single Table Inheritance"
description: "Class hierarchy in one table"
search_paths:
- "app/models/**/*.rb"
code_patterns:
- "class \\w+ < \\w+"
- "self\\.inheritance_column"
keywords: [STI, inheritance, subclass]
best_practice: |
# Base class
class User < ApplicationRecord
# Common behavior
end
# Subclasses
class Admin < User
def can_manage_users?
true
end
end
class Member < User
def can_manage_users?
false
end
end
# Migration needs 'type' column
add_column :users, :type, :string
```
---
## API Patterns
### API Versioning
```yaml
api_versioning:
title: "API Versioning"
description: "Version management strategies"
search_paths:
- "app/controllers/api/**/*.rb"
- "app/controllers/api/v*/**/*.rb"
file_patterns:
- "api/v*/**/*.rb"
code_patterns:
- "module Api::V\\d+"
- "namespace :api"
keywords: [api, version, v1, v2]
best_practice: |
# config/routes.rb
namespace :api do
namespace :v1 do
resources :users
end
namespace :v2 do
resources :users
end
end
# app/controllers/api/v1/users_controller.rb
module Api
module V1
class UsersController < ApiController
def index
render json: User.all
end
end
end
end
```
### API Serialization
```yaml
api_serialization:
title: "API Serialization"
description: "JSON response formatting"
search_paths:
- "app/serializers/**/*.rb"
- "app/blueprints/**/*.rb"
- "app/views/api/**/*.json.jbuilder"
file_patterns:
- "*_serializer.rb"
- "*_blueprint.rb"
- "*.json.jbuilder"
code_patterns:
- "class \\w+Serializer"
- "< ActiveModel::Serializer"
- "< Blueprinter::Base"
- "json\\."
keywords: [json, serializer, blueprint, jbuilder]
best_practice: |
# Using ActiveModel::Serializers
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :full_name, :created_at
has_many :posts
def full_name
"#{object.first_name} #{object.last_name}"
end
end
# Using Blueprinter
class UserBlueprint < Blueprinter::Base
identifier :id
fields :email, :created_at
field :full_name do |user|
"#{user.first_name} #{user.last_name}"
end
association :posts, blueprint: PostBlueprint
end
```
### API Authentication
```yaml
api_authentication:
title: "API Authentication"
description: "Token-based authentication"
search_paths:
- "app/controllers/api/**/*.rb"
- "app/controllers/concerns/**/*.rb"
code_patterns:
- "before_action :authenticate"
- "Authorization.*Bearer"
- "JWT"
- "authenticate_with_http_token"
keywords: [auth, token, jwt, bearer]
best_practice: |
# app/controllers/api/api_controller.rb
module Api
class ApiController < ActionController::API
before_action :authenticate_user!
private
def authenticate_user!
token = request.headers['Authorization']&.split(' ')&.last
decoded = JWT.decode(token, Rails.application.secret_key_base, true)
@current_user = User.find(decoded[0]['user_id'])
rescue JWT::DecodeError, ActiveRecord::RecordNotFound
render json: { error: 'Unauthorized' }, status: :unauthorized
end
attr_reader :current_user
end
end
```
---
## Authorization Patterns
### Policy Objects (Pundit)
```yaml
policies:
title: "Authorization Policies"
description: "Pundit policy pattern"
search_paths:
- "app/policies/**/*.rb"
file_patterns:
- "*_policy.rb"
code_patterns:
- "class \\w+Policy"
- "def initialize\\(user, record\\)"
- "def index\\?"
- "def show\\?"
- "def create\\?"
- "def update\\?"
- "def destroy\\?"
keywords: [pundit, policy, authorization, can]
best_practice: |
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def index?
true
end
def show?
true
end
def create?
user.present?
end
def update?
user.present? && (post.author == user || user.admin?)
end
def destroy?
user.present? && (post.author == user || user.admin?)
end
class Scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
if user&.admin?
scope.all
else
scope.published
end
end
private
attr_reader :user, :scope
end
end
```
---
## Background Job Patterns
### Job Structure
```yaml
background_jobs:
title: "Background Jobs"
description: "ActiveJob pattern"
search_paths:
- "app/jobs/**/*.rb"
file_patterns:
- "*_job.rb"
code_patterns:
- "class \\w+Job < ApplicationJob"
- "def perform"
- "queue_as"
- "retry_on"
keywords: [job, background, async, sidekiq]
best_practice: |
class ProcessPaymentJob < ApplicationJob
queue_as :critical
retry_on PaymentGateway::NetworkError, wait: 5.seconds, attempts: 3
discard_on PaymentGateway::InvalidCard
def perform(payment_id)
payment = Payment.find(payment_id)
PaymentProcessor.new(payment).process!
end
end
# Usage:
ProcessPaymentJob.perform_later(payment.id)
ProcessPaymentJob.set(wait: 1.hour).perform_later(payment.id)
```
---
## Testing Patterns
### Factory Usage
```yaml
factory_usage:
title: "FactoryBot Factories"
description: "Test data creation"
search_paths:
- "spec/factories/**/*.rb"
- "test/factories/**/*.rb"
file_patterns:
- "*.rb"
code_patterns:
- "FactoryBot\\.define"
- "factory :\\w+"
- "trait :\\w+"
keywords: [factory, factorybot, test data]
best_practice: |
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
password { "password123" }
first_name { "John" }
last_name { "Doe" }
trait :admin do
role { :admin }
end
trait :with_posts do
after(:create) do |user|
create_list(:post, 3, author: user)
end
end
factory :admin_user, traits: [:admin]
end
end
# Usage:
create(:user)
create(:user, :admin)
create(:user, :with_posts)
build(:user)
```
### Request Specs
```yaml
request_specs:
title: "Request Specs"
description: "API endpoint testing"
search_paths:
- "spec/requests/**/*_spec.rb"
code_patterns:
- "RSpec\\.describe.*type: :request"
- "get .*"
- "post .*"
- "expect\\(response\\)"
keywords: [request spec, api test, integration test]
best_practice: |
RSpec.describe "Api::V1::Users", type: :request do
let(:user) { create(:user) }
let(:auth_headers) { { 'Authorization' => "Bearer #{user.token}" } }
describe "GET /api/v1/users" do
it "returns users list" do
create_list(:user, 3)
get api_v1_users_path, headers: auth_headers
expect(response).to have_http_status(:ok)
expect(json_response['users'].size).to eq(4) # 3 + authenticated user
end
end
describe "POST /api/v1/users" do
let(:valid_params) do
{ user: { email: 'new@example.com', password: 'password123' } }
end
it "creates a new user" do
expect {
post api_v1_users_path, params: valid_params
}.to change(User, :count).by(1)
expect(response).to have_http_status(:created)
end
end
end
```
---
## Decorator Patterns
### Draper Decorators
```yaml
decorators:
title: "Decorator/Presenter Pattern"
description: "View logic separation"
search_paths:
- "app/decorators/**/*.rb"
- "app/presenters/**/*.rb"
file_patterns:
- "*_decorator.rb"
- "*_presenter.rb"
code_patterns:
- "class \\w+Decorator"
- "< Draper::Decorator"
- "delegate_all"
keywords: [decorator, presenter, view logic]
best_practice: |
class UserDecorator < Draper::Decorator
delegate_all
def full_name
"#{first_name} #{last_name}"
end
def formatted_created_at
created_at.strftime("%B %d, %Y")
end
def avatar_url
if object.avatar.attached?
h.url_for(object.avatar)
else
h.asset_path('default-avatar.png')
end
end
def profile_link
h.link_to full_name, h.user_path(object), class: 'user-link'
end
end
# Usage in controller:
@user = User.find(params[:id]).decorate
# Usage in view:
<%= @user.full_name %>
<%= @user.profile_link %>
```
---
## Rails 8 Defaults
Rails 8 includes several built-in solutions that replace common third-party gems:
### Solid Queue
**Purpose**: Background job processing (replaces Sidekiq, Resque, Delayed Job)
Rails 8's default background job adapter. Provides:
- Database-backed job queue
- No Redis dependency
- Built-in retry logic
- Job prioritization
- Mission control web UI
**Setup**:
```bash
bin/rails solid_queue:install
```
**Usage**:
```ruby
# config/application.rb or config/environments/production.rb
config.active_job.queue_adapter = :solid_queue
# Jobs work the same as before
class ProcessPaymentJob < ApplicationJob
queue_as :critical
def perform(payment_id)
# Job logic
end
end
```
### Solid Cache
**Purpose**: Database-backed caching (replaces Redis, Memcached for caching)
Rails 8's default cache store. Provides:
- Database-backed cache
- No Redis dependency
- All standard Rails cache features
- Automatic expiration
- Production-ready performance
**Setup**:
```bash
bin/rails solid_cache:install
```
**Usage**:
```ruby
# config/environments/production.rb
config.cache_store = :solid_cache_store
# Caching works the same as before
Rails.cache.fetch('expensive_query', expires_in: 1.hour) do
# Expensive operation
end
```
### Solid Cable
**Purpose**: Database-backed Action Cable (replaces Redis for WebSocket pub/sub)
Rails 8's default Action Cable adapter. Provides:
- Database-backed pub/sub
- No Redis dependency for real time features
- WebSocket support
- Horizontal scaling support
**Setup**:
```bash
bin/rails solid_cable:install
```
**Usage**:
```ruby
# config/cable.yml
production:
adapter: solid_cable
# Channels work the same as before
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room_id]}"
end
end
```
### When to Use Rails 8 Defaults
**Use Solid Queue when**:
- Starting new Rails 8 project
- Want to avoid Redis operational complexity
- Job volume < 1000/second
- Prefer simplicity over maximum throughput
**Use Solid Cache when**:
- Starting new Rails 8 project
- Cache hit rates are moderate
- Want unified database infrastructure
- Avoiding Redis operational overhead
**Use Solid Cable when**:
- Starting new Rails 8 project
- Real-time features with moderate concurrency
- Want to simplify infrastructure
- Database can handle WebSocket load
**Consider alternatives when**:
- Extreme performance requirements (millions of jobs/sec)
- Very high cache hit rates (>95%)
- Thousands of concurrent WebSocket connections
- Already have Redis in production
---
## Pattern Categories
The `rails-pattern-finder` skill recognizes these pattern categories:
### Authentication
User login, session management, password handling
**Common implementations**:
- Devise gem
- Rails authentication generator (Rails 8+)
- Custom authentication with `has_secure_password`
- OAuth integrations (OmniAuth)
### Background Jobs
Asynchronous task processing
**Common implementations**:
- Solid Queue (Rails 8 default)
- Sidekiq
- Resque
- Delayed Job
- ActiveJob with any adapter
### Caching
Performance optimization through data caching
**Common implementations**:
- Solid Cache (Rails 8 default)
- Redis cache store
- Memcached
- File store
- Database-backed caching
### Real Time
WebSocket connections and live updates
**Common implementations**:
- Solid Cable (Rails 8 default for Action Cable)
- Action Cable with Redis
- Hotwire Turbo Streams
- Server-Sent Events (SSE)
### File Uploads
Handling user-uploaded files
**Common implementations**:
- Active Storage (Rails built-in)
- Shrine
- CarrierWave
- Paperclip (deprecated)
### Pagination
Dividing large datasets into pages
**Common implementations**:
- Pagy
- Kaminari
- will_paginate
- Custom pagination with `limit/offset`
---
## Usage
Each pattern includes:
- **title**: Human-readable name
- **description**: What the pattern does
- **search_paths**: Where to find files
- **file_patterns**: File naming conventions
- **code_patterns**: Regex to match code structures
- **keywords**: Related concepts
- **best_practice**: Reference implementation
The `rails-pattern-finder` skill uses these definitions to:
1. Search the codebase for existing patterns
2. Extract relevant examples
3. Provide best-practice templates when pattern not found
4. Recommend Rails 8 built-in alternatives when appropriate

View File

@@ -0,0 +1,69 @@
---
name: rails-pattern-recognition
description: Adaptive learning system that tracks implementation patterns, success rates, and performance metrics to suggest proven solutions for future tasks.
version: 1.1
priority: 5
---
# Rails Pattern Recognition
Adaptive learning system for Rails workflows.
## Usage
```python
# Suggest patterns
patterns = invoke_skill("rails-pattern-recognition", mode="suggest", context_tags=["rails", "auth"])
# Update metrics
invoke_skill("rails-pattern-recognition", mode="update", metrics={
"pattern_used": "Devise Auth",
"success": true,
"duration_minutes": 15
})
```
## Implementation
This skill manages a JSON database of implementation patterns and their performance metrics.
### Data Structure (`.claude/data/rails-patterns.json`)
```json
{
"patterns": {
"Devise Auth": {
"tags": ["rails", "auth", "devise"],
"uses": 10,
"successes": 9,
"avg_duration": 12.5,
"last_used": "2023-10-27T10:00:00Z"
}
}
}
```
### Modes
#### 1. Suggest (`mode="suggest"`)
- **Input**: `context_tags` (list of strings)
- **Logic**:
- Filter patterns matching at least one tag
- Calculate confidence score: `(successes / uses) * log(uses + 1)`
- Sort by confidence
- **Output**: List of patterns with confidence scores
#### 2. Update (`mode="update"`)
- **Input**: `metrics` (dict)
- `pattern_used` (string)
- `success` (boolean)
- `duration_minutes` (float)
- **Logic**:
- Update `uses`, `successes`
- Recalculate `avg_duration`
- Update `last_used`
- **Output**: Confirmation
## Standalone Operation
This skill operates entirely within the project's `.claude/` directory, ensuring portability and independence from global system state.

View File

@@ -0,0 +1,207 @@
---
name: rails-performance-patterns
description: Detects N+1 queries, suggests eager loading, and recommends database indexes
auto_invoke: true
trigger_on: [file_modify]
file_patterns: ["**/models/**/*.rb", "**/controllers/**/*.rb"]
tags: [rails, performance, optimization, n+1, eager-loading, indexes]
priority: 2
version: 2.0
---
# Rails Performance Patterns Skill
Automatically detects performance issues and suggests optimizations.
## What This Skill Does
**Automatic Detection:**
- N+1 query problems (missing eager loading)
- Missing database indexes on foreign keys
- Inefficient query patterns
- Large result sets without pagination
**When It Activates:**
- Model files with associations modified
- Controller actions that query models
- Iteration over associations detected
## Key Checks
### 1. N+1 Query Detection
**Problem Pattern:**
```ruby
# app/controllers/posts_controller.rb
def index
@posts = Post.all # 1 query
@posts.each do |post|
puts post.author.name # N queries (one per post)
end
end
```
**Skill Output:**
```
⚠️ Performance: Potential N+1 query
Location: app/controllers/posts_controller.rb:15
Issue: Accessing 'author' association in loop without eager loading
Fix: Add includes to eager load:
@posts = Post.includes(:author).all
```
**Solution:**
```ruby
def index
@posts = Post.includes(:author).all # 2 queries total
end
```
### 2. Missing Indexes
**Checks:**
- Foreign keys have indexes
- Commonly queried columns indexed
- Unique constraints have indexes
**Example:**
```ruby
# db/migrate/xxx_create_posts.rb
create_table :posts do |t|
t.references :user # ✅ Auto-creates index
t.string :slug # ❌ Missing index if queried often
end
```
**Skill Output:**
```
⚠️ Performance: Missing index recommendation
Location: app/models/post.rb:5
Issue: slug column used in where clauses without index
Add migration:
add_index :posts, :slug, unique: true
```
### 3. Pagination Missing
**Problem:**
```ruby
def index
@products = Product.all # ❌ Loads all 100k+ products
end
```
**Skill Output:**
```
⚠️ Performance: Large result set without pagination
Location: app/controllers/products_controller.rb:10
Issue: Loading all Product records (estimated 100k+ rows)
Recommendation: Add pagination
# Use kaminari or pagy
@products = Product.page(params[:page]).per(20)
```
### 4. Counter Cache Opportunities
**Pattern:**
```ruby
# Without counter cache
@user.posts.count # Runs COUNT(*) query every time
# With counter cache
@user.posts_count # Reads from cached column
```
**Skill Output:**
```
Performance: Counter cache opportunity
Location: app/views/users/show.html.erb:12
Pattern: Frequently accessing post count
Add to migration:
add_column :users, :posts_count, :integer, default: 0
Update association:
belongs_to :user, counter_cache: true
```
## Configuration
```yaml
# .rails-performance.yml
n1_detection:
enabled: true
severity: warning
indexes:
check_foreign_keys: true
check_query_columns: true
pagination:
warn_threshold: 1000
require_for_large_tables: true
counter_cache:
suggest_threshold: 3 # Suggest if accessed 3+ times
```
## Monitoring Integration
**Add instrumentation:**
```ruby
# config/initializers/query_monitoring.rb
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
if event.duration > 100 # Log slow queries
Rails.logger.warn("Slow query (#{event.duration}ms): #{event.payload[:sql]}")
end
end
```
## Common Optimizations
**Eager Loading:**
```ruby
# N+1
Post.all.each { |p| p.author.name }
# Fixed: includes (left join)
Post.includes(:author).each { |p| p.author.name }
# Fixed: preload (separate queries)
Post.preload(:author).each { |p| p.author.name }
# Fixed: eager_load (always joins)
Post.eager_load(:author).each { |p| p.author.name }
```
**Select Specific Columns:**
```ruby
# Loads all columns
Post.all
# Loads only needed columns
Post.select(:id, :title, :created_at)
```
**Batch Processing:**
```ruby
# Loads all at once
Post.all.each { |p| process(p) }
# Loads in batches of 1000
Post.find_each(batch_size: 1000) { |p| process(p) }
```
## References
- **Rails Performance Guide**: https://guides.rubyonrails.org/performance_testing.html
- **Bullet Gem**: https://github.com/flyerhzm/bullet
- **Pattern Library**: /patterns/caching-patterns.md
---
**This skill helps you build fast Rails applications from the start.**

View File

@@ -0,0 +1,311 @@
---
name: rails-security-patterns
description: Automatically validates security best practices and prevents vulnerabilities
auto_invoke: true
trigger_on: [file_create, file_modify]
file_patterns: ["**/controllers/**/*.rb", "**/models/**/*.rb"]
tags: [rails, security, authentication, authorization, sql-injection]
priority: 1
version: 2.0
---
# Rails Security Patterns Skill
Auto-validates security best practices and blocks common vulnerabilities.
## What This Skill Does
**Automatic Security Checks:**
- Strong parameters in controllers (prevents mass assignment)
- SQL injection prevention (parameterized queries)
- CSRF token handling (API mode considerations)
- Authentication presence
- Authorization checks
**When It Activates:**
- Controller files created or modified
- Model files with database queries modified
- Authentication-related changes
## Security Checks
### 1. Strong Parameters
**Checks:**
- Every `create` and `update` action uses strong parameters
- No direct `params` usage in model instantiation
- `permit` calls include only expected attributes
**Example Violation:**
```ruby
# BAD
def create
@user = User.create(params[:user]) # ❌ Mass assignment
end
# GOOD
def create
@user = User.create(user_params) # ✅ Strong params
end
private
def user_params
params.require(:user).permit(:name, :email)
end
```
**Skill Output:**
```
❌ Security: Mass assignment vulnerability
Location: app/controllers/users_controller.rb:15
Issue: params[:user] used directly without strong parameters
Fix: Define strong parameters method:
private
def user_params
params.require(:user).permit(:name, :email, :role)
end
Then use: @user = User.create(user_params)
```
### 2. SQL Injection Prevention
**Checks:**
- No string interpolation in `where` clauses
- Parameterized queries used
- No raw SQL without placeholders
**Example Violation:**
```ruby
# BAD
User.where("email = '#{params[:email]}'") # ❌ SQL injection
User.where("name LIKE '%#{params[:query]}%'") # ❌ SQL injection
# GOOD
User.where("email = ?", params[:email]) # ✅ Parameterized
User.where("name LIKE ?", "%#{params[:query]}%") # ✅ Safe
User.where(email: params[:email]) # ✅ Hash syntax
```
**Skill Output:**
```
❌ Security: SQL injection vulnerability
Location: app/models/user.rb:45
Issue: String interpolation in SQL query
Vulnerable code:
User.where("email = '#{email}'")
Fix: Use parameterized query:
User.where("email = ?", email)
Or use hash syntax:
User.where(email: email)
```
### 3. Authentication Checks
**Checks:**
- Controllers have authentication filters
- Sensitive actions require authentication
- Token-based auth for API endpoints
**Example:**
```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user! # ✅ Auth required
def index
# ...
end
end
```
**Skill Output (if missing):**
```
⚠️ Security: No authentication found
Location: app/controllers/admin/users_controller.rb
Issue: Admin controller without authentication
Recommendation: Add authentication:
before_action :authenticate_user!
before_action :require_admin!
```
### 4. Authorization Checks
**Checks:**
- Update/destroy actions verify ownership
- Role-based access control present
- Resource-level authorization
**Example:**
```ruby
# BAD
def destroy
@post = Post.find(params[:id])
@post.destroy # ❌ No ownership check
end
# GOOD
def destroy
@post = current_user.posts.find(params[:id]) # ✅ Scoped to user
@post.destroy
end
# BETTER
def destroy
@post = Post.find(params[:id])
authorize @post # ✅ Using Pundit/CanCanCan
@post.destroy
end
```
**Skill Output:**
```
⚠️ Security: Missing authorization check
Location: app/controllers/posts_controller.rb:42
Issue: destroy action without ownership verification
Recommendation: Add authorization:
Option 1 (scope to user):
@post = current_user.posts.find(params[:id])
Option 2 (use authorization gem):
authorize @post # Pundit
authorize! :destroy, @post # CanCanCan
```
### 5. Sensitive Data Exposure
**Checks:**
- No passwords in logs
- API keys not hardcoded
- Secrets use environment variables
**Example Violation:**
```ruby
# BAD
API_KEY = "sk_live_abc123..." # ❌ Hardcoded secret
# GOOD
API_KEY = ENV['STRIPE_API_KEY'] # ✅ Environment variable
```
**Skill Output:**
```
❌ Security: Hardcoded secret detected
Location: config/initializers/stripe.rb:3
Issue: API key hardcoded in source
Fix: Use environment variable:
API_KEY = ENV['STRIPE_API_KEY']
Add to .env (don't commit):
STRIPE_API_KEY=sk_live_your_key_here
```
## Integration with Pre-commit Hook
This skill works with the pre-commit hook to block unsafe commits:
**Automatic blocks:**
- SQL injection vulnerabilities
- Missing strong parameters in create/update actions
- Hardcoded secrets/API keys
- Mass assignment vulnerabilities
**Warnings (allow commit):**
- Missing authentication (might be intentional for public endpoints)
- Missing authorization (might use custom logic)
- Complex queries (performance concern, not security)
## Configuration
Create `.rails-security.yml` to customize:
```yaml
# .rails-security.yml
strong_parameters:
enforce: true
block_commit: true
sql_injection:
enforce: true
block_commit: true
authentication:
require_for_controllers: true
exceptions:
- Api::V1::PublicController
- PagesController
authorization:
warn_on_missing: true
block_commit: false
secrets:
detect_patterns:
- "sk_live_"
- "api_key"
- "password"
- "secret"
block_commit: true
```
## Common Patterns
### API Authentication
**Token-based:**
```ruby
class Api::BaseController < ActionController::API
before_action :authenticate_token!
private
def authenticate_token!
token = request.headers['Authorization']&.split(' ')&.last
@current_user = User.find_by(api_token: token)
render json: { error: 'Unauthorized' }, status: :unauthorized unless @current_user
end
end
```
### Scope to User
**Pattern:**
```ruby
# Always scope to current_user when possible
@posts = current_user.posts
@post = current_user.posts.find(params[:id])
# Prevents accessing other users' resources
```
### Rate Limiting
**Recommendation:**
```ruby
# Gemfile
gem 'rack-attack'
# config/initializers/rack_attack.rb
Rack::Attack.throttle('api/ip', limit: 100, period: 1.minute) do |req|
req.ip if req.path.start_with?('/api/')
end
```
## References
- **OWASP Top 10**: https://owasp.org/www-project-top-ten/
- **Rails Security Guide**: https://guides.rubyonrails.org/security.html
- **Pattern Library**: /patterns/authentication-patterns.md
---
**This skill runs automatically and blocks security vulnerabilities before they reach production.**

View File

@@ -0,0 +1,242 @@
---
name: rails-test-patterns
description: Ensures comprehensive test coverage following Rails testing best practices
auto_invoke: true
trigger_on: [file_create, file_modify]
file_patterns: ["**/spec/**/*_spec.rb", "**/test/**/*_test.rb"]
tags: [rails, testing, rspec, minitest, coverage, tdd]
priority: 2
version: 2.0
---
# Rails Test Patterns Skill
Automatically ensures test quality and comprehensive coverage.
## What This Skill Does
**Automatic Checks:**
- Test framework detection (RSpec vs Minitest)
- AAA pattern (Arrange-Act-Assert)
- Coverage thresholds (80%+ models, 70%+ controllers)
- Factory usage over fixtures
- No flaky tests (sleep, Time.now without freezing)
**When It Activates:**
- Test files created or modified
- New models/controllers added (ensures tests exist)
## Test Quality Checks
### 1. AAA Pattern
**Good:**
```ruby
RSpec.describe User do
describe '#full_name' do
it 'combines first and last name' do
# Arrange
user = User.new(first_name: 'John', last_name: 'Doe')
# Act
result = user.full_name
# Assert
expect(result).to eq('John Doe')
end
end
end
```
**Skill Output (if violated):**
```
⚠️ Test Pattern: AAA structure not clear
Location: spec/models/user_spec.rb:15
Recommendation: Separate Arrange, Act, Assert with comments
```
### 2. Framework Detection
**Detects:**
```ruby
# RSpec
describe Model do
it 'does something' do
expect(value).to eq(expected)
end
end
# Minitest
class ModelTest < ActiveSupport::TestCase
test "does something" do
assert_equal expected, value
end
end
```
### 3. Factory Usage
**Preferred:**
```ruby
# spec/factories/users.rb
FactoryBot.define do
factory :user do
email { Faker::Internet.email }
name { Faker::Name.name }
end
end
# In tests
user = create(:user)
```
**Skill Output:**
```
Test Pattern: Consider using FactoryBot
Location: spec/models/post_spec.rb:8
Current: User.create(name: 'Test', email: 'test@example.com')
Recommendation: Define factory and use create(:user)
```
### 4. Coverage Thresholds
**Checks:**
- Models: 80%+ coverage
- Controllers: 70%+ coverage
- Services: 90%+ coverage
**Skill Output:**
```
⚠️ Test Coverage: Below threshold
File: app/models/order.rb
Coverage: 65% (threshold: 80%)
Missing: #calculate_total, #apply_discount methods
Add tests for uncovered methods.
```
### 5. Flaky Test Detection
**Problem:**
```ruby
# BAD
it 'expires after 1 hour' do
user = create(:user)
sleep(3601) # ❌ Actual waiting
expect(user.expired?).to be true
end
# GOOD
it 'expires after 1 hour' do
user = create(:user)
travel 1.hour do # ✅ Time travel
expect(user.expired?).to be true
end
end
```
**Skill Output:**
```
❌ Test Anti-pattern: Using sleep in test
Location: spec/models/session_spec.rb:42
Issue: sleep() makes tests slow and flaky
Fix: Use time helpers
travel 1.hour do
# assertions here
end
```
## Test Types
### Model Tests
**Should test:**
- Validations
- Associations
- Scopes
- Instance methods
- Class methods
**Example:**
```ruby
RSpec.describe Post, type: :model do
describe 'validations' do
it { should validate_presence_of(:title) }
it { should validate_uniqueness_of(:slug) }
end
describe 'associations' do
it { should belong_to(:user) }
it { should have_many(:comments) }
end
describe '#published?' do
it 'returns true when published_at is set' do
post = build(:post, published_at: 1.day.ago)
expect(post.published?).to be true
end
end
end
```
### Request Tests
**Should test:**
- HTTP status codes
- Response body content
- Authentication requirements
- Authorization checks
**Example:**
```ruby
RSpec.describe 'Posts API', type: :request do
describe 'GET /api/v1/posts' do
it 'returns all posts' do
create_list(:post, 3)
get '/api/v1/posts'
expect(response).to have_http_status(:ok)
expect(JSON.parse(response.body).size).to eq(3)
end
context 'when not authenticated' do
it 'returns unauthorized' do
get '/api/v1/posts'
expect(response).to have_http_status(:unauthorized)
end
end
end
end
```
## Configuration
```yaml
# .rails-testing.yml
coverage:
models: 80
controllers: 70
services: 90
patterns:
enforce_aaa: warning
require_factories: info
detect_flaky: error
framework:
auto_detect: true
prefer: rspec # or minitest
```
## References
- **RSpec Best Practices**: https://rspec.info/
- **Minitest Guide**: https://github.com/minitest/minitest
- **FactoryBot**: https://github.com/thoughtbot/factory_bot
- **Pattern Library**: /patterns/testing-patterns.md
---
**This skill ensures your Rails app has comprehensive, maintainable test coverage.**

View File

@@ -0,0 +1,173 @@
# Rails Version Detector
---
name: rails-version-detector
description: Detects Rails version from project files (Gemfile.lock, Gemfile, or .ruby-version)
version: 1.1.0
author: Rails Workflow Team
tags: [rails, version, detection, helper]
priority: 2
---
## Purpose
Automatically detects the Rails version in the current project by inspecting:
1. `Gemfile.lock` (preferred - exact version)
2. `Gemfile` (fallback - version constraint)
3. `.ruby-version` or `.tool-versions` (Ruby version hint)
Returns version information for use by other skills and agents.
## Usage
This skill is **auto-invoked** by other Rails skills when they need version information.
**Manual invocation** (rarely needed):
```
@rails-version-detector
```
## Detection Strategy
### Priority 1: Gemfile.lock (Exact Version)
```ruby
# Searches for:
rails (7.1.3)
actioncable (= 7.1.3)
actionmailbox (= 7.1.3)
...
```
**Extraction logic**:
- Pattern: `rails \((\d+\.\d+\.\d+)\)`
- Returns: Exact version (e.g., "7.1.3")
### Priority 2: Gemfile (Version Constraint)
```ruby
# Searches for:
gem "rails", "~> 7.1.0"
gem 'rails', '>= 7.0.0', '< 8.0'
```
**Extraction logic**:
- Pattern: `gem ['"]rails['"],\s*['"]([^'"]+)['"]`
- Returns: Version constraint (e.g., "~> 7.1.0")
- Interprets: "~> 7.1.0" → "7.1.x"
### Priority 3: Ruby Version (Heuristic)
```ruby
# .ruby-version or .tool-versions
ruby 3.2.2
```
**Mapping**:
- Ruby 3.3.x → Rails 7.1+ or 8.0+
- Ruby 3.2.x → Rails 7.0+
- Ruby 3.1.x → Rails 7.0+
- Ruby 3.0.x → Rails 6.1+ or 7.0
- Ruby 2.7.x → Rails 6.0 or 6.1
**Returns**: Estimated range (e.g., "7.0 or 7.1")
## Output Format
### Success Response
```json
{
"version": "7.1.3",
"source": "Gemfile.lock",
"confidence": "exact",
"major": 7,
"minor": 1,
"patch": 3
}
```
### Constraint Response
```json
{
"version": "~> 7.1.0",
"source": "Gemfile",
"confidence": "constraint",
"interpreted_as": "7.1.x",
"major": 7,
"minor": 1
}
```
### Heuristic Response
```json
{
"version": "7.0-7.1",
"source": ".ruby-version",
"confidence": "heuristic",
"ruby_version": "3.2.2",
"possible_rails": ["7.0", "7.1"]
}
```
### Not Found Response
```json
{
"version": null,
"source": null,
"confidence": "none",
"error": "No Rails version detected. Is this a Rails project?"
}
```
## Implementation
**Tool usage**:
- `Read` tool to read Gemfile.lock, Gemfile, .ruby-version
- `Grep` tool to search for version patterns
- Fallback to `Bash` if needed: `bundle show rails | grep 'rails-'`
**Caching**:
- Version detection results cached for session
- Re-check if Gemfile.lock modified (check mtime)
## Error Handling
**Gemfile.lock missing**:
- Fallback to Gemfile
- Warn: "Gemfile.lock not found. Run `bundle install` for exact version."
**No version found**:
- Return error response
- Suggest: "Ensure `gem 'rails'` in Gemfile"
**Multiple Rails versions**:
- Return first match (main Rails gem)
- Ignore: railties, actionpack (these are sub-gems)
## Integration
**Auto-invoked by**:
- @rails-docs-search (to fetch correct version docs)
- @rails-api-lookup (to search version-specific APIs)
- @rails-pattern-finder (to find version-appropriate patterns)
- All 7 Rails agents (to ensure version-compatible code generation)
**Manual use case**:
```
User: "What Rails version is this project using?"
Assistant: *invokes @rails-version-detector*
"This project uses Rails 7.1.3 (detected from Gemfile.lock)"
```
## Testing
**Test cases**:
1. Rails 7.1.3 in Gemfile.lock → Exact version
2. `~> 7.1.0` in Gemfile → Interpreted as 7.1.x
3. Ruby 3.2.2 only → Heuristic: 7.0-7.1
4. No Rails → Error response
5. Rails 8.0.0 → Correct major version detection
## Notes
- This skill replaces the need for `mcp__rails__get_rails_info` MCP tool
- Version detection is fast (< 1 second)
- Results cached per session to avoid repeated file reads
- Confidence levels help downstream skills decide if they need clarification from user