skills/pr-preflight/SKILL.md
Exhaustive pre-PR audit that front-runs code review tools by catching the same issues Copilot/reviewers find iteratively, across any stack.
npx skillsauth add arndvs/ctrlshft pr-preflightInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
/preflight)Output "Read PR Preflight skill." to chat to acknowledge you read this file.
Run this before every PR open or update. The goal is a review where the automated reviewer finds nothing, because you found it first.
This skill is diff-aware: it knows what changed, audits those files completely (not just the diff lines), and looks for the same classes of issue that iterative review tools catch one-at-a-time.
gh pr create or before pushing a PR updateBefore anything else, map what this PR actually touches.
# What branch and base?
git branch --show-current
git log --oneline main..HEAD 2>/dev/null || git log --oneline HEAD~5..HEAD
# What files changed?
git diff --name-only main..HEAD 2>/dev/null || git diff --name-only HEAD~1..HEAD
# Full diff (for context)
git diff main..HEAD 2>/dev/null || git diff HEAD~1..HEAD
From this, answer:
Do not skip this step. Every later phase uses this map.
Detect what's present and run the right tools. Do not run tools for stacks that aren't here.
# Detect stack
ls package.json tsconfig.json pyproject.toml Cargo.toml go.mod \
Makefile *.sh hooks/ 2>/dev/null | sort
# Node / TypeScript / React
if [[ -f package.json ]]; then
if [[ -f tsconfig.json ]]; then
echo "=== TypeScript ===" && npx tsc --noEmit 2>&1; tsc_exit=$?; echo "(exit $tsc_exit)" | tail -20
fi
if jq -e '.scripts.lint' package.json &>/dev/null; then
echo "=== Lint ===" && npm run lint 2>&1; lint_exit=$?; echo "(exit $lint_exit)" | tail -20
fi
if jq -e '.scripts.test' package.json &>/dev/null; then
echo "=== Tests ===" && npm test -- --passWithNoTests 2>&1; test_exit=$?; echo "(exit $test_exit)" | tail -30
fi
fi
# Bash
if find . -name '*.sh' -not -path '*/.git/*' | grep -q .; then
if ! command -v shellcheck &>/dev/null; then
echo "SKIP: shellcheck not installed"
else
find . -name '*.sh' -not -path '*/.git/*' | \
xargs shellcheck --severity=warning 2>&1; sc_exit=$?; echo "(exit $sc_exit)"
fi
fi
# Python
if [[ -f pyproject.toml ]] || find . -name '*.py' -not -path '*/.git/*' | grep -q .; then
if command -v ruff &>/dev/null; then
echo "=== Ruff ===" && ruff check . 2>&1; ruff_exit=$?; echo "(exit $ruff_exit)" | tail -20
fi
if command -v mypy &>/dev/null; then
echo "=== Mypy ===" && mypy . 2>&1; mypy_exit=$?; echo "(exit $mypy_exit)" | tail -20
fi
fi
Every tool failure is blocking. Do not proceed past Phase 2 with red type errors, failing tests, or shellcheck errors. Fix those first — reviewers will find them and they create noise that hides real feedback.
This is the phase that replaces iterative review cycles.
For each file that changed (from Phase 1), read the entire file — not just the diff. Reviewers see the whole file. You need to see what they see.
Apply the full codebase-audit categories to each changed file:
sudo -E, env -i, --ignore-environment?;, &&, ||)?Now look at only what changed, with fresh eyes.
git diff main..HEAD 2>/dev/null || git diff HEAD~1..HEAD
For each hunk in the diff:
Does the change make sense in isolation?
Read each + line. Could a reviewer misunderstand what it does? If yes, it needs a comment — not because comments are required, but because confusion causes review threads.
Are there leftover artifacts?
console.log, echo, print statementsAre there missing pieces?
settings.json?Naming and clarity
Read the PR as if you are the reviewer seeing it for the first time. Ask exactly the questions a good reviewer would ask.
For every changed file, ask:
For the PR as a whole, ask:
If you find yourself answering any of these with "well, it depends" or "kind of" — that's a finding.
Run the mechanical checks that are easy to miss.
# No uncommitted changes sneaking in
git status --porcelain
# No accidentally staged secrets
git diff --cached | grep -iE 'SECRET|PASSWORD|API_KEY|TOKEN|PRIVATE_KEY' | head -5 || true
git diff HEAD | grep -iE 'SECRET|PASSWORD|API_KEY|TOKEN|PRIVATE_KEY' | head -5 || true
# Branch is up to date with base
git fetch origin main 2>/dev/null || true
git log --oneline HEAD..origin/main 2>/dev/null | head -5
# Commit messages are clean
git log --oneline main..HEAD 2>/dev/null || git log --oneline HEAD~5..HEAD
## PR Preflight Report — <branch name>
**Change summary**: <1 sentence>
**Files changed**: N
**Stack**: React/TS | Bash | Python | Mixed
### Phase 2 — Tool Results
| Tool | Result |
|------|--------|
| TypeScript | ✅ No errors |
| ESLint | ✅ No warnings |
| Tests | ✅ 42 passed |
| ShellCheck | ⚠️ 1 warning (non-blocking) |
### Phase 3 — Full-File Issues
<findings in codebase-audit format: [file:line] description>
### Phase 4 — Diff Issues
<findings: debug statements, missing pieces, naming>
### Phase 5 — Reviewer Simulation
<questions a reviewer would ask, with answers>
### Phase 6 — Pre-Push
| Check | Result |
|-------|--------|
| Clean working tree | ✅ |
| No staged secrets | ✅ |
| Up to date with main | ✅ |
| Commit messages | ✅ conventional |
**READY FOR REVIEW** (or **BLOCKED: fix N issues first**)
Never request a review without running this first.
The iterative review cycle (fix → push → review → fix → push → review) happens because review is being used as a quality gate. It is not a quality gate — it is a second opinion. Your quality gate is this preflight.
If this finds nothing and the tool checks pass, Copilot will find nothing either. If Copilot still finds something after a clean preflight, add that finding to Phase 3 or 4 so it gets caught next time.
development
Use when implementing UI, checking dark/light mode, or validating animations — adds a visual feedback loop via browser screenshots so frontend changes are verified, not assumed.
development
Use when Claude Code sessions had many manual approval ("press 1") prompts or when auditing hook permissions; identifies which Bash commands required approval.
tools
Use after merging a PR or during periodic cleanup to archive plan-mode files by linking them to merged PRs.
testing
Use when stress-testing a plan against the project's domain model — grills the design, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise.