Skip to content

Git hooks for linters and formatters

Configure your checks once and run them before commits, while you work, or in CI. hk runs independent steps in parallel and coordinates changes to shared files.

mise use hk

Install with mise, or choose another installation method. All options →

hk.pklOne set of steps
// Run by check, fix, and pre-commit
steps {
  ["prettier"] = Builtins.prettier
  ["eslint"] = Builtins.eslint
  ["ruff"] = Builtins.ruff
}

Independent files, concurrent work

app.ts
checkfix
main.py
checkfix

Checks can read a file concurrently; fixes wait for exclusive access.

See the complete configuration
  1. hk: On a dark stage, a fishing hook drops in on a line while a pen draws the hk logo. The hook becomes the leg of the k and clicks into place. Caption: Git hooks and project checks, in parallel.
  2. Steps in hk.pkl: Names of hk's builtin linter and formatter configurations stream across the screen. Caption: Builtins, from prettier to zizmor. Seven of them fly into an hk.pkl file as steps: prettier, ruff, ruff-format, which depends on ruff, shfmt, shellcheck, trailing-whitespace and newlines. Caption: Configured in Pkl: typed and reusable.
  3. git commit runs hk: The command git commit, with the message feat: hoist the sails, slams onto the screen. Pressing Enter opens a terminal where hk's pre-commit hook starts and fetches the staged files. Caption: After hk install, git commit runs hk before the commit.
  4. Unstaged work is stashed: The staged file src/main.py appears with one unstaged line at its end, a to-do comment. hk slices that line off into a git stash, and the formatting problems in the staged version are underlined. Caption: Unstaged work is stashed, so linters see only what you staged.
  5. Steps in parallel: The seven steps run as bars on four file lanes. prettier, ruff and shfmt start together on different files. Caption: Different files? Steps run at once. shellcheck waits for shfmt on the same shell script, ruff-format waits for ruff because it depends on it, and trailing-whitespace and newlines, which touch every file, wait for the others and then take turns. Caption: Same file? They take turns. A note says the order comes from one real commit and is not to scale.
  6. Fixes staged, edits back: The fixed files are staged, and README.md is back to its last committed version. Side by side, the staged src/main.py holds the fixed code, and the worktree copy gets the same fixes plus the to-do line back from the stash. Caption: Fixes are staged. Your edits come back. Then the commit feat: hoist the sails lands on the main branch. Caption: The commit gets the fixed version.
  7. A commit hk can't fix: A later commit stages scripts/deploy.sh with an unused variable. shellcheck flags it with a warning, which hk cannot fix, so the step fails. The fishing hook snags the commit before it reaches the branch, so no commit is made. Caption: Can't be fixed? hk blocks the commit.
  8. Commit, terminal, CI: Three panels show the same seven steps passing in a git commit, in hk fix at a terminal, and in hk check --all in CI. Caption: One set of steps: commit, terminal, CI. Then two small timelines compare one file, README.md: under hk check, three checks read it at the same time; under hk fix, three fixers take turns. Caption: Checks share a file. Fixes take turns.
  9. Benchmarks: A bar chart race from hk's published benchmark, run on a 6,159-file repository with 10 fixers and 8 CPUs. Commit: About 60 staged files with defects, fixed by each tool's pre-commit hook. hk (parallel, file locks) takes 1.16 s, lefthook (fixers in turn, parallel checks) 1.95 s, prek (fixers in turn, parallel checks, batched) 2.34 s and pre-commit (sequential hooks, batched files) 3.14 s. hk is 1.7 times faster than the fastest other tool, lefthook. Caption: Timed only when the files are right. A note under the chart reads Every tool and scenario: hk.jdx.dev/benchmarks.
  10. Morph: The bars shrink into strokes that set into the hk logo.
  11. mise use hk: The hk logo returns in full, beside the name hk, the line Git hooks for linters and formatters, the command mise use hk, and the address hk.jdx.dev.
What hk does when you commit, in about a minute. The captions are on screen, so it works with the sound off. Timings come from the benchmarks.

Parallel execution

Read/write locks coordinate overlapping steps. Diff and file-list checks let hk narrow the work that needs an exclusive lock.

How execution works →

Partial commits

Stash unstaged work before fixing the staged version of a file, then restore it after the hook finishes.

Understand stashing →

Linter configuration

Start with built-in configurations or write a shell command. Use mise or your existing package manager to provide the tools.

Browse builtins →

Run checks locally and in CI

Define your steps once in Pkl. Reuse them when you check a change, fix your working tree, or validate the whole repository.

Learn the workflow →
hk check
Check modified files
hk fix
Apply available fixes
hk check --all
Check the repository in CI
hk check --plan
Preview which steps will run
MIT LicenseCopyright © 2026jdx.dev