One set of Git hooks for all your repos.
When you work with Claude Code, you're committing and pushing changes constantly. Without guardrails, a bad commit slips through — broken formatting, failing tests, lint violations — and you don't notice until CI fails or a reviewer catches it.
These hooks are your safety net. Set them up once, globally, and every commit in every repo gets checked automatically. Claude Code runs git commit, the hooks catch problems before they land, and you move on with confidence. No per-repo setup, no remembering to run checks manually.
A globally-installable Git hooks collection managed by Githooks. Install once, apply everywhere. The hooks auto-detect project types, defer to Makefile conventions when available, and skip gracefully when tools aren't present. No per-repo configuration required.
The hooks follow a Makefile-first philosophy: if your project has a Makefile with a check target, the hooks run make -j check and let your existing build system handle the details. This aligns with the makefile-workflow conventions where projects have standardized check, check-format, lint, and test targets.
The collection grows over time to understand more languages and toolchains, but always remains global-friendly — sensible defaults that work without configuration.
Install globally for all repos:
# Install Githooks if needed
git hooks install
# Add shared hooks globally
git config --global githooks.shared "https://github.com/jaeyeom/shared-githooks.git@main"
# Update shared hooks
git hooks shared updateAlternatively, use per-repo configuration via .githooks/.shared.yaml:
urls:
- "https://github.com/jaeyeom/shared-githooks.git@main"When you commit, hooks run in parallel following this logic:
commit → pre-commit hooks (parallel) →
├── Makefile with check: target? → make -j check (covers everything)
├── Go project without Makefile check? → golangci-lint
├── Python project without Makefile check? → ruff check + format
├── Shell scripts staged? → shellcheck
├── Bazel project without Makefile check? → affected tests
├── .org files staged? → org-lint
├── Semgrep config present? → semgrep scan
├── Large files staged? → reject (configurable limit)
├── Whitespace errors? → git diff --check
└── Non-ASCII filenames? → reject (configurable)
commit-msg hooks (parallel) →
├── Subject line >72 chars? → reject
├── Co-Authored-By: line? → reject
└── AI-generated marker? → reject
The deference pattern is key: if your Makefile has a check target that runs linters, the tool-specific hooks (like lint-go.sh) skip automatically to avoid duplicate work. If your Makefile mentions golangci-lint, the Go hook defers. If it mentions bazel test, the Bazel hook defers.
When no Makefile exists, the hooks fall back to running tools directly. When tools aren't installed, they skip silently.
For the best experience, add a Makefile with a check target:
.PHONY: all check format check-format lint test
all: format lint test
check: check-format lint test # CI-safe, read-only
check-format:
# verify formatting without mutating files
lint:
# run linters (e.g., golangci-lint, shellcheck, eslint)
test:
# run testsThe check target should be read-only (no file mutations) and safe for make -j (parallel execution). When hooks detect make check, they defer tool-specific checks to your Makefile.
See the makefile-workflow conventions for detailed patterns per language (Go, Node.js, Bazel, mixed stacks).
| Hook | What It Does | Skip Conditions |
|---|---|---|
check.sh |
Runs make -j check if available |
No Makefile with check: target |
check-large-files.sh |
Rejects staged files exceeding a size threshold (default 1 MB) | Never (always runs); configure limit with git config hooks.maxfilesize <bytes> |
check-whitespace.sh |
Detects trailing whitespace and mixed line endings via git diff-index --check |
Files handled by language formatters (*.go, *.py, *.proto, .bzl, BUILD) |
check-i18n-sync.sh |
Requires all language variants of a doc file to be staged together | Opt-in: enable with git config hooks.i18nsync true |
check-non-ascii.sh |
Rejects non-ASCII filenames for portability | git config hooks.allownonascii true |
lint-go.sh |
Runs golangci-lint run ./... |
No .golangci.yml, tool not installed, or Makefile check: target mentions golangci-lint |
lint-python.sh |
Runs ruff check and ruff format --check on Python projects |
No ruff config, tool not installed, Bazel manages ruff via @multitool, or Makefile check: target mentions ruff |
lint-shell.sh |
Runs shellcheck on staged .sh files |
No staged .sh files, tool not installed, or Makefile check: target mentions shellcheck |
lint-org.sh |
Runs org-lint on staged .org files |
No staged .org files, tool not installed |
lint-semgrep.sh |
Runs semgrep scan for static analysis |
No .semgrep.yml, .semgrep.yaml, or .semgrep/ directory; tool not installed; or Makefile check: target mentions semgrep |
test-bazel.sh |
Runs affected Bazel tests via bazel-affected-tests, auto-fixes format tests |
No BUILD/BUILD.bazel file, bazel not installed, or Makefile check: target mentions bazel test |
| Hook | What It Does | Skip Conditions |
|---|---|---|
check-subject-length.sh |
Enforces ≤72 character subject line | Never (always runs) |
check-co-authored-by.sh |
Rejects Co-Authored-By: lines |
Never (always runs) |
check-generated-comment.sh |
Rejects Generated with markers (AI tool artifacts) |
Never (always runs) |
| Setting | Effect |
|---|---|
git config hooks.allownonascii true |
Allow non-ASCII filenames |
git config hooks.i18nsync true |
Enable documentation sync check (requires all language variants staged together) |
git config hooks.maxfilesize <bytes> |
Set large file threshold (default: 1048576 = 1 MB) |
Makefile check: target |
Overrides tool-specific hooks when present |
.NOTPARALLEL in Makefile |
Disables parallel make -j execution |
Disable specific hooks via Githooks ignore patterns:
# Disable a specific hook
git hooks ignore add --pattern "ns:jaeyeom-shared-githooks/pre-commit/checks/test-bazel.sh"
# Disable all Bazel tests
git hooks ignore add --pattern "**/test-bazel.sh"Or add patterns to .githooks/.ignore.yaml:
patterns:
- "pre-commit/checks/test-bazel.sh"| Document | Description |
|---|---|
| Getting Started | Installation, setup, version pinning |
| Hooks Reference | Detailed behavior and conditions for all hooks |
| Configuration Guide | Namespaces, disabling hooks, environment variables, containers |
| Development Guide | Adding new hooks, coding conventions, testing |
Install development tools:
brew install shfmt shellcheck yamllint biomeRun formatting and checks:
make # format + lint
make check # CI-friendly checks (no mutation)
make help # show all targetsSee CLAUDE.md for full technical documentation on directory structure, YAML hook configs, parallel execution, containerization, and more.