Lefthook
Create or update Lefthook configuration with exact option names, practical defaults, and clear debugging steps. Explain the tool briefly, then produce a working config for the current repository.
Use this skill when the task mentions:
lefthook.yml, .lefthook.yml, .config/lefthook.yml, lefthook-local.yml, or another lefthook-local.* file
- Git hooks such as
pre-commit, commit-msg, pre-push, post-merge, or post-checkout
lefthook install, lefthook run, lefthook dump, or lefthook validate
- choosing between
commands, jobs, and scripts
- placeholders such as
{staged_files}, {push_files}, {all_files}, {files}, or {1}
- hook bugs such as commands not running, wrong files matching, fixed files not restaged, or local-only wrappers leaking into shared config
Non-Negotiables
- Inspect the repository before writing config. Reuse the existing config filename and format if one already exists.
- Match hook commands to the repo's real toolchain. Reuse existing package manager commands, scripts, and linters instead of inventing wrappers.
- Choose the simplest structure that fits. Use
commands by default, jobs for ordering or grouping, and scripts only for executable hook files.
- Use placeholders and filters that match the hook type.
pre-commit usually wants {staged_files}, pre-push usually wants {push_files}, and commit-msg usually wants {1}.
- Add
stage_fixed: true only for commands that rewrite tracked files and should re-stage them.
- Put local-only skips, Docker wrappers, and private overrides in
lefthook-local.*, not in the shared team config.
- When debugging or reviewing, use
lefthook validate, lefthook dump, and lefthook run <hook> before guessing.
Start Here
Inspect the repository before writing anything.
- Look for existing config files first:
lefthook.yml, .lefthook.yml, .config/lefthook.yml, lefthook.yaml, lefthook.toml, lefthook.json, lefthook.jsonc, and matching lefthook-local.* overrides.
- Preserve the existing format and naming if a config already exists.
- Prefer YAML
lefthook.yml for new setups unless the repo already standardizes on TOML, JSON, or JSONC.
- Read package manager files, script directories, and CI or lint tooling before proposing hook commands.
- Keep one main format per project. Do not introduce a second main Lefthook config format.
Author Configs
Choose the simplest structure that fits the workflow.
- Use
commands for the default case: straightforward named tasks such as linting, formatting, tests, or commit message checks.
- Use
jobs when order, grouping, parallel, piped, mixed run and script entries, or merge-friendly named jobs matter.
- Use
scripts only when the repository already uses executable files under source_dir/<hook>/ or when the user explicitly wants file-based scripts.
- Prefer hook names that match real Git hooks such as
pre-commit, commit-msg, and pre-push. It is also valid to define custom task groups and run them through lefthook run <name>.
- Keep commands local to the repo toolchain. Reuse existing
npm, pnpm, yarn, bun, go, bundle, uv, or shell commands instead of inventing new wrappers.
- Use file templates deliberately:
{staged_files} for pre-commit, {push_files} for pre-push, {all_files} for whole-repo checks, {files} when a custom files: command is defined, {cmd} for local wrapping, and {1} for hook arguments such as commit-msg.
- Add
stage_fixed: true only when the command actually rewrites tracked files and should re-stage them.
- Use
glob, exclude, file_types, and root to keep commands narrow and fast.
- Reach for
lefthook-local.* when the user wants local-only skips, Docker wrappers, or private overrides that should not change the shared team config.
Integrated example for a JS or TS repo that already uses ESLint, Prettier, and Commitlint:
pre-commit:
commands:
eslint:
glob: "*.{js,jsx,ts,tsx}"
run: pnpm eslint --fix {staged_files}
stage_fixed: true
prettier:
glob: "*.{js,jsx,ts,tsx,json,md,yml,yaml}"
run: pnpm prettier --write {staged_files}
stage_fixed: true
commit-msg:
commands:
commitlint:
run: pnpm commitlint --edit {1}
Why this shape:
commands is enough because each task is independent.
pre-commit uses {staged_files} because the checks should scope to the staged set.
commit-msg uses {1} because Git passes the commit message file path as the first hook argument.
- Both formatters set
stage_fixed: true because they rewrite tracked files.
Read overview.md for install and command flow, config-model.md for structure and merge behavior, patterns.md for common recipes, best-practices.md when the user asks for battle-tested guidance, and schema-cheatsheet.md for exact keys.
Explain And Debug
Explain Lefthook in terms of repository behavior, not marketing.
- Describe it as a Git hook manager that reads config on each run and executes repo-defined commands or scripts.
- Mention that
lefthook install installs Git hook entrypoints and creates an empty config if none exists.
- Mention that config changes do not require reinstall in normal cases because hooks read config each time they run.
- Use
lefthook validate to check configuration validity.
- Use
lefthook dump to inspect the effective merged config after extends, remotes, and lefthook-local.*.
- Use
lefthook run <hook> with --all-files, --file, --job, or --tag to reproduce hook behavior outside git commit or git push.
- Explain merge precedence as: main config, then
extends, then remotes, then lefthook-local.*.
- When using the phrase "best practice", ground exact behavior in the bundled docs and schema, and treat blog-derived patterns as workflow guidance rather than hard requirements.
When reviewing an existing config, call out:
- commands that will not match any files because placeholders and filters do not align;
- rewriters that forgot
stage_fixed: true;
- file-wide commands incorrectly using
{staged_files};
- unnecessary complexity where
commands would be enough;
- local overrides that should live in
lefthook-local.* instead of shared config.
Use Bundled Resources
- For a quick starting point, copy
assets/minimal-pre-commit.yml. For JS or TS repos, copy assets/js-ts-pre-commit.yml. Then adapt the package manager and commands to the repo.
- Load
references/patterns.md for concrete recipes before drafting a non-trivial config.
- Load
references/best-practices.md when the user explicitly asks for recommendations or wants rationale backed by real-world Lefthook usage.
- Load
references/schema-cheatsheet.md when exact option spelling or allowed nesting matters.
- Keep explanations compact in the final answer. Include only the options that matter for the current repository.
1---2name: lefthook3description: Create, explain, review, and troubleshoot Lefthook hook configurations for Git repositories. Use when Codex needs to author or update `lefthook.yml`, `.lefthook.yml`, `.config/lefthook.yml`, `lefthook-local.*`, explain `commands` vs `jobs` vs `scripts`, wire up `install`, `run`, `dump`, or `validate`, add hook automation such as linting, formatting, tests, commit message checks, or local overrides, or debug why a Lefthook setup does not behave as expected.4---56# Lefthook78Create or update Lefthook configuration with exact option names, practical defaults, and clear debugging steps. Explain the tool briefly, then produce a working config for the current repository.910Use this skill when the task mentions:1112- `lefthook.yml`, `.lefthook.yml`, `.config/lefthook.yml`, `lefthook-local.yml`, or another `lefthook-local.*` file13- Git hooks such as `pre-commit`, `commit-msg`, `pre-push`, `post-merge`, or `post-checkout`14- `lefthook install`, `lefthook run`, `lefthook dump`, or `lefthook validate`15- choosing between `commands`, `jobs`, and `scripts`16- placeholders such as `{staged_files}`, `{push_files}`, `{all_files}`, `{files}`, or `{1}`17- hook bugs such as commands not running, wrong files matching, fixed files not restaged, or local-only wrappers leaking into shared config1819## Non-Negotiables20211. Inspect the repository before writing config. Reuse the existing config filename and format if one already exists.222. Match hook commands to the repo's real toolchain. Reuse existing package manager commands, scripts, and linters instead of inventing wrappers.233. Choose the simplest structure that fits. Use `commands` by default, `jobs` for ordering or grouping, and `scripts` only for executable hook files.244. Use placeholders and filters that match the hook type. `pre-commit` usually wants `{staged_files}`, `pre-push` usually wants `{push_files}`, and `commit-msg` usually wants `{1}`.255. Add `stage_fixed: true` only for commands that rewrite tracked files and should re-stage them.266. Put local-only skips, Docker wrappers, and private overrides in `lefthook-local.*`, not in the shared team config.277. When debugging or reviewing, use `lefthook validate`, `lefthook dump`, and `lefthook run <hook>` before guessing.2829## Start Here3031Inspect the repository before writing anything.3233- Look for existing config files first: `lefthook.yml`, `.lefthook.yml`, `.config/lefthook.yml`, `lefthook.yaml`, `lefthook.toml`, `lefthook.json`, `lefthook.jsonc`, and matching `lefthook-local.*` overrides.34- Preserve the existing format and naming if a config already exists.35- Prefer YAML `lefthook.yml` for new setups unless the repo already standardizes on TOML, JSON, or JSONC.36- Read package manager files, script directories, and CI or lint tooling before proposing hook commands.37- Keep one main format per project. Do not introduce a second main Lefthook config format.3839## Author Configs4041Choose the simplest structure that fits the workflow.4243- Use `commands` for the default case: straightforward named tasks such as linting, formatting, tests, or commit message checks.44- Use `jobs` when order, grouping, `parallel`, `piped`, mixed `run` and `script` entries, or merge-friendly named jobs matter.45- Use `scripts` only when the repository already uses executable files under `source_dir/<hook>/` or when the user explicitly wants file-based scripts.46- Prefer hook names that match real Git hooks such as `pre-commit`, `commit-msg`, and `pre-push`. It is also valid to define custom task groups and run them through `lefthook run <name>`.47- Keep commands local to the repo toolchain. Reuse existing `npm`, `pnpm`, `yarn`, `bun`, `go`, `bundle`, `uv`, or shell commands instead of inventing new wrappers.48- Use file templates deliberately: `{staged_files}` for `pre-commit`, `{push_files}` for `pre-push`, `{all_files}` for whole-repo checks, `{files}` when a custom `files:` command is defined, `{cmd}` for local wrapping, and `{1}` for hook arguments such as `commit-msg`.49- Add `stage_fixed: true` only when the command actually rewrites tracked files and should re-stage them.50- Use `glob`, `exclude`, `file_types`, and `root` to keep commands narrow and fast.51- Reach for `lefthook-local.*` when the user wants local-only skips, Docker wrappers, or private overrides that should not change the shared team config.5253Integrated example for a JS or TS repo that already uses ESLint, Prettier, and Commitlint:5455```yml56pre-commit:57 commands:58 eslint:59 glob: "*.{js,jsx,ts,tsx}"60 run: pnpm eslint --fix {staged_files}61 stage_fixed: true62 prettier:63 glob: "*.{js,jsx,ts,tsx,json,md,yml,yaml}"64 run: pnpm prettier --write {staged_files}65 stage_fixed: true6667commit-msg:68 commands:69 commitlint:70 run: pnpm commitlint --edit {1}71```7273Why this shape:7475- `commands` is enough because each task is independent.76- `pre-commit` uses `{staged_files}` because the checks should scope to the staged set.77- `commit-msg` uses `{1}` because Git passes the commit message file path as the first hook argument.78- Both formatters set `stage_fixed: true` because they rewrite tracked files.7980Read [overview.md](./references/overview.md) for install and command flow, [config-model.md](./references/config-model.md) for structure and merge behavior, [patterns.md](./references/patterns.md) for common recipes, [best-practices.md](./references/best-practices.md) when the user asks for battle-tested guidance, and [schema-cheatsheet.md](./references/schema-cheatsheet.md) for exact keys.8182## Explain And Debug8384Explain Lefthook in terms of repository behavior, not marketing.8586- Describe it as a Git hook manager that reads config on each run and executes repo-defined commands or scripts.87- Mention that `lefthook install` installs Git hook entrypoints and creates an empty config if none exists.88- Mention that config changes do not require reinstall in normal cases because hooks read config each time they run.89- Use `lefthook validate` to check configuration validity.90- Use `lefthook dump` to inspect the effective merged config after `extends`, `remotes`, and `lefthook-local.*`.91- Use `lefthook run <hook>` with `--all-files`, `--file`, `--job`, or `--tag` to reproduce hook behavior outside `git commit` or `git push`.92- Explain merge precedence as: main config, then `extends`, then `remotes`, then `lefthook-local.*`.93- When using the phrase "best practice", ground exact behavior in the bundled docs and schema, and treat blog-derived patterns as workflow guidance rather than hard requirements.9495When reviewing an existing config, call out:9697- commands that will not match any files because placeholders and filters do not align;98- rewriters that forgot `stage_fixed: true`;99- file-wide commands incorrectly using `{staged_files}`;100- unnecessary complexity where `commands` would be enough;101- local overrides that should live in `lefthook-local.*` instead of shared config.102103## Use Bundled Resources104105- For a quick starting point, copy `assets/minimal-pre-commit.yml`. For JS or TS repos, copy `assets/js-ts-pre-commit.yml`. Then adapt the package manager and commands to the repo.106- Load `references/patterns.md` for concrete recipes before drafting a non-trivial config.107- Load `references/best-practices.md` when the user explicitly asks for recommendations or wants rationale backed by real-world Lefthook usage.108- Load `references/schema-cheatsheet.md` when exact option spelling or allowed nesting matters.109- Keep explanations compact in the final answer. Include only the options that matter for the current repository.