Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Audit asdf-installed Ruby versions against project pins and remove unreferenced ones. Dry-run by default.
.claude/skills/joshukraine-ruby-gc/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 109% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 103% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 80% | 0% |
Garbage-collect asdf-installed Ruby versions that nothing on the system references.
Dry-run by default. Without --apply, this is a read-only report and you must not uninstall anything.
$ARGUMENTS — pass --apply to enable the uninstall step. Absent that flag, stop after the report.--apply enables deletion; it does not authorize it. You still show the full table and get an explicit yes first.These are non-negotiable. The cost of a wrong delete is a broken app plus a slow rebuild; the cost of a wrong keep is a few hundred MB.
~/.tool-versions.bashasdf list ruby du -sh ~/.asdf/installs/ruby/*
bashcat ~/.tool-versions
This is a symlink into ~/dotfiles/asdf/.tool-versions (the real dotfiles). Note that ~/code/my_repos/dotfiles/ and ~/dotfiles.bak/ are stale clones carrying outdated pins — ignore them as authorities on the global version, though their pins still count under rule 2 until the user cleans them up.
bashfind ~ -type f \( -name .tool-versions -o -name .ruby-version \) \ -not -path '*/node_modules/*' -not -path '*/.git/*' \ -not -path '*/Library/*' -not -path '*/.Trash/*' \ -not -path '*/vendor/*' -not -path '*/.asdf/*' \ -not -path '*/.local/share/nvim/*' -not -path '*/.local/share/zap/*' \ -not -path '*/.local/share/neobean/*' \ -exec grep -H . {} + 2>/dev/null
Do not add -maxdepth. Projects nest deeper than seems reasonable — code/active/eto/ComixDistroBase/comix_distro/ is five levels below ~/code, and a -maxdepth 4 scan silently misses it while still returning plenty of plausible-looking output.
Scan all of ~, not just ~/code. Real projects live in ~/Sites/ too — that's where the legacy ETO sites pinning Ruby 2.7.4 are.
The plugin-directory exclusions are deliberate: nvim and zsh plugins vendor their own .tool-versions (bullets.vim pins ruby 3.1.0, neotest-rspec pins 3.1.1, a zap plugin pins 2.5.3). Those are library artifacts, not projects, and shouldn't hold an install hostage.
~/dotfiles/asdf/.asdfrc sets legacy_version_file = yes, and asdf-ruby's bin/list-legacy-filenames echoes .ruby-version Gemfile. So a Gemfile is a version pin on this system, exactly like a .ruby-version. Verify both facts each run rather than trusting this note — if legacy_version_file is ever turned off, Gemfiles stop counting.
bashfind ~ -type f -name Gemfile \ -not -path '*/node_modules/*' -not -path '*/.git/*' \ -not -path '*/Library/*' -not -path '*/.Trash/*' \ -not -path '*/vendor/*' -not -path '*/.asdf/*' \ -not -path '*/.local/share/*' \ -exec grep -HE '^\s*ruby\s' {} + 2>/dev/null
Rule 4 is only enforceable against a known-good baseline. Measured 2026-07-17, immediately after a cleanup run:
| Signal | Baseline | Treat as a broken scan if... | |---|---|---| | .ruby-version + .tool-versions files found | 58 | fewer than ~40 | | .tool-versions carrying a ruby line | 21 | fewer than ~14 | | Gemfiles with any ruby line | 113 | fewer than ~80 | | Gemfiles with an exact ruby pin | 10 | zero |
Counts drift as projects come and go, so treat them as an order-of-magnitude check, not an assertion — a 20% wobble is normal, a 60% drop is a bug. The landmark checks below are the real test, because they fail loudly rather than plausibly:
~/Sites/ — 13 ruby pins live there, including the legacy ETO sites on 2.7.4. Zero hits under ~/Sites means the scan never left ~/code.~/code/active/eto/ComixDistroBase/comix_distro/. Missing it means a depth limit crept in.~ (~/code/active/eto/ETOWebReboot/legacy/*/.tool-versions). Any -maxdepth silently truncates this.Note the Gemfile ratio: 113 files carry a ruby line but only 10 are real pins. That gap is the ~100 prag-studio Gemfiles pinning ruby '>= 3.0.0'. If exact pins ever approach the total, the parse is wrongly counting range specifiers — that inflates the keep list and makes the skill useless rather than dangerous. Both failure directions are worth catching.
Five formats, all present on this system:
| File | Example contents | Extract | |---|---|---| | .ruby-version | ruby-4.0.6 | strip the ruby- prefix → 4.0.6 | | .ruby-version | 3.1.2 | use as-is | | .tool-versions | ruby 3.1.2 | the ruby line only — the file also carries nodejs, python, lua, neovim | | Gemfile | ruby "3.1.2" | → 3.1.2 — a real pin | | Gemfile | ruby '>= 3.0.0' or ruby file: ".ruby-version" | not a pin — ignore |
A .tool-versions line may list multiple fallback versions (python 3.12.4 2.7.18); if a ruby line ever does, every version listed counts as pinned.
Only an exact Gemfile version pins anything. parse-legacy-file's sed pipeline ends with /^[^0-9]/d, which drops any result not starting with a digit — so >= 3.0.0 yields nothing, and file: ".ruby-version" defers to the .ruby-version already scanned in step 3. This matters: ~100 prag-studio Gemfiles say ruby '>= 3.0.0', and counting them would wrongly pin versions nothing uses. Mirror the plugin's real behavior; don't invent stricter or looser rules.
Sort delete candidates into two tiers, and present them separately:
~/code/archives/), throwaway sandboxes (~/code/rails/, ~/code/fly/), other people's dotfiles (~/code/nathan/, ~/code/kelsie/), or stale dotfile copies. Technically pinned, plausibly disposable — the user decides, per version, every run. Never fold these into the safe tier.Show a table: version, size, keep/delete, and the concrete reason (a pin path, or "no pins found"). Include the total reclaimable.
Also report pins referencing versions that aren't installed (e.g. 3.3.4 has pins but no install). It means those projects are already broken, and it's a useful signal that a pin doesn't prove a project is alive.
State the known gaps every run, so the user can calibrate:
~ is scanned — anything on an external volume is invisible..ruby-version, .tool-versions, and Gemfile. A project selecting its Ruby some other way (a wrapper script, a Docker image, direnv) would not show up.CHECKPOINT: stop here unless --apply was passed. Report and end.
--apply + explicit confirmation)Confirm the exact list with the user, then per version:
bashasdf uninstall ruby <version>
Report each result. Afterwards, re-run asdf list ruby and du -sh ~/.asdf/installs/ruby to show the new state and actual space reclaimed.
/create-bump/create-bump is a project-local skill in the asdf working repo (~/code/active/asdf/.claude/skills/create-bump/), not part of dotfiles — it won't appear in this repo.
It uninstalls only the single MRI version it installed for testing — safe, because it created that version minutes earlier and nothing can be pinned to it yet. It must not call this skill or do fleet-wide cleanup; a routine ruby-build bump should never be able to delete an app's Ruby. Run /ruby-gc deliberately, on its own.
That constraint is enforced where it binds — in /create-bump's own step 8, since a skill can only follow rules written in the file it reads. The note here is orientation, not enforcement. If /create-bump is ever rewritten, re-check that its step 8 still carries the no-fleet-cleanup rule.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 18,268 | 22,325 | +22% | 1 | 1 | 0% | 3,274 | 3,354 | +2% | 0 | 0 | — |
case-02 | fail→fail | 18,960 | 3,859 | -80% | 1 | 1 | 0% | 3,561 | 2,962 | -17% | 0 | 0 | — |
case-03 | pass→fail | 17,063 | 9,048 | -47% | 1 | 1 | 0% | 3,410 | 3,027 | -11% | 0 | 0 | — |
case-04 | fail→pass | 8,801 | 10,594 | +20% | 1 | 1 | 0% | 1,500 | 3,132 | +109% | 0 | 0 | — |
case-05 | fail→fail | 7,209 | 7,449 | +3% | 1 | 1 | 0% | 1,234 | 3,399 | +175% | 0 | 0 | — |
case-06 | fail→fail | 8,697 | 8,620 | -1% | 1 | 1 | 0% | 1,416 | 2,902 | +105% | 0 | 0 | — |
case-07 | pass→pass | 10,433 | 4,811 | -54% | 1 | 1 | 0% | 1,651 | 3,317 | +101% | 0 | 0 | — |
case-08 | fail→pass | 8,758 | 3,506 | -60% | 1 | 1 | 0% | 1,511 | 3,065 | +103% | 0 | 0 | — |
case-09 | pass→pass | 12,938 | 3,705 | -71% | 1 | 1 | 0% | 2,176 | 3,127 | +44% | 0 | 0 | — |
case-10 | fail→pass | 11,758 | 6,394 | -46% | 1 | 1 | 0% | 1,901 | 3,541 | +86% | 0 | 0 | — |
case-11 | fail→pass | 12,744 | 5,256 | -59% | 1 | 1 | 0% | 1,865 | 3,377 | +81% | 0 | 0 | — |
case-12 | pass→pass | 11,170 | 3,258 | -71% | 1 | 1 | 0% | 1,550 | 2,933 | +89% | 0 | 0 | — |
case-13 | fail→pass | 10,838 | 2,866 | -74% | 1 | 1 | 0% | 1,604 | 2,886 | +80% | 0 | 0 | — |
case-14 | pass→pass | 10,700 | 5,094 | -52% | 1 | 1 | 0% | 1,555 | 3,237 | +108% | 0 | 0 | — |
case-15 | fail→pass | 5,772 | 4,249 | -26% | 1 | 1 | 0% | 884 | 3,005 | +240% | 0 | 0 | — |
case-16 | fail→pass | 10,740 | 3,389 | -68% | 1 | 1 | 0% | 1,660 | 3,050 | +84% | 0 | 0 | — |
case-17 | pass→pass | 10,680 | 3,088 | -71% | 1 | 1 | 0% | 1,669 | 3,016 | +81% | 0 | 0 | — |
case-18 | pass→pass | 9,834 | 2,781 | -72% | 1 | 1 | 0% | 1,884 | 2,933 | +56% | 0 | 0 | — |
case-19 | pass→pass | 7,129 | 2,750 | -61% | 1 | 1 | 0% | 1,163 | 2,766 | +138% | 0 | 0 | — |
case-20 | fail→pass | 12,040 | 3,516 | -71% | 1 | 1 | 0% | 2,178 | 2,972 | +36% | 0 | 0 | — |
case-21 | pass→pass | 11,164 | 3,781 | -66% | 1 | 1 | 0% | 1,600 | 3,140 | +96% | 0 | 0 | — |
case-22 | pass→fail | 9,840 | 2,699 | -73% | 1 | 1 | 0% | 1,638 | 2,863 | +75% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 20 counted toward the lift figure. The other 2 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +27 percentage points is the difference between those two pass rates over the 20 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.