From ruby-upgrade-toolkit
Use when the user runs /ruby-upgrade-toolkit:upgrade or wants a fully automated Ruby/Rails upgrade pipeline. Orchestrates a phased upgrade by delegating per-phase execution to the fix skill and verification to the status skill. Keeps a live task list, pauses on RED verification, and lets the user continue, retry, or abort. Accepts ruby:X.Y.Z and optional rails:X.Y arguments.
npx claudepluginhub dhruvasagar/ruby-upgrade-toolkit --plugin ruby-upgrade-toolkitThis skill is limited to using the following tools:
Run a fully automated, phased Ruby (and optionally Rails) upgrade from start to finish.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Run a fully automated, phased Ruby (and optionally Rails) upgrade from start to finish.
Architecture. Upgrade is a thin orchestrator. It owns prerequisite checks (baseline, branch, Ruby installs), the phase loop, banners, the Failure Protocol, and the Final infra checklist. Everything else is delegated:
plan/SKILL.md creates the TodoWrite task list — the single shared source of "what phases remain" for both manual and orchestrated flows. Upgrade reuses an existing list if its implied target matches the invocation arguments (so a user who ran /plan then did a few /fix next iterations manually keeps their progress when they later call /upgrade); otherwise it re-invokes plan, which overwrites. Upgrade never builds its own list.fix/SKILL.md owns the complete per-phase flow: apply → iterate to green → verify → prompt for commit → tick off the task. Upgrade drives it by repeatedly invoking /ruby-upgrade-toolkit:fix next.status/SKILL.md is loaded by fix during its verification step; upgrade does not run status directly for per-phase gating.skills/rails-upgrade-guide/references/.Manual mode equivalence. Because upgrade drives fix through the same /fix next invocation a user would type, the manual workflow (/plan once, then iterate /fix next) produces identical behaviour to /upgrade. The only thing upgrade adds is the pre-loop prerequisite checks, the automatic loop, and the post-loop infra check — the per-phase work is byte-identical.
Auto-advancement. Once fix completes a phase with a successful commit (user approved at fix's commit prompt), upgrade moves to the next task automatically — no separate "continue?" prompt between phases. The user's checkpoint is the per-phase commit confirmation inside fix. If the user declines the commit, upgrade pauses and asks whether to continue on a dirty tree or abort.
Maintain a live task list so the user can see exactly what is done, what is in progress, and what is coming next. Pause and surface any failure clearly before asking whether to continue, retry, or abort.
Extract from the user's arguments:
ruby:X.Y.Z — required target Ruby versionrails:X.Y — optional target Rails version (omit sections if not provided)ruby -v 2>/dev/null || true
cat .ruby-version 2>/dev/null
grep "^ruby " Gemfile 2>/dev/null
grep -A2 "RUBY VERSION" Gemfile.lock 2>/dev/null
bundle exec rails -v 2>/dev/null || true
grep "gem ['\"]rails['\"]" Gemfile 2>/dev/null
grep "^ rails " Gemfile.lock 2>/dev/null | head -1
git branch --show-current 2>/dev/null
Read Gemfile and Gemfile.lock. Record:
CURRENT_RUBY — current active Ruby versionTARGET_RUBY — from argumentsCURRENT_RAILS — if Rails project (else "none")TARGET_RAILS — from arguments (else "none")Early exit: If CURRENT_RUBY == TARGET_RUBY and (TARGET_RAILS is none OR CURRENT_RAILS == TARGET_RAILS), print:
Already at target versions (Ruby TARGET_RUBY / Rails TARGET_RAILS).
Nothing to do. Run /ruby-upgrade-toolkit:status to confirm readiness.
Then stop.
If rails: argument given, load $CLAUDE_PLUGIN_ROOT/skills/rails-upgrade-guide/references/ruby-rails-compatibility.md and apply its validation rules. On hard incompatibility, stop immediately using the reference's error template.
Load $CLAUDE_PLUGIN_ROOT/skills/rails-upgrade-guide/references/upgrade-paths.md and use its rules to compute the ordered list of intermediate Ruby (and Rails) versions between current and target. Each intermediate version becomes its own phase — the test suite must be green before moving to the next.
Upgrade does not build its own task list; plan owns that. But upgrade checks whether an existing list can be reused before regenerating, so a user who already ran /plan (manually or from a previous upgrade run) and completed some phases via /fix next keeps their progress.
Call TodoWrite/TaskList. Collect all tasks whose subject matches ^Phase \d+ — Ruby|Rails or ^Final — Infra checks.
From the collected tasks:
Phase N — Ruby (\d+\.\d+\.\d+): → LIST_TARGET_RUBY (or none if no Ruby tasks)Phase N — Rails (\d+\.\d+): → LIST_TARGET_RAILS (or none if no Rails tasks)If no matching tasks exist at all, treat the list as absent → go to 4d.
Reuse the list if all of the following hold:
LIST_TARGET_RUBY == TARGET_RUBY or (current Ruby already equals TARGET_RUBY and LIST_TARGET_RUBY == none)LIST_TARGET_RAILS == TARGET_RAILS or (TARGET_RAILS == none and LIST_TARGET_RAILS == none)Otherwise regenerate (go to 4d). Print which way you went so the user can see it:
Reusing existing task list (target matches): N tasks, M already completed.
or
Existing task list targets Ruby X.Y / Rails A.B, but you invoked upgrade for
Ruby X'.Y' / Rails A'.B'. Regenerating task list to match the new target.
Load $CLAUDE_PLUGIN_ROOT/skills/plan/SKILL.md and run it with ruby:<TARGET_RUBY> [rails:<TARGET_RAILS>]. Plan generates the Markdown roadmap (with estimates) AND creates the TodoWrite task list, overwriting any existing one per its Step 7 rules.
Regardless of reuse or regeneration, print:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Ruby Upgrade Orchestrator
Ruby: CURRENT_RUBY → TARGET_RUBY
Rails: CURRENT_RAILS → TARGET_RAILS (or "Ruby only")
Path: [list intermediate steps if multi-step]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[N tasks total, M pending, M' completed]. Running prerequisites next.
The resulting task list has this shape:
Phase 1 — Ruby X.Y.Z: apply + verify + commit
Phase 2 — Ruby X.Y.Z: apply + verify + commit
...
Phase N — Rails X.Y: apply + verify + commit
Final — Infra checks (CI/CD, Dockerfile) + checklist
These checks are upgrade-specific orchestration — they do not appear as tasks in plan's list, and manual users who run /plan + /fix next skip them (or do equivalents on their own).
Run the "Test suite — full run" and "Test suite — failure count" blocks from $CLAUDE_PLUGIN_ROOT/skills/rails-upgrade-guide/references/verification-suite.md.
Record BASELINE_FAILURES. If > 0, report:
⚠️ Pre-existing failures detected: N failures before upgrade begins.
These are NOT caused by the upgrade and will not be fixed automatically.
Do not abort — continue.
If the upgrade path crosses more than one Ruby minor version, verify each intermediate version is already installed:
rbenv versions 2>/dev/null || rvm list 2>/dev/null
If any intermediate Ruby version is missing, stop and list what needs to be installed, then the user re-runs /upgrade.
git status --short
git branch --show-current
If already on an upgrade branch (e.g. upgrade/ruby-*), continue. Otherwise:
Recommended: create a dedicated branch before starting.
git checkout -b upgrade/ruby-TARGET_RUBY
Proceed on current branch? [yes / no — I'll create the branch first]
Wait for confirmation before proceeding.
Run the "RuboCop — offense count (JSON)" block. Record BASELINE_RUBOCOP.
Apply the "Load", "Schema check", and "Preflight (credentials)" sections of
$CLAUDE_PLUGIN_ROOT/skills/rules/references/rules-engine.md, using the
upgrade-time strictness level: missing credential env vars are a hard
error.
If .ruby-upgrade-toolkit/rules.yml is absent, set RULES_LOADED = false
and skip this entire substep — upgrade behaves byte-identically to a
version without this feature.
If rules are loaded:
/ruby-upgrade-toolkit:rules validate.RULES_BY_PHASE. This drives the per-phase preview
banner in Step 6.Print: ✓ Custom rules preflight OK — N active rules across M phases
(or the appropriate "no rules.yml present" message).
Print: ✓ Prerequisites complete — baseline recorded (N RSpec failures, N RuboCop offenses)
Loop until no fix-actionable tasks remain in the task list.
On each iteration:
TodoWrite/TaskList to find the first pending task. If none remain, break the loop and proceed to Step 7 (Final).Phase N — Ruby X.Y.Z: apply + verify + commit):
ruby -v). If not, pause with the standard activation instruction and wait for continue./ruby-upgrade-toolkit:fix next — fix reads the same task list, picks up the same task, executes end-to-end (apply → iterate to green → verify → prompt for commit → tick off task on commit).Phase N — Rails X.Y: apply + verify + commit):
/ruby-upgrade-toolkit:fix next — no need to check Ruby (it's already at target).Outcomes from each fix invocation:
Phase completed verification but commit was declined.
Reply "continue" to advance past this task (your working tree is dirty),
or "abort" to stop the upgrade.
On continue, manually tick the task off in the list and advance.Why upgrade doesn't load fix's SKILL.md directly: the /fix next slash-command invocation is what drives parsing, task resolution, and the commit prompt end-to-end. Calling it keeps manual and orchestrated flows truly identical — the same fix run the user would get if they typed /fix next themselves.
Mark the "Final — Infra checks" task as in progress.
Print:
━━━ Final — Infra checks ━━━
Load $CLAUDE_PLUGIN_ROOT/skills/status/SKILL.md and run it to produce the full health dashboard (test suite, deprecations, RuboCop, Zeitwerk, readiness tier).
Additionally run these cross-cutting infra checks that don't fit status's code-level scope:
# CI/CD files still referencing old Ruby
grep -rn "ruby-${CURRENT_RUBY}" .github/ .circleci/ Jenkinsfile .gitlab-ci.yml 2>/dev/null | head -10
# Dockerfiles still using old base image
grep -rn "ruby:${CURRENT_RUBY}" Dockerfile* docker-compose* 2>/dev/null | head -10
Surface any matches in the Final Summary as manual steps.
Mark the "Final — Infra checks" task complete in TodoWrite.
Print the complete summary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Upgrade Complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Ruby: CURRENT_RUBY → TARGET_RUBY ✓
Rails: CURRENT_RAILS → TARGET_RAILS ✓ (or "not upgraded")
RSpec
Before: N failures (N pre-existing)
After: N failures (all pre-existing — 0 new)
RuboCop
Before: N offenses
After: N offenses
Gems updated: [list]
Files changed: N
━━━ Manual steps still required ━━━
• CI/CD files still referencing Ruby CURRENT_RUBY:
[list paths]
• Dockerfiles still using old base image:
[list paths]
• Pre-existing RSpec failures (fix independently): N
• Complex patterns deferred for user review:
[list any HABTM, open redirect, or other deferred items]
Run /ruby-upgrade-toolkit:status at any time to recheck readiness.
Invoke this whenever a phase verification returns RED (new failures above baseline), or a fix introduces a regression that cannot be auto-corrected in one retry.
Print:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⛔ UPGRADE PAUSED — Phase [name] did not pass verification
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Failures introduced by this phase: N (above the N pre-existing baseline)
[Print the full RSpec failure output for each new failure — file, line, error message, backtrace]
━━━ What you can do ━━━
A) Investigate and fix — resolve the failures yourself, then reply "continue"
to resume from the verification step of this phase.
B) Retry this phase — reply "retry phase [name]" to re-run just this phase's
fixes from the beginning (useful if a fix was incomplete).
C) Abort — reply "abort" to stop here. All changes made so far
are preserved — nothing is rolled back.
D) Disable rule <id> — (only shown when a custom rule caused the failure,
e.g. a required verification-gate failed). Reply
"disable rule <id>" to flip that rule's enabled flag
to false and retry this phase. Use when you want to
unblock the pipeline and revisit the rule later
rather than editing rules.yml mid-run.
Waiting for your decision...
When rendering this prompt: include option D only if RULES_LOADED is true
and at least one of the failures is attributable to a rule-driven gate or
rule-driven step. Identify the responsible rule from RULE_GATE_RESULTS /
phase apply outcomes. When multiple rules contributed, list each as its own
D-sub-option (D1, D2, …).
When the user replies "continue": Re-run the verification step for the current phase. If now GREEN, mark the phase complete and proceed to the next phase.
When the user replies "retry phase [name]": Re-run that phase's fix step (not the version pin step — only the code/gem fix step) from the beginning, then re-verify.
When the user replies "disable rule ":
Invoke /ruby-upgrade-toolkit:rules disable <id> (via the rules skill — same
file edit, same confirmation semantics as if the user called it directly).
Reload the rules file. Retry the phase from its verify step. Record in the
final summary that rule <id> was disabled mid-run so it shows up in the
"Manual steps still required" section (suggest re-enabling and re-running
when the underlying issue is addressed).
When the user replies "abort": Print a summary of what was completed and what was not. Do not roll back any changes. Exit.