Purpose
Thin check-centric setup per the uniform setup contract (docs/PLUGIN-PHILOSOPHY.md
"Setup is explicit and repeatable" in the marketplace repository): check inspects and
reports, apply resolves. This plugin owns no consumer-project configuration. Rules come
from the repository's own Ruff config, and the only tunable is the native userConfig
toggle, so apply is guidance-and-verify, with exactly one write path: the explicitly
invoked apply install-ruff install into the repo's existing managed environment
described below.
Action routing: no argument or check runs the check; apply runs the check first, then
remediation; apply install-ruff additionally authorizes the consumer-repo install
described below. All are non-interactive. Never prompt when the action is given.
check (read-only)
The hook script (${CLAUDE_PLUGIN_ROOT}/hooks/ruff-format.sh) is the single source of
truth for what it requires and how it resolves things.
Read it first. Probe what it actually does, don't recite this file. Then run each probe via
Bash and report a PASS/FAIL/INFO table with one remediation line per FAIL. Do not modify anything.
When the plugin's toggle is disabled, every prerequisite absence downgrades from FAIL to
INFO. The hook exits through its enabled-gate before probing anything, so a deliberately
disabled plugin is not broken. Report the probes informationally and note that re-enabling
restores the FAIL semantics.
- Bash version. Check against the hook's documented floor (README Requirements),
noting any features the hook degrades without (for example telemetry's
EPOCHREALTIME,
Bash 5.0+).
jq. command -v jq. FAIL if absent: the hook then skips with a visible
once-per-session notice instead of formatting.
- Ruff binary. Resolve it exactly the way the hook's resolution code does: its
repo-managed virtual-environment walk (the exact
.venv interpreter paths it tests for
the current platform, walking up from the edited file toward the repo root) and then
PATH. Test only what the hook tests. A binary the hook would not accept must not PASS
here. FAIL when nothing the hook would resolve is present while a Ruff config governs the
repo; the hook then emits a visible once-per-session skip notice instead of formatting.
- Consumer Ruff config. Mirror the hook's opt-in walk: it stops at the FIRST
(closest) governing config found walking from the edited file's directory up to the repo
root, honoring Ruff's own same-directory precedence and counting a
pyproject.toml only
when it carries a [tool.ruff] section or any [tool.ruff.*] subtable such as
[tool.ruff.lint]. Read the hook for the exact names and the section test; its test is
the authority. Report the governing config the walk discovers, or INFO that none exists.
Absence is the opt-out by design, so the plugin is inert (INFO, not FAIL), matching the
README's "ships no rules of its own" stance.
- Hook toggle. Report the effective
ruff_format_enabled value:
${user_config.ruff_format_enabled} (unexpanded or empty means default true).
- Hook registration. INFO: confirm the plugin is enabled for this project
(
/plugin → Installed) rather than parsing settings files.
apply (idempotent)
Run check, then for each FAIL offer the resolution. Never install anything without the
consumer's explicit go-ahead in the invocation. apply install-ruff installs Ruff only
into a managed Python environment the repo already uses, never by creating one and never
globally, mirroring how the hook resolves the binary. Resolve the target from what the repo
already declares:
Principles, in order. They decide every case, whatever the tool:
- Identify the repo's dependency manager from its own markers. A lockfile or a
pyproject.toml tool section (uv, Poetry, Pipenv, PDM, Hatch, …). Recognize the tool
from what the repo declares; don't assume from an enumerated list.
- Record through the manager, never around it. A managed project gets Ruff via that
tool's own dev-dependency add command (e.g.
uv add --dev ruff,
poetry add --group dev ruff, pipenv install --dev ruff, pdm add -d ruff) so the
manifest and lockfile record it. A bare pip install into its environment is state
the tool's next sync or clean silently removes.
- Never create or mutate an environment. When no environment exists yet, use the
tool's record-only mode when it has one (e.g.
uv add --dev ruff --no-sync. Plain
uv add syncs and would create .venv) and hand the sync/install step to the
consumer as their own command; when the tool's add command cannot avoid
creating/instantiating an environment, don't run it. Give it as guidance instead.
- Only where the hook resolves. The hook resolves repo-ancestor
.venv interpreters
or PATH, nothing else. Tools that default their environment to a cache directory
(Poetry, Pipenv, and any similar) must have an in-project environment confirmed first
(the tool's own config/env answers, e.g. poetry config virtualenvs.in-project,
PIPENV_VENV_IN_PROJECT); otherwise guide (enable in-project mode + recreate, or put
ruff on PATH) rather than installing somewhere the hook never looks.
- Bare
pip install only into a plain existing .venv with no manager markers of
any kind. State the change and target environment before running.
- Ambiguity stops. No environment plus no recognized manager, or conflicting
signals → guidance only, anchored on the README's astral install URL
(
https://docs.astral.sh/ruff/installation/), matching the hook's own skip-notice
text.
After ANY remediation, re-run the relevant check probe and report its actual result.
Never claim resolved on the install command's exit code alone. For everything else apply
only points:
- missing
jq / Bash: platform install instructions from the README Requirements section;
this skill never installs system packages.
- toggle off: reconfigure through Claude Code's native flow, per the marketplace's
plugin-reconfiguration convention
(https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/plugin-reconfiguration/README.md,
which owns the verified-version record): interactive
/plugin configure ruff-format@<marketplace>
any time, or headless claude plugin install ruff-format@<marketplace> -s <scope> --config ruff_format_enabled=true
(repeatable per key) — against an already-installed plugin it prints already installed and
still writes the value. Do not uninstall to reconfigure: that drops this plugin's entire
stored pluginConfigs entry, resetting every option in the README's Options reference to its
manifest default. -s defaults to user; pass the scope claude plugin list reports, and run
from that project's directory for a project/local scope, or the write lands at a scope that
does not load. This skill never writes user settings or pluginConfigs. Afterwards rerun
check in a fresh session — the rendered ${user_config.*} is injected at skill load and
each hook's CLAUDE_PLUGIN_OPTION_* is fixed at session start, so a same-session check still
reports the OLD value; report the observed effective value, never an unobserved change.
- no Ruff config: offer to create a minimal Ruff config in the repository root only when
explicitly asked. The plugin imposes no rules of its own.
Re-running apply after everything passes changes nothing and reports "already configured".
What this skill does NOT do
- Run the formatter. Editing any
.py/.pyi file exercises the hook end-to-end.
- Write the plugin cache, Claude Code user settings, or
pluginConfigs.
- Create a virtual environment, install Ruff globally, or install outside a managed
environment the repo already uses.
- Download or execute tools during
check; network use happens only in an explicitly
requested apply install-ruff inside the consumer repository.
1---2name: setup-263description: Verify the ruff-format hook's runtime prerequisites and configuration for this repository. Use when: 'set up ruff-format', 'configure ruff-format', 'is ruff-format working', formatting silently isn't happening, or the hook reported a missing prerequisite. Actions: check (read-only verification, default) | apply (resolve what check found). Re-runnable and safe.4---56## Purpose78Thin check-centric setup per the uniform setup contract (`docs/PLUGIN-PHILOSOPHY.md`9"Setup is explicit and repeatable" in the marketplace repository): `check` inspects and10reports, `apply` resolves. This plugin owns no consumer-project configuration. Rules come11from the repository's own Ruff config, and the only tunable is the native `userConfig`12toggle, so `apply` is guidance-and-verify, with exactly one write path: the explicitly13invoked `apply install-ruff` install into the repo's existing managed environment14described below.1516Action routing: no argument or `check` runs the check; `apply` runs the check first, then17remediation; `apply install-ruff` additionally authorizes the consumer-repo install18described below. All are non-interactive. Never prompt when the action is given.1920## `check` (read-only)2122The hook script (`${CLAUDE_PLUGIN_ROOT}/hooks/ruff-format.sh`) is the single source of23truth for what it requires and how it resolves things.2425**Read it first.** Probe what it actually does, don't recite this file. Then run each probe via26Bash and report a PASS/FAIL/INFO table with one remediation line per FAIL. Do not modify anything.2728When the plugin's toggle is disabled, every prerequisite absence downgrades from FAIL to29INFO. The hook exits through its enabled-gate before probing anything, so a deliberately30disabled plugin is not broken. Report the probes informationally and note that re-enabling31restores the FAIL semantics.32331. **Bash version.** Check against the hook's documented floor (README Requirements),34 noting any features the hook degrades without (for example telemetry's `EPOCHREALTIME`,35 Bash 5.0+).362. **`jq`.** `command -v jq`. FAIL if absent: the hook then skips with a visible37 once-per-session notice instead of formatting.383. **Ruff binary.** Resolve it exactly the way the hook's resolution code does: its39 repo-managed virtual-environment walk (the exact `.venv` interpreter paths it tests for40 the current platform, walking up from the edited file toward the repo root) and then41 `PATH`. Test only what the hook tests. A binary the hook would not accept must not PASS42 here. FAIL when nothing the hook would resolve is present while a Ruff config governs the43 repo; the hook then emits a visible once-per-session skip notice instead of formatting.444. **Consumer Ruff config.** Mirror the hook's opt-in walk: it stops at the FIRST45 (closest) governing config found walking from the edited file's directory up to the repo46 root, honoring Ruff's own same-directory precedence and counting a `pyproject.toml` only47 when it carries a `[tool.ruff]` section or any `[tool.ruff.*]` subtable such as48 `[tool.ruff.lint]`. Read the hook for the exact names and the section test; its test is49 the authority. Report the governing config the walk discovers, or INFO that none exists.50 Absence is the opt-out by design, so the plugin is inert (INFO, not FAIL), matching the51 README's "ships no rules of its own" stance.525. **Hook toggle.** Report the effective `ruff_format_enabled` value:53 `${user_config.ruff_format_enabled}` (unexpanded or empty means default `true`).546. **Hook registration.** INFO: confirm the plugin is enabled for this project55 (`/plugin` → Installed) rather than parsing settings files.5657## `apply` (idempotent)5859Run `check`, then for each FAIL offer the resolution. Never install anything without the60consumer's explicit go-ahead in the invocation. `apply install-ruff` installs Ruff **only61into a managed Python environment the repo already uses**, never by creating one and never62globally, mirroring how the hook resolves the binary. Resolve the target from what the repo63already declares:6465Principles, in order. They decide every case, whatever the tool:66671. **Identify the repo's dependency manager from its own markers.** A lockfile or a68 `pyproject.toml` tool section (uv, Poetry, Pipenv, PDM, Hatch, …). Recognize the tool69 from what the repo declares; don't assume from an enumerated list.702. **Record through the manager, never around it.** A managed project gets Ruff via that71 tool's own dev-dependency add command (e.g. `uv add --dev ruff`,72 `poetry add --group dev ruff`, `pipenv install --dev ruff`, `pdm add -d ruff`) so the73 manifest and lockfile record it. A bare `pip install` into its environment is state74 the tool's next sync or clean silently removes.753. **Never create or mutate an environment.** When no environment exists yet, use the76 tool's record-only mode when it has one (e.g. `uv add --dev ruff --no-sync`. Plain77 `uv add` syncs and would create `.venv`) and hand the sync/install step to the78 consumer as their own command; when the tool's add command cannot avoid79 creating/instantiating an environment, don't run it. Give it as guidance instead.804. **Only where the hook resolves.** The hook resolves repo-ancestor `.venv` interpreters81 or `PATH`, nothing else. Tools that default their environment to a cache directory82 (Poetry, Pipenv, and any similar) must have an in-project environment confirmed first83 (the tool's own config/env answers, e.g. `poetry config virtualenvs.in-project`,84 `PIPENV_VENV_IN_PROJECT`); otherwise guide (enable in-project mode + recreate, or put85 `ruff` on `PATH`) rather than installing somewhere the hook never looks.865. **Bare `pip install` only into a plain existing `.venv`** with no manager markers of87 any kind. State the change and target environment before running.886. **Ambiguity stops.** No environment plus no recognized manager, or conflicting89 signals → guidance only, anchored on the README's astral install URL90 (`https://docs.astral.sh/ruff/installation/`), matching the hook's own skip-notice91 text.9293After ANY remediation, re-run the relevant `check` probe and report its actual result.94Never claim resolved on the install command's exit code alone. For everything else `apply`95only points:9697- missing `jq` / Bash: platform install instructions from the README Requirements section;98 this skill never installs system packages.99- toggle off: reconfigure through Claude Code's native flow, per the marketplace's100 plugin-reconfiguration convention101 (<https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/plugin-reconfiguration/README.md>,102 which owns the verified-version record): interactive `/plugin configure ruff-format@<marketplace>`103 any time, or headless `claude plugin install ruff-format@<marketplace> -s <scope> --config ruff_format_enabled=true`104 (repeatable per key) — against an already-installed plugin it prints `already installed` and105 still writes the value. Do **not** uninstall to reconfigure: that drops this plugin's entire106 stored `pluginConfigs` entry, resetting every option in the README's Options reference to its107 manifest default. `-s` defaults to `user`; pass the scope `claude plugin list` reports, and run108 from that project's directory for a `project`/`local` scope, or the write lands at a scope that109 does not load. This skill never writes user settings or `pluginConfigs`. Afterwards rerun110 `check` in a **fresh session** — the rendered `${user_config.*}` is injected at skill load and111 each hook's `CLAUDE_PLUGIN_OPTION_*` is fixed at session start, so a same-session `check` still112 reports the OLD value; report the observed effective value, never an unobserved change.113- no Ruff config: offer to create a minimal Ruff config in the repository root only when114 explicitly asked. The plugin imposes no rules of its own.115116Re-running `apply` after everything passes changes nothing and reports "already configured".117118## What this skill does NOT do119120- Run the formatter. Editing any `.py`/`.pyi` file exercises the hook end-to-end.121- Write the plugin cache, Claude Code user settings, or `pluginConfigs`.122- Create a virtual environment, install Ruff globally, or install outside a managed123 environment the repo already uses.124- Download or execute tools during `check`; network use happens only in an explicitly125 requested `apply install-ruff` inside the consumer repository.