MCPcopy Index your code
hub / github.com/evilmartians/lefthook

github.com/evilmartians/lefthook @v2.1.9

repository ↗ · DeepWiki ↗ · release v2.1.9 ↗ · Ask this repo → · + Follow
501 symbols 1,989 edges 126 files 97 documented · 19% 17 cross-repo links updated 6d agov2.1.9 · 2026-05-29★ 8,47864 open issues
README

Build Status codecov

Lefthook

A Git hooks manager for Node.js, Ruby, Python and many other types of projects.

  • Fast. It is written in Go. Can run commands in parallel.
  • Powerful. It allows to control execution and files you pass to your commands.
  • Simple. It is single dependency-free binary which can work in any environment.

📖 Introduction post

Sponsored by Evil Martians

Install

With Go (>= 1.26):

go install github.com/evilmartians/lefthook/v2@v2.1.9
  • or as a go tool
go get -tool github.com/evilmartians/lefthook/v2@v2.1.9

With NPM:

npm install lefthook --save-dev

For Ruby:

gem install lefthook

For Python:

pipx install lefthook

Installation guide with more ways to install lefthook: apt, brew, winget, and others.

Usage

Configure your hooks, install them once and forget about it: rely on the magic underneath.

TL;DR

# Configure your hooks
vim lefthook.yml

# Install them to the git project
lefthook install

# Enjoy your work with git
git add -A && git commit -m '...'

More details

Why Lefthook

  • Parallel execution

    Gives you more speed. docs
pre-push:
  parallel: true
  • Flexible list of files

    If you want your own list. Custom and prebuilt examples.
pre-commit:
  jobs:
    - name: lint frontend
      run: yarn eslint {staged_files}

    - name: lint backend
      run: bundle exec rubocop --force-exclusion -- {all_files}

    - name: stylelint frontend
      files: git diff --name-only HEAD @{push}
      run: yarn stylelint {files}
  • Glob and regexp filters

    If you want to filter list of files. You could find more glob pattern examples here.
pre-commit:
  jobs:
    - name: lint backend
      glob: "*.rb" # glob filter
      exclude:
        - "*/application.rb"
        - "*/routes.rb"
      run: bundle exec rubocop --force-exclusion -- {all_files}
  • Execute in sub-directory

    If you want to execute the commands in a relative path
pre-commit:
  jobs:
    - name: lint backend
      root: "api/" # Careful to have only trailing slash
      glob: "*.rb" # glob filter
      run: bundle exec rubocop -- {all_files}
  • Run scripts

If oneline commands are not enough, you can execute files. docs

commit-msg:
  jobs:
    - script: "template_checker"
      runner: bash
  • Tags

    If you want to control a group of commands. docs
pre-push:
  jobs:
    - name: audit packages
      tags:
        - frontend
        - linters
      run: yarn lint

    - name: audit gems
      tags:
        - backend
        - security
      run: bundle audit
  • Support Docker

If you are in the Docker environment. docs

pre-commit:
  jobs:
    - script: "good_job.js"
      runner: docker run -it --rm <container_id_or_name> {cmd}
  • Local config

If you are a frontend/backend developer and want to skip unnecessary commands or override something in Docker. docs

# lefthook-local.yml
pre-push:
  exclude_tags:
    - frontend
  jobs:
    - name: audit packages
      skip: true
  • Direct control

If you want to run hooks group directly.

$ lefthook run pre-commit
  • Your own tasks

If you want to run specific group of commands directly.

fixer:
  jobs:
    - run: bundle exec rubocop --force-exclusion --safe-auto-correct -- {staged_files}
    - run: yarn eslint --fix {staged_files}
$ lefthook run fixer
  • Control output

You can control what lefthook prints with output option.

output:
  - execution
  - failure

Guides

Examples

Check examples

Articles

Extension points exported contracts — how you extend this code

Command (Interface)
(no doc) [6 implementers]
internal/system/command.go
Executor (Interface)
Executor provides an interface for command execution. It is used here for testing purpose mostly. [2 implementers]
internal/run/controller/exec/executor.go
CommandWithContext (Interface)
(no doc) [2 implementers]
internal/system/command.go

Core symbols most depended-on inside this repo

Errorf
called by 108
internal/logger/logger.go
Run
called by 48
internal/system/command.go
Warnf
called by 31
internal/logger/logger.go
Paint
called by 30
internal/logger/colors.go
ParseHook
called by 29
tests/helpers/configtest/config.go
Cmd
called by 27
internal/git/commander.go
Success
called by 25
internal/run/result/result.go
Failure
called by 24
internal/run/result/result.go

Shape

Method 231
Function 182
Struct 75
TypeAlias 7
Interface 5
Class 1

Languages

Go96%
Python2%
TypeScript2%

Modules by API surface

internal/git/repo.go28 symbols
internal/config/loader.go27 symbols
internal/command/install.go23 symbols
internal/run/controller/filter/filter.go18 symbols
internal/logger/logger.go18 symbols
internal/run/controller/command/replacer/replacer.go15 symbols
internal/config/config.go12 symbols
internal/command/run.go12 symbols
internal/logger/spinner.go11 symbols
internal/git/commander.go11 symbols
internal/logger/execution_logger.go10 symbols
internal/command/lefthook.go10 symbols

Dependencies from manifests, versioned

github.com/alessio/shellescapev1.4.1 · 1×
github.com/bahlo/generic-list-gov0.2.0 · 1×
github.com/bmatcuk/doublestar/v4v4.10.0 · 1×
github.com/charmbracelet/colorprofilev0.4.3 · 1×
github.com/charmbracelet/ultravioletv0.0.0-2025120516121 · 1×
github.com/charmbracelet/x/ansiv0.11.7 · 1×
github.com/charmbracelet/x/termv0.2.2 · 1×
github.com/charmbracelet/x/termiosv0.1.1 · 1×
github.com/charmbracelet/x/windowsv0.2.2 · 1×

For agents

$ claude mcp add lefthook \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact