Configure three-layer pre-commit system with linting, type checking, and testing hooks. Use for quality gate setup and code standards. Skip if pre-commit is optimally configured.
Configures a three-layer pre-commit system with linting, type checking, and testing hooks for code quality.
/plugin marketplace add athola/claude-night-market/plugin install attune@claude-night-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
scripts/run-component-lint.sh)scripts/run-component-typecheck.sh)scripts/run-component-tests.sh)scripts/check-all-quality.shConfigure a detailed three-layer pre-commit quality system that enforces linting, type checking, and testing before commits.
/attune:upgrade-project instead for updating existing configurationsThis skill implements a technical quality system based on three distinct layers. Layer 1 consists of fast global checks that perform quick linting and type checking on all files in approximately 50 to 200 milliseconds. Layer 2 focuses on component-specific checks, running detailed linting, type checking, and testing for changed components only, which typically takes between 10 and 30 seconds. Finally, Layer 3 uses validation hooks for structure verification, security scanning, and custom project checks. This multi-layered approach verifies that new code is automatically checked before commit, which prevents technical debt from entering the repository.
```yaml
repos:
repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks:
repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.2 hooks:
repo: https://github.com/pre-commit/mirrors-mypy rev: v1.13.0 hooks:
repo: https://github.com/PyCQA/bandit rev: 1.8.0 hooks:
For monorepos, plugin architectures, or projects with multiple components, add per-component quality checks.
Create quality check scripts:
scripts/run-component-lint.sh)```bash #!/bin/bash
set -euo pipefail
CHANGED_COMPONENTS=$(git diff --cached --name-only | grep -E '^(plugins|components)/' | cut -d/ -f2 | sort -u) || true
if [ -z "$CHANGED_COMPONENTS" ]; then echo "No components changed" exit 0 fi
echo "Linting changed components: $CHANGED_COMPONENTS"
FAILED=()
for component in $CHANGED_COMPONENTS; do if [ -d "plugins/$component" ]; then echo "Linting $component..." # Capture exit code to properly propagate failures local exit_code=0 if [ -f "plugins/$component/Makefile" ] && grep -q "^lint:" "plugins/$component/Makefile"; then (cd "plugins/$component" && make lint) || exit_code=$? else (cd "plugins/$component" && uv run ruff check .) || exit_code=$? fi if [ "$exit_code" -ne 0 ]; then FAILED+=("$component") fi fi done
if [ ${#FAILED[@]} -gt 0 ]; then echo "Lint failed for: ${FAILED[*]}" exit 1 fi ```
scripts/run-component-typecheck.sh)```bash #!/bin/bash
set -euo pipefail
CHANGED_COMPONENTS=$(git diff --cached --name-only | grep -E '^(plugins|components)/' | cut -d/ -f2 | sort -u) || true
if [ -z "$CHANGED_COMPONENTS" ]; then exit 0 fi
echo "Type checking changed components: $CHANGED_COMPONENTS"
FAILED=()
for component in $CHANGED_COMPONENTS; do if [ -d "plugins/$component" ]; then echo "Type checking $component..." # Capture output and exit code separately to properly propagate failures local output local exit_code=0 if [ -f "plugins/$component/Makefile" ] && grep -q "^typecheck:" "plugins/$component/Makefile"; then output=$(cd "plugins/$component" && make typecheck 2>&1) || exit_code=$? else output=$(cd "plugins/$component" && uv run mypy src/ 2>&1) || exit_code=$? fi # Display output (filter make noise) echo "$output" | grep -v "^make[" || true if [ "$exit_code" -ne 0 ]; then FAILED+=("$component") fi fi done
if [ ${#FAILED[@]} -gt 0 ]; then echo "Type check failed for: ${FAILED[*]}" exit 1 fi ```
scripts/run-component-tests.sh)```bash #!/bin/bash
set -euo pipefail
CHANGED_COMPONENTS=$(git diff --cached --name-only | grep -E '^(plugins|components)/' | cut -d/ -f2 | sort -u) || true
if [ -z "$CHANGED_COMPONENTS" ]; then exit 0 fi
echo "Testing changed components: $CHANGED_COMPONENTS"
FAILED=()
for component in $CHANGED_COMPONENTS; do if [ -d "plugins/$component" ]; then echo "Testing $component..." # Capture exit code to properly propagate failures local exit_code=0 if [ -f "plugins/$component/Makefile" ] && grep -q "^test:" "plugins/$component/Makefile"; then (cd "plugins/$component" && make test) || exit_code=$? else (cd "plugins/$component" && uv run pytest tests/) || exit_code=$? fi if [ "$exit_code" -ne 0 ]; then FAILED+=("$component") fi fi done
if [ ${#FAILED[@]} -gt 0 ]; then echo "Tests failed for: ${FAILED[*]}" exit 1 fi ```
```yaml
id: run-component-lint name: Lint Changed Components entry: ./scripts/run-component-lint.sh language: system pass_filenames: false files: ^(plugins|components)/.*\.py$
id: run-component-typecheck name: Type Check Changed Components entry: ./scripts/run-component-typecheck.sh language: system pass_filenames: false files: ^(plugins|components)/.*\.py$
id: run-component-tests name: Test Changed Components entry: ./scripts/run-component-tests.sh language: system pass_filenames: false files: ^(plugins|components)/.*\.(py|md)$ ```
Add custom validation hooks for project-specific requirements.
```yaml
```bash
python3 plugins/attune/scripts/attune_init.py \ --lang python \ --name my-project \ --path .
mkdir -p scripts chmod +x scripts/run-component-*.sh ```
Create pyproject.toml with strict type checking:
```toml [tool.mypy] python_version = "3.12" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true strict = true
[[tool.mypy.overrides]] module = "plugins.*" strict = true ```
```toml [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] addopts = [ "-v", # Verbose output "--strict-markers", # Strict marker enforcement "--cov=src", # Coverage for src/ "--cov-report=term", # Terminal coverage report ]
markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", "integration: marks tests as integration tests", ] ```
```bash
uv sync --extra dev
uv run pre-commit install
uv run pre-commit run --all-files
git add . git commit -m "feat: add feature"
```
For full quality checks (CI/CD, monthly audits):
scripts/check-all-quality.sh```bash #!/bin/bash
set -e
echo "=== Running Full Quality Checks ==="
./scripts/run-component-lint.sh --all
./scripts/run-component-typecheck.sh --all
./scripts/run-component-tests.sh --all
echo "=== All Quality Checks Passed ===" ```
Pre-commit hooks run in this order:
```
All must pass for commit to succeed.
| Check | Single Component | Multiple Components | All Components |
|---|---|---|---|
| Global Ruff | ~50ms | ~200ms | ~500ms |
| Global Mypy | ~200ms | ~500ms | ~1s |
| Component Lint | ~2-5s | ~4-10s | ~30-60s |
| Component Typecheck | ~3-8s | ~6-16s | ~60-120s |
| Component Tests | ~5-15s | ~10-30s | ~120-180s |
| Total | ~10-30s | ~20-60s | ~2-5min |
--incremental flag```bash
SKIP=run-component-tests git commit -m "WIP: tests in progress"
SKIP=run-component-lint,run-component-typecheck,run-component-tests git commit -m "WIP"
git commit --no-verify -m "Emergency fix" ```
Add project-specific hooks:
```yaml
id: check-architecture name: Validate Architecture Decisions entry: python3 scripts/check_architecture.py language: system pass_filenames: false files: ^(plugins|src)/.*\.py$
id: check-coverage name: Verify Test Coverage entry: python3 scripts/check_coverage.py language: system pass_filenames: false files: ^(plugins|src)/.*\.py$ ```
Verify CI runs the same detailed checks:
```yaml
name: Code Quality
on: [push, pull_request]
jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
run: pip install uv
- name: Install dependencies
run: uv sync
- name: Run Comprehensive Quality Checks
run: ./scripts/check-all-quality.sh
- name: Upload Coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage.xml
```
Solution: Only changed components are checked by default. For even faster commits:
```bash
SKIP=run-component-tests git commit -m "WIP: feature development"
./scripts/run-component-tests.sh --changed ```
```bash
uv run pre-commit clean
find . -name "pycache" -type d -exec rm -rf {} + find . -name ".pytest_cache" -type d -exec rm -rf {} + find . -name ".mypy_cache" -type d -exec rm -rf {} + ```
```bash
uv run pre-commit run --verbose --all-files
cd plugins/my-component make lint make typecheck make test ```
```toml
[tool.pytest.ini_options] pythonpath = ["src"] ```
```toml
[[tool.mypy.overrides]] module = "legacy_module.*" disallow_untyped_defs = false ```
Start with strict settings from the beginning, as they are easier to maintain over time. We recommend configuring type checking with strict = true in your pyproject.toml and setting up testing early by including pytest in your pre-commit hooks. If you must skip any hooks, always document the reason for the exception.
When adding hooks to an existing codebase, use a gradual adoption strategy. Start with global checks and add component-specific checks later as you resolve legacy issues. Fix identified quality problems progressively and create a baseline to document the current state for tracking improvements. Use the --no-verify flag sparingly and only for true emergencies.
Standardize your development targets by using per-component Makefiles for linting, type checking, and testing. Centralize common settings in a root pyproject.toml while allowing for per-component overrides. Automate the detection of changed components to keep commit times fast, and use a progressive disclosure approach to show summaries first and detailed errors only on failure.
```yaml
repos:
repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks:
repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.2 hooks:
repo: https://github.com/pre-commit/mirrors-mypy rev: v1.13.0 hooks:
repo: https://github.com/PyCQA/bandit rev: 1.8.0 hooks:
id: run-component-lint name: Lint Changed Components entry: ./scripts/run-component-lint.sh language: system pass_filenames: false files: ^plugins/.*\.py$
id: run-component-typecheck name: Type Check Changed Components entry: ./scripts/run-component-typecheck.sh language: system pass_filenames: false files: ^plugins/.*\.py$
id: run-component-tests name: Test Changed Components entry: ./scripts/run-component-tests.sh language: system pass_filenames: false files: ^plugins/.*\.(py|md)$
Skill(attune:project-init) - Full project initializationSkill(attune:workflow-setup) - GitHub Actions setupSkill(attune:makefile-generation) - Generate component MakefilesSkill(pensive:shell-review) - Audit shell scripts for exit code and safety issuesmake test for unit tests, make lint for static analysis, make format for auto-formatting. Target 85%+ coverage.Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.