Curated library of 41 public AI agent skills for Ruby on Rails development. Organized by category: planning, testing, code-quality, ddd, engines, infrastructure, api, patterns, context, and orchestration. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and TDD automation. Repository workflows remain documented in GitHub but are intentionally excluded from the Tessl tile.
95
93%
Does it follow best practices?
Impact
96%
1.77xAverage score across 41 eval scenarios
Passed
No known issues
Complete code examples for Turbo and Stimulus implementations.
<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
<%= turbo_frame_tag "posts_list" do %>
<%= render @posts %>
<% end %><!-- app/views/posts/index.html.erb -->
<%= turbo_frame_tag "new_post" do %>
<%= link_to "New Post", new_post_path %>
<% end %>
<%= turbo_frame_tag "posts_list" do %>
<%= render @posts %>
<% end %><!-- app/views/posts/new.html.erb -->
<%= turbo_frame_tag "new_post" do %>
<%= render "form", post: @post %>
<% end %><!-- Load content when frame scrolls into view -->
<%= turbo_frame_tag "comments", src: post_comments_path(@post), loading: :lazy do %>
<p>Loading comments...</p>
<% end %><!-- app/views/posts/create.turbo_stream.erb -->
<%= turbo_stream.append "posts_list", partial: "post", locals: { post: @post } %>
<%= turbo_stream.update "new_post", partial: "posts/new_link" %>
<%= turbo_stream.update "post_count", Post.count %><!-- app/views/posts/update.turbo_stream.erb -->
<%= turbo_stream.replace @post, partial: "post", locals: { post: @post } %><!-- app/views/posts/destroy.turbo_stream.erb -->
<%= turbo_stream.remove @post %>
<%= turbo_stream.update "post_count", Post.count %># app/models/post.rb
class Post < ApplicationRecord
after_create_commit -> { broadcast_append_to "posts" }
after_update_commit -> { broadcast_replace_to "posts" }
after_destroy_commit -> { broadcast_remove_to "posts" }
end<!-- app/views/posts/index.html.erb -->
<%= turbo_stream_from "posts" %>
<%= turbo_frame_tag "posts_list" do %>
<%= render @posts %>
<% end %>// app/javascript/controllers/clipboard_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["source", "button"]
copy() {
navigator.clipboard.writeText(this.sourceTarget.value)
this.buttonTarget.textContent = "Copied!"
setTimeout(() => {
this.buttonTarget.textContent = "Copy"
}, 2000)
}
}<!-- app/views/posts/show.html.erb -->
<div data-controller="clipboard">
<input data-clipboard-target="source" type="text" value="<%= post_url(@post) %>" readonly>
<button data-action="clipboard#copy" data-clipboard-target="button">Copy</button>
</div>// app/javascript/controllers/slideshow_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["slide"]
static values = { index: Number }
next() {
this.indexValue++
}
previous() {
this.indexValue--
}
indexValueChanged() {
this.showCurrentSlide()
}
showCurrentSlide() {
this.slideTargets.forEach((slide, index) => {
slide.hidden = index !== this.indexValue
})
}
}<div data-controller="slideshow" data-slideshow-index-value="0">
<button data-action="slideshow#previous">←</button>
<button data-action="slideshow#next">→</button>
<div data-slideshow-target="slide">🐵</div>
<div data-slideshow-target="slide" hidden>🙈</div>
<div data-slideshow-target="slide" hidden>🙉</div>
<div data-slideshow-target="slide" hidden>🙊</div>
</div>// app/javascript/controllers/form_validation_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["input", "error"]
validate() {
if (this.inputTarget.value.length < 5) {
this.errorTarget.textContent = "Must be at least 5 characters"
} else {
this.errorTarget.textContent = ""
}
}
}<%= form_with model: @post, data: { controller: "form-validation" } do |f| %>
<%= f.text_field :title,
data: {
form_validation_target: "input",
action: "input->form-validation#validate"
} %>
<span data-form-validation-target="error" class="error"></span>
<% end %><!-- Step 1: Plain HTML (works without JS) -->
<%= form_with model: @post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit "Create Post" %>
<% end %><!-- Step 2: Wrap in frame for partial updates -->
<%= turbo_frame_tag "post_form" do %>
<%= form_with model: @post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit "Create Post" %>
<% end %>
<% end %># Step 3: Add controller response
class PostsController < ApplicationController
def create
@post = Post.new(post_params)
if @post.save
respond_to do |format|
format.turbo_stream
format.html { redirect_to @post }
end
else
render :new, status: :unprocessable_entity
end
end
endReal-time updates via WebSocket:
# app/models/post.rb
class Post < ApplicationRecord
broadcasts_to ->(post) { [post.board, "posts"] }, inserts_by: :prepend
endSurgical DOM updates (Turbo 8+):
<%= turbo_stream.morph @post, partial: "posts/post", locals: { post: @post } %>Scoped navigation with nested turbo frames:
<%= turbo_frame_tag "board" do %>
<%= turbo_frame_tag "post_#{@post.id}" do %>
<%= link_to "Edit", edit_post_path(@post) %>
<% end %>
<% end %>Configurable, CSS-decoupled controllers:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { url: String, delay: { type: Number, default: 300 } }
static classes = ["active"]
connect() {
this.element.classList.add(this.activeClass)
console.log("Fetching from", this.urlValue, "after", this.delayValue, "ms")
}
}<div data-controller="loader"
data-loader-url-value="/posts"
data-loader-delay-value="500"
data-loader-active-class="is-loading">
</div>docs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
mcp_server
skills
api
generate-api-collection
implement-graphql
code-quality
apply-code-conventions
apply-stack-conventions
assets
snippets
code-review
refactor-code
respond-to-review
review-architecture
security-check
context
load-context
setup-environment
ddd
define-domain-language
model-domain
review-domain-boundaries
engines
create-engine
create-engine-installer
document-engine
extract-engine
release-engine
review-engine
test-engine
upgrade-engine
infrastructure
implement-background-job
implement-hotwire
optimize-performance
review-migration
seed-database
version-api
orchestration
skill-router
patterns
create-service-object
implement-calculator-pattern
write-yard-docs
planning
create-prd
generate-tasks
plan-tickets
testing
plan-tests
test-service
triage-bug
write-tests
workflows