| name | code-style |
| description | Use when writing or editing code in this org's Python or JS/TS, especially before committing or opening a PR — and proactively the moment a diff adds an import, an except/catch, or any logging. Enforces the style rules Claude gets wrong by default: import grouping, error-wrapping (no bare except / empty catch), no leftover debug prints, explicit over clever. Runs scripts/check_style.sh (ruff, mypy --strict, eslint + grep guards) which exits nonzero so it drops into a pre-commit hook or CI. |
Code Style
Overview
Claude writes code that passes a default linter and still violates this org's
style — because the violations live in the gaps linters don't fail on out of the
box: a print() left in from debugging, except Exception: pass, imports in the
wrong group, a clever one-liner where an explicit loop belonged. This skill makes
those rules enforceable instead of aspirational. The single source of truth is
scripts/check_style.sh, which wraps the real tools (ruff, mypy strict, eslint)
and adds grep guards for the things those tools ignore by default. It exits nonzero
on any violation, so the same script that you run locally is the one CI runs.
This is a "don't state the obvious" skill: it does not re-teach you Python or
JavaScript. It only calls out the non-default rules and pushes Claude off its
defaults.
When to Use
Use this when:
- About to commit, push, or open a PR with Python or JS/TS changes.
- A diff adds or reorders imports, adds an
except/catch, or adds logging —
these are where the default-vs-org gap shows up most.
- Setting up a repo's pre-commit hook or CI lint step (wire the script in once).
Do NOT use this when:
- You want a behavioral/design critique of the change — that's
adversarial-review.
- You're evaluating test quality (deterministic, behavior-not-implementation) —
that's
testing-practices.
- The repo is a language this script doesn't cover; extend the script rather than
hand-checking, so the rule stays enforceable.
Running the check
scripts/check_style.sh
scripts/check_style.sh src/ pkg/
Each tool layer is skipped gracefully if not installed; the grep guards always
run. Exit code 0 = clean, 1 = at least one violation (with file:line printed).
Wire it as a hook or Action
Because it exits nonzero, enforcement is one line.
Pre-commit (.git/hooks/pre-commit or .pre-commit-config.yaml local hook):
.claude/skills/code-style/scripts/check_style.sh || exit 1
GitHub Action step:
- run: .claude/skills/code-style/scripts/check_style.sh
The non-default rules (what Claude gets wrong)
Read these before editing; the script enforces them, but you should write to them
the first time rather than fixing on the rebound.
Import ordering and grouping
Three groups, blank line between, alphabetical within group: (1) standard library,
(2) third-party, (3) first-party / local. No mixing, no unsorted dumps. ruff check
with the import rule (I) enforces this — keep that rule enabled in ruff.toml.
Claude's default is to append a new import wherever it's convenient; don't.
Error wrapping — the big one
- Never
except: and never bare except Exception: pass. Catch the specific
exception you expect.
- Wrap with context, then re-raise. Add what you were doing
(
raise RuntimeError(f"loading config {path}") from err) so the traceback says
where, not just what. Don't catch only to log-and-continue unless silently
proceeding is genuinely correct.
- No empty JS/TS
catch {}. Handle it or re-throw with context. Swallowing an
error is how a failure becomes a silent wrong answer.
No debug residue
No leftover print(...), console.log/console.debug, breakpoint(), pdb, or
debugger;. Use the logger if the output is meant to stay; otherwise delete it.
The grep guards fail the build on these — they are the most common thing Claude
leaves behind.
Explicit over clever
Prefer the readable form over the compact trick: a named intermediate over a
four-level nested comprehension, an early return over a deep ternary, a plain
loop over reduce gymnastics. The reviewer (human or adversarial-review) should
understand the line on first read.
Gotchas
- Linters pass and the code is still off-style. A fresh ruff/eslint config does
NOT fail on
except: pass or a stray console.log. That's the whole reason for
the grep guards — don't assume "lint is green" means "style is clean".
- Import grouping silently rots. If the
I rule isn't enabled in ruff.toml,
ruff won't sort imports and Claude's append-anywhere habit wins. Verify the rule
is on; the script can't enforce a rule the config disabled.
# noqa / // eslint-disable to make the check pass. Suppressing the rule is
not fixing the violation. Only suppress with a specific code and a comment saying
why; a blanket disable defeats the skill.
- Wrapping an exception without
from. raise X("...") inside an except
loses the original cause. Use raise X("...") from err so the chain survives.
- "It's just a debug print, I'll remove it later." Later is the commit. The
guard fails now precisely so it never ships.
- Reformatting unrelated lines.
ruff format is scoped to your diff intent —
don't let an auto-format reflow the whole file and bury your change. Format the
files you touched, review the diff.
Files
SKILL.md — this file; the non-default rules and how to enforce them.
scripts/check_style.sh — wraps ruff (lint + format), mypy --strict, eslint, and
adds grep guards for bare except / empty catch / debug prints / breakpoints.
Exits nonzero on violation; usable directly as a pre-commit hook or CI step.