| name | git-hooks-automation |
| description | ALWAYS use this when the request matches GIT Hooks Automation: Master Git hooks setup with Husky, lint-staged, pre-commit framework, and commitlint. |
Git Hooks Automation
Selective Reading Rule
Start with:
references/senior-master-standard.md
references/usage-routing.md
references/quality-checklist.md
Then load only the inherited docs, scripts, assets, or examples that match the user's actual task.
Automate code quality enforcement at the Git level. Set up hooks that lint, format, test, and validate before commits and pushes ever reach your CI pipeline — catching issues in seconds instead of minutes.
When to Use This Skill
- User asks to "set up git hooks" or "add pre-commit hooks"
- Configuring Husky, lint-staged, or the pre-commit framework
- Enforcing commit message conventions (Conventional Commits, commitlint)
- Automating linting, formatting, or type-checking before commits
- Setting up pre-push hooks for test runners
- Migrating from Husky v4 to v9+ or adopting hooks from scratch
- User mentions "pre-commit", "commit-msg", "pre-push", "lint-staged", or "githooks"
Git Hooks Fundamentals
Git hooks are scripts that run automatically at specific points in the Git workflow. They live in .git/hooks/ and are not version-controlled by default — which is why tools like Husky exist.
Hook Types & When They Fire
| Hook | Fires When | Common Use |
|---|
pre-commit | Before commit is created | Lint, format, type-check staged files |
prepare-commit-msg | After default msg, before editor | Auto-populate commit templates |
commit-msg | After user writes commit message | Enforce commit message format |
post-commit | After commit is created | Notifications, logging |
pre-push | Before push to remote | Run tests, check branch policies |
pre-rebase | Before rebase starts | Prevent rebase on protected branches |
post-merge | After merge completes | Install deps, run migrations |
post-checkout | After checkout/switch | Install deps, rebuild assets |
Native Git Hooks (No Framework)
cat > .git/hooks/pre-commit << 'EOF'
set -e
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|jsx|tsx)$' || true)
if [ -n "$STAGED_FILES" ]; then
echo "🔍 Linting staged files..."
echo "$STAGED_FILES" | xargs npx eslint --fix
echo "$STAGED_FILES" | xargs git add
fi
EOF
chmod +x .git/hooks/pre-commit
Problem: .git/hooks/ is local-only and not shared with the team. Use a framework instead.
Husky + lint-staged (Node.js Projects)
The modern standard for JavaScript/TypeScript projects. Husky manages Git hooks; lint-staged runs commands only on staged files for speed.
Quick Setup (Husky v9+)
npm install --save-dev husky lint-staged
npx husky init
echo "npx lint-staged" > .husky/pre-commit
Configure lint-staged in package.json
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"*.{css,scss}": [
"prettier --write",
"stylelint --fix"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}
}
Add Commit Message Linting
npm install --save-dev @commitlint/cli @commitlint/config-conventional
cat > commitlint.config.js << 'EOF'
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert'
]],
'subject-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100]
}
};
EOF
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
Add Pre-Push Hook
echo "npm test" > .husky/pre-push
Complete Husky Directory Structure
project/
├── .husky/
│ ├── pre-commit # npx lint-staged
│ ├── commit-msg # npx --no -- commitlint --edit $1
│ └── pre-push # npm test
├── commitlint.config.js
├── package.json # lint-staged config here
└── ...
pre-commit Framework (Python / Polyglot)
Language-agnostic framework that works with any project. Hooks are defined in YAML and run in isolated environments.
Setup
pip install pre-commit
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: detect-private-key
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
- id: ruff
args: ['--fix']
- id: ruff-format
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
- id: shellcheck
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.2.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
EOF
pre-commit install
pre-commit install --hook-type commit-msg
pre-commit run --all-files
Key Commands
pre-commit install
pre-commit run --all-files
pre-commit autoupdate
pre-commit run <hook-id>
pre-commit clean
Custom Hook Scripts (Any Language)
For projects not using Node or Python, write hooks directly in shell.
Portable Pre-Commit Hook
#!/bin/sh
set -e
echo "=== Pre-Commit Checks ==="
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached")
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
echo "❌ Direct commits to $BRANCH are not allowed. Use a feature branch."
exit 1
fi
if git diff --cached --diff-filter=ACM | grep -nE '(console\.log|debugger|binding\.pry|import pdb)' > /dev/null 2>&1; then
echo "⚠️ Debug statements found in staged files:"
git diff --cached --diff-filter=ACM | grep -nE '(console\.log|debugger|binding\.pry|import pdb)'
echo "Remove them or use git commit --no-verify to bypass."
exit 1
fi
LARGE_FILES=$(git diff --cached --name-only --diff-filter=ACM | while read f; do
size=$(wc -c < "$f" 2>/dev/null || echo 0)
if [ "$size" -gt 1048576 ]; then echo "$f ()KB)";
)
[ -n ];
1
git diff --cached --diff-filter=ACM | grep -nEi > /dev/null 2>&1;
1
Share Custom Hooks via core.hooksPath
git config core.hooksPath .githooks
setup:
git config core.hooksPath .githooks
chmod +x .githooks/*
CI Integration
Hooks are a first line of defense, but CI is the source of truth.
Run pre-commit in CI (GitHub Actions)
name: Lint
on: [push, pull_request]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: pre-commit/action@v3.0.1
Run lint-staged in CI (Validation Only)
name: Lint Check
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx eslint . --max-warnings=0
- run: npx prettier --check .
Common Pitfalls & Fixes
Hooks Not Running
| Symptom | Cause | Fix |
|---|
| Hooks silently skipped | Not installed in .git/hooks/ | Run npx husky init or pre-commit install |
| "Permission denied" | Hook file not executable | chmod +x .husky/pre-commit |
| Hooks run but wrong ones | Stale hooks from old setup | Delete .git/hooks/ contents, reinstall |
| Works locally, fails in CI | Different Node/Python versions | Pin versions in CI config |
Performance Issues
{
"scripts": {
"precommit": "eslint src/ && prettier --write src/"
}
}
{
"lint-staged": {
"*.{js,ts}": ["eslint --fix", "prettier --write"]
}
}
Bypassing Hooks (When Needed)
git commit --no-verify -m "wip: quick save"
git push --no-verify
SKIP=eslint git commit -m "fix: update config"
Warning: Bypassing hooks should be rare. If your team frequently bypasses, the hooks are too slow or too strict — fix them.
Migration Guide
Husky v4 → v9 Migration
npm uninstall husky
rm -rf .husky
npm install --save-dev husky
npx husky init
echo "npx lint-staged" > .husky/pre-commit
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
Adopting Hooks on an Existing Project
{ "*.{js,ts}": ["prettier --write"] }
{ "*.{js,ts}": ["eslint --fix", "prettier --write"] }
Key Principles
- Staged files only — Never lint the entire codebase on every commit
- Auto-fix when possible —
--fix flags reduce developer friction
- Fast hooks — Pre-commit should complete in < 5 seconds
- Fail loud — Clear error messages with actionable fixes
- Team-shared — Use Husky or
core.hooksPath so hooks are version-controlled
- CI as backup — Hooks are convenience; CI is the enforcer
- Gradual adoption — Start with formatting, add linting, then testing
Related Skills
@codebase-audit-pre-push - Deep audit before GitHub push
@verification-before-completion - Verification before claiming work is done
@bash-pro - Advanced shell scripting for custom hooks
@github-actions-templates - CI/CD workflow templates
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.