Files
aj-geddes ae9da7c095 style: apply Prettier formatting across all files
Auto-formatted with npx prettier --write . to fix CI format-check failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 03:56:40 -06:00
..

Pre-Commit Linting Hook

Automatically run linters before committing code to catch issues early.

Hook Configuration

Add to your .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit-linting/check.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Hook Script

Save as .claude/hooks/pre-commit-linting/check.sh:

#!/bin/bash
set -euo pipefail

# Parse hook input
INPUT=$(cat)
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Only check git commit commands
if [[ ! "$TOOL_INPUT" =~ git[[:space:]]+commit ]]; then
    echo '{"continue": true, "suppressOutput": true}'
    exit 0
fi

# Run linters
echo "Running pre-commit linters..." >&2

ERRORS=()

# ESLint for JavaScript/TypeScript
if command -v eslint &> /dev/null && [ -f .eslintrc.json ] || [ -f .eslintrc.js ]; then
    if ! eslint . --max-warnings 0 2>&1; then
        ERRORS+=("ESLint found issues")
    fi
fi

# Prettier for formatting
if command -v prettier &> /dev/null && [ -f .prettierrc ]; then
    if ! prettier --check . 2>&1; then
        ERRORS+=("Prettier formatting issues found")
    fi
fi

# Pylint for Python
if command -v pylint &> /dev/null && find . -name "*.py" -not -path "./venv/*" | head -1 | grep -q .; then
    if ! pylint **/*.py --fail-under=8.0 2>&1; then
        ERRORS+=("Pylint found issues")
    fi
fi

# RuboCop for Ruby
if command -v rubocop &> /dev/null && [ -f .rubocop.yml ]; then
    if ! rubocop 2>&1; then
        ERRORS+=("RuboCop found issues")
    fi
fi

# Report results
if [ ${#ERRORS[@]} -gt 0 ]; then
    ERROR_MSG="Pre-commit linting failed:\\n"
    for error in "${ERRORS[@]}"; do
        ERROR_MSG+="  - $error\\n"
    done
    ERROR_MSG+="\\nPlease fix these issues before committing."

    echo "{\"permissionDecision\": \"deny\", \"reason\": \"$ERROR_MSG\"}" | jq -c
    exit 2
fi

echo '{"permissionDecision": "allow"}'
exit 0

Setup

  1. Make the script executable:
chmod +x .claude/hooks/pre-commit-linting/check.sh
  1. Install linters for your stack:

JavaScript/TypeScript:

npm install --save-dev eslint prettier
npx eslint --init

Python:

pip install pylint black

Ruby:

gem install rubocop
  1. Configure each linter with project-specific rules

Features

  • Automatically runs before git commits
  • Checks multiple file types (JS, TS, Python, Ruby)
  • Blocks commits with linting errors
  • Fast execution with 30-second timeout
  • Provides clear error messages
  • Skips non-commit Bash commands

Customization

Add more linters by extending the script:

# Go
if command -v golangci-lint &> /dev/null && [ -f go.mod ]; then
    if ! golangci-lint run 2>&1; then
        ERRORS+=("golangci-lint found issues")
    fi
fi

# Rust
if command -v cargo &> /dev/null && [ -f Cargo.toml ]; then
    if ! cargo clippy -- -D warnings 2>&1; then
        ERRORS+=("Clippy found issues")
    fi
fi

Bypassing (When Necessary)

For emergency commits, you can temporarily disable:

# Commit without hooks
git commit --no-verify -m "Emergency fix"

Best Practices

  • Keep linting rules consistent across team
  • Run linters in CI/CD as well
  • Auto-fix when possible (prettier --write)
  • Configure IDE integration for real-time feedback
  • Use .gitignore patterns to exclude generated files