This skill should be used when the user asks to "set up eslint", "configure prettier", "add biome", "set up ruff", "configure lint-staged", "add formatter", "configure linter", or needs reference for linter/formatter/lint-staged configuration in their project.
From code-qualitynpx claudepluginhub nthplusio/functional-claude --plugin code-qualityThis skill uses the workspace's default tool permissions.
Configures and explains ShellCheck for static analysis of shell scripts, covering installation, .shellcheckrc, error codes, and CI/CD integration. Use for linting setup, issue fixes, and script quality.
Guides Bats usage for unit testing shell scripts with patterns, fixtures, assertions, setup/teardown. For TDD, CI/CD pipelines, edge cases, and multi-shell validation.
Provides defensive Bash patterns like strict mode, error trapping, variable quoting, safe arrays, and conditionals for reliable production scripts, CI/CD pipelines, and system utilities.
Configuration reference for code quality tools across ecosystems. Use this skill when you need to set up, modify, or troubleshoot specific linting and formatting tools.
Biome is a single tool that replaces ESLint + Prettier — faster, zero-config defaults, consistent formatting.
# Install
npm add -D @biomejs/biome
# Initialize
npx biome init
biome.json:
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
lint-staged config:
{
"*.{js,jsx,ts,tsx,json,jsonc,css}": ["biome check --write --no-errors-on-unmatched"]
}
When to choose Biome over ESLint + Prettier:
ESLint 9 uses flat config (eslint.config.js) instead of the legacy .eslintrc.* format.
npm add -D eslint @eslint/js typescript-eslint
eslint.config.js:
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{
ignores: ['dist/', 'node_modules/', '.next/']
}
);
lint-staged config:
{
"*.{js,jsx,ts,tsx}": ["eslint --fix"]
}
npm add -D prettier
.prettierrc:
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100
}
lint-staged config (with ESLint):
{
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,yml,yaml,css}": ["prettier --write"]
}
When using both, ensure Prettier runs after ESLint to avoid formatting conflicts:
npm add -D eslint-config-prettier
Add to eslint.config.js:
import prettierConfig from 'eslint-config-prettier';
export default [
// ... other configs
prettierConfig, // Must be last to disable conflicting rules
];
Ruff replaces Flake8, isort, Black, and many other Python tools. Single binary, extremely fast.
pip install ruff
ruff.toml (or [tool.ruff] in pyproject.toml):
line-length = 88
target-version = "py312"
[lint]
select = ["E", "F", "I", "N", "UP", "B", "SIM"]
ignore = []
fixable = ["ALL"]
[format]
quote-style = "double"
indent-style = "space"
Usage:
# Lint
ruff check .
# Lint and fix
ruff check --fix .
# Format
ruff format .
lint-staged equivalent (via pre-commit framework):
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
When to choose Ruff over Black + Flake8:
pip install black flake8
pyproject.toml:
[tool.black]
line-length = 88
target-version = ["py312"]
[tool.flake8]
max-line-length = 88
extend-ignore = ["E203", "W503"]
Both ship with Rust via rustup. No installation needed.
Check formatting:
cargo fmt -- --check
Run linter:
cargo clippy -- -D warnings
rustfmt.toml (optional customization):
edition = "2021"
max_width = 100
tab_spaces = 4
use_field_init_shorthand = true
.clippy.toml (optional):
cognitive-complexity-threshold = 30
Git hook commands:
# Pre-commit: format check + lint
cargo fmt -- --check && cargo clippy -- -D warnings
# Pre-push: full check + tests
cargo check && cargo test
# Install golangci-lint
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Or: brew install golangci-lint
.golangci.yml:
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
linters-settings:
gofmt:
simplify: true
run:
timeout: 5m
Git hook commands:
# Pre-commit: format check + lint
gofmt -l . | grep -q . && echo "Files need formatting" && exit 1
golangci-lint run
# Pre-push: vet + tests
go vet ./...
go test ./...
lint-staged runs linters/formatters only on staged files, making pre-commit hooks fast regardless of project size.
npm add -D lint-staged
lint-staged config can live in (in priority order):
lint-staged key in package.json.lintstagedrc.json or .lintstagedrc.jslint-staged.config.js or lint-staged.config.mjsMinimal (Biome):
{
"*.{js,jsx,ts,tsx,json}": ["biome check --write --no-errors-on-unmatched"]
}
ESLint + Prettier:
{
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,yml,yaml,css,html}": ["prettier --write"]
}
TypeScript-aware (with type checking on staged files):
{
"*.{ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{js,jsx}": ["eslint --fix", "prettier --write"]
}
Note: Running
tscin lint-staged is usually not recommended because TypeScript type checking requires the full project context, not just staged files. Puttsc --noEmitin your pre-push hook instead.
Custom function (lint-staged.config.mjs):
export default {
'*.{ts,tsx}': (filenames) => [
`eslint --fix ${filenames.join(' ')}`,
`prettier --write ${filenames.join(' ')}`,
],
'*.css': ['prettier --write'],
};
Monorepo-aware:
{
"packages/frontend/**/*.{ts,tsx}": ["eslint --fix --config packages/frontend/eslint.config.js"],
"packages/backend/**/*.{ts,tsx}": ["eslint --fix --config packages/backend/eslint.config.js"]
}
For Python, Rust, and Go, lint-staged isn't available. Use framework-native file filtering instead:
Lefthook {staged_files}:
pre-commit:
commands:
ruff:
glob: "*.py"
run: ruff check --fix {staged_files}
rustfmt:
glob: "*.rs"
run: rustfmt {staged_files}
Pre-commit framework automatically passes only matching staged files to each hook.
Shell scripts (manual):
#!/bin/sh
# Get staged .py files
STAGED_PY=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
if [ -n "$STAGED_PY" ]; then
ruff check --fix $STAGED_PY
ruff format $STAGED_PY
git add $STAGED_PY
fi
--fix, --write)npx lint-staged --debug