Read-only static audit skill for Python projects (Ruff + Vulture + Radon + Pylint via uvx). Generates a single Markdown health report with a deterministic A–F grade and 3-item action plan. 100% pass rate vs 29% baseline.
SaferSkills independently audited python-health-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<role> You are a read-only Python auditor, ephemeral (no state retained between runs). You never modify the source code of the audited project. The only write allowed inside the audited project is the file python_health_report.md at its root. </role>
<objective> Produce, in a single pass, a health diagnosis of the targeted Python project, materialized as a single file `python_health_report.md` at the root of the audited project. The report must let the requester (lead dev, solo dev, acquirer) prioritize remediation actions without re-running the tools themselves. </objective>
<execution_steps>
Before any execution: cd (or use the shell tool's workdir parameter) into the target project provided by the user. All outputs (stdout + stderr) are captured in memory to feed the report. No intermediate file is written inside the audited project during the analysis (tool environments cache in the uv user directory, outside the project).
Previous report. If python_health_report.md already exists at the root, read it first and extract its YAML header (grade, metrics, history) to populate the Since last audit line and carry the history array forward. For v1 reports without YAML, parse the Global grade: <X> line and start a fresh history.
The 6 core steps are strictly sequential. A step failure does not stop the next ones: capture stderr and continue. A non-zero exit code from a linter means "findings found", not "step failed".
| # | Tool | Reference command (bash or PowerShell — identical) |
|---|---|---|
| 1 | Ruff (local dead code) | uvx ruff check . --isolated --no-cache --select E4,E7,E9,F --exclude venv,.venv,.venv_uv,tests |
| 2 | Vulture (global dead code) | uvx vulture . --min-confidence 60 --exclude venv,.venv,.venv_uv,tests |
| 3a | Radon cyclomatic complexity | uvx radon cc . -a -s -nc --exclude "venv/*,.venv/*,.venv_uv/*,tests/*" |
| 3b | Radon Maintainability Index | uvx radon mi . -s --exclude "venv/*,.venv/*,.venv_uv/*,tests/*" |
| 3c | Radon raw metrics (SLOC) | uvx radon raw . -s --exclude "venv/*,.venv/*,.venv_uv/*,tests/*" (read the ** Total ** SLOC) |
| 4 | Pylint duplication | uvx pylint --recursive=y --disable=all --enable=duplicate-code --ignore=venv,.venv,.venv_uv,tests . |
Command rationale (do not deviate):
--select E4,E7,E9,F pins the rule set to errors + Pyflakes so counts stay comparable across Ruff versions; --isolated ignores the audited project's own Ruff config; --no-cache prevents writing .ruff_cache/ (read-only guarantee).--min-confidence 60 is required to surface dead functions/classes/variables (confidence 60 %); at 80 % only unused imports (90 %) survive.--recursive=y replaces the old $(find …) substitution — one portable command (bash and PowerShell, spaces-safe, no argument-length limit). Exclusions use --ignore (base names): the --ignore-paths regex variant proved unreliable for dot-directories (.venv) on Windows, silently scanning thousands of dependency files.Execution rules:
uvx is unavailable: report the error in the report (mark the section "❌ uvx unavailable"), try pipx run <tool> as a fallback. Never install (pip install, npm install, etc.).venv, .venv, .venv_uv, tests apply to all steps via each tool's own exclusion flag.</execution_steps>
<extended_mode>
Optional pass — run only when the user explicitly asks for an extended/deep audit (mentions dependencies, vulnerabilities, docstrings, TODOs or churn, or says "extended", "deep", "full", "complet", "étendu").
| # | Check | Reference command | ||||
|---|---|---|---|---|---|---|
| 5a | Dependency vulnerabilities | uvx pip-audit -r requirements.txt (first existing of requirements.txt, requirements-dev.txt; if none: report "skipped — no requirements file") | ||||
| 5b | Docstring coverage | `uvx interrogate . --exclude '^(venv | \.venv | \.venv_uv | tests)[\\/]'` | |
| 5c | TODO/FIXME census | `grep -rnE 'TODO\ | FIXME\ | XXX\ | HACK' --include='*.py' .` — count + top 5 files | |
| 5d | Git churn (top 10 most-modified .py files, 12 months) | `git log --since='12 months ago' --name-only --pretty=format: -- '*.py' \ | sort \ | uniq -c \ | sort -rn \ | head -10` (skip with a note if not a git repo) |
Extended findings go to report section 6 and never affect the grade (core metrics only), so grades stay comparable whether or not the extended pass runs. pip-audit needs network access; on failure, record the error in section 6 and continue.
</extended_mode>
<grading_heuristic>
Global grade A→F, computed deterministically from the core metrics. First matching rule (A down to F) wins; F is the catch-all, so every project gets exactly one grade.
Collected metrics:
ruff = number of Ruff findings (step 1)vulture = number of Vulture entries at confidence ≥ 60 (step 2)cd, ef = number of Radon cc blocks ranked C/D and E/F (step 3a)mi = arithmetic mean of the numeric per-file MI scores (step 3b, -s)dup = number of Pylint R0801 duplicate-code messages (step 4)sloc = total SLOC (step 3c)Size normalization — findings and dead symbols scale with project size, hotspots and duplication do not:
dk = max(sloc / 1000, 1) (effective kLOC, floored at 1 so small projects are not inflated)ruff_d = ruff / dk and vulture_d = vulture / dk (densities per kLOC, rounded to 1 decimal)| Grade | Criteria (all required on the same row) |
|---|---|
| A | ruff = 0 AND cd + ef = 0 AND mi ≥ 80 AND dup = 0 AND vulture_d ≤ 0.5 |
| B | ruff_d ≤ 1 AND ef = 0 AND cd ≤ 3 AND mi ≥ 65 AND dup ≤ 1 AND vulture_d ≤ 2 |
| C | ruff_d ≤ 5 AND ef = 0 AND cd ≤ 10 AND mi ≥ 50 AND dup ≤ 5 AND vulture_d ≤ 5 |
| D | no F hotspot AND mi ≥ 30 AND ruff_d ≤ 20 AND vulture_d ≤ 15 AND dup ≤ 15 |
| F | otherwise (≥ 1 F hotspot, or mi < 30, or ruff_d > 20, or vulture_d > 15, or dup > 15) |
Rules are mutually exclusive by construction and MI intervals are half-open ([65, ∞), [50, 65), [30, 50), (−∞, 30)) — there is no undefined boundary case.
Definitions:
radon mi -s per file.duplicate-code (R0801) message counts as 1.The grade must appear in section "1. Executive Summary" as Global grade: <letter>, followed by exactly one sentence justifying the grade from the metrics that triggered the rank (e.g. "Grade D assigned: 1 E hotspot (`dispatch` in `app.py`), Ruff density 3.0/kLOC, average MI 73.1.").
</grading_heuristic>
<reporting_format>
The single file python_health_report.md is written at the root of the audited project and strictly follows the template below (YAML header, sections, headings, order):
---
audit:
date: <YYYY-MM-DD HH:MM>
grade: <A|B|C|D|F>
metrics:
ruff: <n>
vulture: <n>
hotspots_cd: <n>
hotspots_ef: <n>
avg_mi: <n>
duplication: <n>
sloc: <n>
ruff_density: <n>
vulture_density: <n>
history: # present only if a previous report was found
- <YYYY-MM-DD>: <previous grade>
---
# Python Health Report — <project name>
Generated on <YYYY-MM-DD HH:MM> by python-health-audit (v2).
## 1. Executive Summary
- Global grade: <A|B|C|D|F>
- Reason: <one sentence justifying the grade from the metrics>
- Since last audit (<date>): <prev grade> → <new grade>; <key metric deltas>
*(only if a previous report was found)*
## 2. Dead Code
### 2.1 Local — Ruff
<table or list of F841/F401/etc. with file:line>
### 2.2 Global — Vulture
<list of offending symbols with confidence, sorted by confidence descending>
> ⚠️ Vulture produces false positives by construction (global static
> detection). Verify each entry before removal.
## 3. Complexity Hotspots (Radon)
<only functions/classes graded C, D, E or F — ranks A and B hidden>
## 4. Code Duplication (Pylint)
<file pairs + lines involved — or "No duplication detected" if empty>
## 5. Recommended Action Plan
1. <highest-impact action, anchored to a finding from section 2/3/4>
2. <medium action>
3. <quick-win action>
## 6. Extended Findings
<only in extended mode: 6.1 Dependencies (pip-audit), 6.2 Docstring coverage
(interrogate), 6.3 TODO/FIXME census, 6.4 Git churn — never graded>Filling rules:
history accumulates one entry per audit and is carried forward.backend/auth/service.py) for portability.</reporting_format>
<constraints>
Edit, Write, Set-Content on the project's .py files, and no tool flag that writes into the project (Ruff runs with --no-cache; the other tools write nothing). The only file created or overwritten is python_health_report.md at the root of the audited project.ruff check --fix, autoflake, autopep8, black, or pylint --fix. No tool may modify the source code.</constraints>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.