Ruff bump
Move one repo or a whole fleet onto a new ruff release — predict which rules can fire against each repo's own select list, gate on the resolver seeing the version, then land one reviewed commit per ruleFrom its SKILL.md
npx -y skills add tony/ai-workflow-plugins --skill ruff-bumpAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.3 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it
Bump ruff
Raise the ruff version floor across the repositories in scope and absorb everything the new release surfaces — one commit per rule, each justified against the rule's own documentation, behind the project's own quality gates.
Three references carry the parts that are easy to get wrong. Read all three before acting on anything.
references/release-triage.md— how to work out what a release actually does to a specific repo, and how much care each class of fix needs.references/pin-sites-and-gating.md— every file a version can be pinned in, and what to do when the resolver refuses to see the release.references/default-rule-set.md— measuring and adopting a newly curated default rule set, and curating what it surfaces.
User arguments: $ARGUMENTS
Context
Repository — run this command and read the output:
git remote get-url origin 2>/dev/null || echo "(not a git repository)"
Default branch — run this command and read the output:
git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || git ls-remote --symref origin HEAD 2>/dev/null | sed -n 's|^ref: refs/heads/\([^[:space:]]*\).*|\1|p' || echo "(unknown)"
Currently resolved version — run this command and read the output:
command -v ruff >/dev/null 2>&1 && ruff --version || echo "(not on PATH — resolve through the project's environment)"
Where ruff is pinned here — run this command and read the output:
git grep -nI -- 'ruff' -- ':(glob)**/pyproject.toml' ':(glob)**/requirements*.txt' ':(glob)**/.pre-commit-config.yaml' ':(glob)**/*.tool-versions' ':(glob)**/mise*.toml' ':(glob)**/package.json' ':(glob).github/workflows/*' 2>/dev/null | grep -iv 'tool\.ruff\|ruff\.lint\|# ' | head -40 || echo "(no pins found)"
Effective rule selection — run this command and read the output:
git grep -A40 -- '\[tool\.ruff\.lint\]' -- ':(glob)**/pyproject.toml' 2>/dev/null | head -60 || echo "(no explicit lint configuration — this repo relies on the defaults, so default-set changes DO apply)"
Resolver cooldown settings that could hide a fresh release — run this command and read the output:
{ cat "${UV_CONFIG_FILE:-$HOME/.config/uv/uv.toml}" 2>/dev/null; cat uv.toml 2>/dev/null; } | grep -i 'exclude-newer' || echo "(no cooldown configured)"
Procedure
1. Resolve the target version and its publication time
If the user named a version, use it. Otherwise take the latest stable release. Either way, confirm the version exists on the package index and record its publication timestamp — you need it in phase 3, and a version string written before it is confirmed to exist is the expensive failure this ordering prevents.
2. Establish scope
Default scope is the current repository. --root walks a directory for git repositories; --repo names individual ones; --owner keeps only those whose remote belongs to the given accounts or organisations. Exclude forks — check the fork flag, not the owner name — and exclude repositories you cannot push to. Say plainly which repositories were excluded and why.
Within scope, drop repositories that have no ruff configuration at all, and repositories that already satisfy the target floor. If a branch or worktree of the intended name already exists somewhere in scope, skip that repository rather than clobbering it, and list it as skipped.
3. Research the release once, then intersect it per repository
Follow the triage reference. Read the release's own changelog at its tag, sort every change into the five buckets, then — separately for each repository — intersect those buckets with that repository's effective rule selection to predict exactly which diagnostics it can produce. Repositories that share a configuration share a prediction; do the research once and reuse it.
The intersection is the point of this phase. A release that expands the default rule set produces no new diagnostics for a repository with an explicit select — but that is a finding, not a non-event, and phase 8 is where it gets raised rather than filed under noise. A formatter change that widens which file types are covered applies everywhere regardless of rule selection, and is usually the largest single source of diff.
Dispatch a subagent per repository where the host supports it, since the repositories are independent.
4. Present the plan and wait
Show the predicted work per repository: the rules expected to fire, whether the formatter's scope widens, which pin sites need editing, and whether a resolver gate is needed. With --audit-only, stop here — nothing is written.
5. Isolate
Work on a branch off the remote default branch, in a throwaway worktree, never in the primary checkout and never on the default branch. --branch overrides the branch name. This matters most for repositories that are parked mid-feature; a worktree leaves that work untouched.
6. Gate the resolver, if and only if it is blocked
Only when the resolver genuinely cannot see the target version. Follow the gating reference: narrow to the one package, temporary, marked as such in the file with its removal condition, and landed as its own first commit so it reverts cleanly. Skip this phase entirely when the resolver can already see the release — an exemption nobody needs is a permanent hole in a supply-chain guard.
7. Raise the floor and refresh the lockfile
Edit every pin site the reference enumerates, in one commit. Prefer a floor over an exact pin, except in pre-commit configuration where the tag is exact by design. Refresh the lockfile with the project's own resolver, and confirm the lock actually moved to the target version rather than assuming the constraint was enough. Cite the release notes in the commit body.
8. Surface the default-rule-set decision
If the release changed the default rule set and a repository sets an explicit select, that repository gets no new diagnostics — and is now silently opted out of the vendor's recommended baseline, invisibly and permanently. Do not report this as inert.
Follow the default-rule-set reference: measure each repository's real delta by extending its configured selection with the default codes on the command line, then present the per-repo finding counts alongside the recommended config shape. With --adopt-defaults, carry the change through — the config edit lands as its own commit, before any fix or ignore, and reports the enabled-rule count before and after. Without the flag, report the measurement and stop, so the decision stays the user's.
Whole-linter prefixes are not a substitute for the default codes: a curated default set takes a subset of each linter, so selecting whole prefixes enables far more than the vendor recommends. Measure it rather than assuming, and say what the measurement showed.
9. One rule, one commit
Enumerate the distinct rule codes the linter now reports. For each, in its own commit: apply that rule's safe fixes alone, hand-fix what the autofix leaves, re-run the project's quality checks, and commit with a body that explains why the rule exists, what changed, and — for behavior-changing rules — what the behavior delta is. Close each body with the rule's documentation URL.
A rule that produces a large mechanical diff across many files is still one commit; it is still one rule. Run the formatter last, as its own commit, and if it rewrote file types it never previously covered, say so explicitly — the reviewer needs to know that churn is mechanical rather than authored.
Do not add code comments explaining a lint fix. The commit message carries the rationale, and a comment naming a linter rule ages into noise the moment the rule is renamed or removed. Configuration files are the exception: a temporary resolver exemption must be annotated where it lives.
10. Verify before claiming anything
Run the project's own quality checks — lint, format check, type check, tests — as defined in its AGENTS.md/CLAUDE.md or its CI workflow. Never substitute assumed commands for the ones the project actually runs.
If tests fail, establish whether they fail on the default branch too before attributing the failure to the bump. Pre-existing failures are reported, not fixed and not concealed. Never report green without having read the output that says so.
11. Push, open, watch, record
Push the branch and open a pull request unless --no-pr. The body states which rules moved and links each to its documentation, and — when phase 6 applied — carries an explicit pre-merge instruction to drop the exemption and re-resolve, with the time the block lapses.
Watch CI to a conclusion. Attribute failures before blaming the bump. Then, unless --no-changelog or the repository keeps no changelog, add an entry following that repository's own changelog conventions; a linter floor is development tooling, so it belongs wherever that repository files development-tooling changes, and only if that repository documents such changes at all.
Rules
- No version string is written before the release is confirmed to exist on the index.
- Every pin site in a repository moves in the same commit, or none does.
- One commit per rule. Never bundle rules, and never bundle a rule with the floor bump.
- Unsafe fixes are applied one at a time and reviewed individually, never in bulk.
- A resolver exemption is temporary, narrow, annotated, separately committed, and called out as a pre-merge blocker.
- Suppressions require a stated reason in the commit body; a rule that is broadly wrong for a project belongs in that project's ignore configuration, not scattered inline.
- Report unrelated breakage; fix it only when the bump caused it.
- Repositories you do not own, forks, and existing branches of the target name are out of scope.
Output
Open with a one-line hero (✓ N repos on ruff X.Y.Z or ⚠ Audit only: N repos behind), then exactly these sections:
## Release— what the target version changes, sorted into the triage buckets, with the formatter's scope change called out separately if there is one.## Scope— repositories in scope with their current and target versions, and what was excluded as fork, unowned, already-current, or already-branched.## Default rule set— per repository: whether it is opted out of the vendor's default set, the measured finding delta if it adopted them, and the recommended config shape. Omit this section only when the release did not change the default set.## Per repo— for each: the rules that fired against its selection, the commits made, and the pull request.## Verification— the quality checks run per repository and their real results, with any pre-existing failure named as pre-existing.## Pre-merge— every repository carrying a temporary resolver exemption, and when its block lapses.## Drift— pin sites found disagreeing with each other before the bump, repositories with no lint configuration, and repositories relying on the default rule set.
End with an ask-user-choice panel offering next steps — for example: drop the resolver exemptions now that the block has lapsed, merge the green pull requests, adopt a newly stabilized rule that is currently unselected, or stop here. Skip the panel only in plan mode.
Portability notes
ask-user-choice— present the listed options and wait for the user to pick one. Hosts with a structured multiple-choice tool (Claude Code'sAskUserQuestion) should use it; otherwise print a numbered list and wait for a numbered reply. Never proceed on an assumed answer.$ARGUMENTS— the text the user passed when invoking this skill. If your host does not substitute it, read it as the user's request in the current turn, and ask when there is none.- Bundled files — every relative path in this skill points at a file shipped inside this skill directory. Read them from here, not from the host's plugin tree.
What ships with it: 3 files
19.2 KB alongside SKILL.md
references/
- default-rule-set.md7.7 KB
- pin-sites-and-gating.md5.3 KB
- release-triage.md6.2 KB