Add `/lint` skill with `scripts/check.py`
Add a user-invocable `/lint` skill that wraps `ruff check` with staged-file defaults and piker's `ruff.toml` config via a PEP 723 script. The `ruff.toml` (from prior commit) now enforces piker's `'''` multiline docstring convention with `D2xx` formatting rules: summary on second line (D213), closing quotes on separate line (D209), blank line between summary and description (D205), plus `W` trailing-whitespace and cherry-picked `D4xx` content checks. Deats, - `scripts/check.py`: pure-stdlib wrapper that defaults to staged `.py` files via `git diff --cached`, with `--all`, `--fix`, `--diff`, and `--stats` flags. - calls `ruff` from `$PATH` (works with nixpkgs, nvim-bundled, or pip-installed). - prints clear install guidance if `ruff` not found. - `SKILL.md`: documents usage, common violations (D213, D205, D204), and fix patterns for converting to piker's docstring style. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
parent
ba728ae0aa
commit
d40a19ae4c
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
name: lint
|
||||||
|
description: >
|
||||||
|
Run ruff lint checks on piker Python files for
|
||||||
|
docstring style (D2xx) and code convention
|
||||||
|
compliance. Checks staged files by default.
|
||||||
|
user-invocable: true
|
||||||
|
argument-hint: "[--all|--fix|--stats|paths...]"
|
||||||
|
allowed-tools: Bash(python3 *), Bash(ruff *), Read, Edit
|
||||||
|
---
|
||||||
|
|
||||||
|
# Ruff Lint Checker
|
||||||
|
|
||||||
|
Run piker's ruff config against staged files,
|
||||||
|
specific paths, or the full codebase.
|
||||||
|
|
||||||
|
## Available scripts
|
||||||
|
|
||||||
|
- **`scripts/check.py`** — self-contained (PEP 723)
|
||||||
|
wrapper around `ruff check` that defaults to staged
|
||||||
|
files and uses the project's `ruff.toml`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# check staged Python files (default)
|
||||||
|
python3 scripts/check.py
|
||||||
|
|
||||||
|
# check full codebase
|
||||||
|
python3 scripts/check.py --all
|
||||||
|
|
||||||
|
# auto-fix fixable violations
|
||||||
|
python3 scripts/check.py --fix
|
||||||
|
|
||||||
|
# preview fixes without applying
|
||||||
|
python3 scripts/check.py --diff
|
||||||
|
|
||||||
|
# show per-rule violation counts
|
||||||
|
python3 scripts/check.py --stats
|
||||||
|
|
||||||
|
# check specific files
|
||||||
|
python3 scripts/check.py piker/ui/_style.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common violations
|
||||||
|
|
||||||
|
| Rule | Meaning | Fixable? |
|
||||||
|
|------|---------|----------|
|
||||||
|
| D213 | summary not on 2nd line after `'''` | yes |
|
||||||
|
| D205 | no blank line after summary | no |
|
||||||
|
| D204 | no blank line after class docstring | yes |
|
||||||
|
| D209 | closing quotes not on own line | yes |
|
||||||
|
| D200 | ignored — piker always multiline | n/a |
|
||||||
|
| W291 | trailing whitespace | yes |
|
||||||
|
|
||||||
|
## Fixing D213 (most common)
|
||||||
|
|
||||||
|
Convert this:
|
||||||
|
```python
|
||||||
|
"""Summary on first line."""
|
||||||
|
```
|
||||||
|
|
||||||
|
To piker's `'''` multiline style:
|
||||||
|
```python
|
||||||
|
'''
|
||||||
|
Summary on second line.
|
||||||
|
|
||||||
|
'''
|
||||||
|
```
|
||||||
|
|
||||||
|
For D205, insert a blank line between the summary
|
||||||
|
and description paragraphs inside the docstring.
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = []
|
||||||
|
# ///
|
||||||
|
'''
|
||||||
|
Ruff lint checker for piker docstring and code style.
|
||||||
|
|
||||||
|
Checks staged files by default, or the full `piker/`
|
||||||
|
tree with `--all`. Uses the project's `ruff.toml`.
|
||||||
|
|
||||||
|
'''
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def get_staged_py_files() -> list[str]:
|
||||||
|
'''
|
||||||
|
Return staged Python file paths (added/copied/modified).
|
||||||
|
|
||||||
|
'''
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
'git', 'diff',
|
||||||
|
'--cached',
|
||||||
|
'--name-only',
|
||||||
|
'--diff-filter=ACM',
|
||||||
|
'--', '*.py',
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
f for f in result.stdout.strip().split('\n')
|
||||||
|
if f
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
'''
|
||||||
|
Parse args and run `ruff check` against targets.
|
||||||
|
|
||||||
|
'''
|
||||||
|
ap = argparse.ArgumentParser(
|
||||||
|
description=(
|
||||||
|
"Run ruff check with piker's ruff.toml config.\n"
|
||||||
|
'\n'
|
||||||
|
'Default: checks staged .py files only.\n'
|
||||||
|
'Use --all for the full piker/ tree.'
|
||||||
|
),
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
'paths',
|
||||||
|
nargs='*',
|
||||||
|
help='files/dirs to check (default: staged)',
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
'--all', '-a',
|
||||||
|
action='store_true',
|
||||||
|
dest='check_all',
|
||||||
|
help='check entire piker/ directory',
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
'--fix',
|
||||||
|
action='store_true',
|
||||||
|
help='auto-fix fixable violations',
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
'--diff',
|
||||||
|
action='store_true',
|
||||||
|
help='preview fixes as unified diff (dry-run)',
|
||||||
|
)
|
||||||
|
ap.add_argument(
|
||||||
|
'--stats',
|
||||||
|
action='store_true',
|
||||||
|
help='show per-rule violation counts only',
|
||||||
|
)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
# determine check targets
|
||||||
|
if args.check_all:
|
||||||
|
targets: list[str] = ['piker/']
|
||||||
|
elif args.paths:
|
||||||
|
targets = args.paths
|
||||||
|
else:
|
||||||
|
targets = get_staged_py_files()
|
||||||
|
if not targets:
|
||||||
|
print(
|
||||||
|
'No staged Python files found.\n'
|
||||||
|
'Use --all for full codebase '
|
||||||
|
'or pass file paths.'
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
print(
|
||||||
|
f'Checking {len(targets)} staged file(s)..\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure ruff is available on PATH
|
||||||
|
if not shutil.which('ruff'):
|
||||||
|
print(
|
||||||
|
'Error: `ruff` not found on PATH.\n'
|
||||||
|
'On NixOS: add to flake.nix or '
|
||||||
|
'`nix-shell -p ruff`\n'
|
||||||
|
'Otherwise: `uv sync --group lint` or '
|
||||||
|
'`pip install ruff`'
|
||||||
|
)
|
||||||
|
return 127
|
||||||
|
|
||||||
|
# build ruff command
|
||||||
|
cmd: list[str] = ['ruff', 'check']
|
||||||
|
|
||||||
|
if args.diff:
|
||||||
|
cmd.append('--diff')
|
||||||
|
elif args.fix:
|
||||||
|
cmd.append('--fix')
|
||||||
|
|
||||||
|
if args.stats:
|
||||||
|
cmd.append('--statistics')
|
||||||
|
|
||||||
|
cmd.extend(targets)
|
||||||
|
|
||||||
|
result = subprocess.run(cmd)
|
||||||
|
return result.returncode
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Loading…
Reference in New Issue