Full Code of evilmartians/lefthook for AI

master f8e73b947e2e cached
372 files
699.5 KB
223.7k tokens
612 symbols
1 requests
Download .txt
Showing preview only (780K chars total). Download the full file or copy to clipboard to get everything.
Repository: evilmartians/lefthook
Branch: master
Commit: f8e73b947e2e
Files: 372
Total size: 699.5 KB

Directory structure:
gitextract_h_yok89w/

├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── codeql.yml
│       ├── gh-pages.yml
│       ├── lint.yml
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── .lefthook.yml
├── .tool-versions
├── .typos.toml
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── assets/
│   └── css/
│       └── lefthook.css
├── book.toml
├── cliff.toml
├── cmd/
│   ├── add-usage.txt
│   ├── add.go
│   ├── check_install.go
│   ├── commands.go
│   ├── commands_without_self_update.go
│   ├── dump.go
│   ├── install.go
│   ├── lefthook.go
│   ├── run.go
│   ├── self_update.go
│   ├── uninstall.go
│   ├── validate.go
│   └── version.go
├── codecov.yml
├── docmd.config.js
├── docs/
│   ├── configuration/
│   │   ├── Commands.md
│   │   ├── Hook.md
│   │   ├── README.md
│   │   ├── Scripts.md
│   │   ├── args.md
│   │   ├── assert_lefthook_installed.md
│   │   ├── colors.md
│   │   ├── configs.md
│   │   ├── env.md
│   │   ├── exclude.md
│   │   ├── exclude_tags.md
│   │   ├── extends.md
│   │   ├── fail_on_changes.md
│   │   ├── fail_on_changes_diff.md
│   │   ├── fail_text.md
│   │   ├── file_types.md
│   │   ├── files-global.md
│   │   ├── files.md
│   │   ├── follow.md
│   │   ├── git_url.md
│   │   ├── glob.md
│   │   ├── glob_matcher.md
│   │   ├── group.md
│   │   ├── install_non_git_hooks.md
│   │   ├── interactive.md
│   │   ├── jobs.md
│   │   ├── lefthook.md
│   │   ├── min_version.md
│   │   ├── name.md
│   │   ├── no_auto_install.md
│   │   ├── no_tty.md
│   │   ├── only.md
│   │   ├── output.md
│   │   ├── parallel.md
│   │   ├── piped.md
│   │   ├── priority.md
│   │   ├── rc.md
│   │   ├── ref.md
│   │   ├── refetch.md
│   │   ├── refetch_frequency.md
│   │   ├── remotes.md
│   │   ├── root.md
│   │   ├── run.md
│   │   ├── runner.md
│   │   ├── script.md
│   │   ├── setup.md
│   │   ├── skip.md
│   │   ├── skip_lfs.md
│   │   ├── source_dir.md
│   │   ├── source_dir_local.md
│   │   ├── stage_fixed.md
│   │   ├── tags.md
│   │   ├── templates.md
│   │   └── use_stdin.md
│   ├── configuration.md
│   ├── examples/
│   │   ├── commitlint.md
│   │   ├── filters.md
│   │   ├── lefthook-local.md
│   │   ├── remotes.md
│   │   ├── skip.md
│   │   ├── stage_fixed.md
│   │   └── wrap-commands.md
│   ├── index.md
│   ├── install.md
│   ├── installation/
│   │   ├── alpine.md
│   │   ├── arch.md
│   │   ├── deb.md
│   │   ├── devbox.md
│   │   ├── go.md
│   │   ├── homebrew.md
│   │   ├── manual.md
│   │   ├── mise.md
│   │   ├── node.md
│   │   ├── python.md
│   │   ├── rpm.md
│   │   ├── ruby.md
│   │   ├── scoop.md
│   │   ├── snap.md
│   │   ├── swift.md
│   │   └── winget.md
│   ├── misc/
│   │   └── contributors.md
│   ├── usage/
│   │   ├── commands/
│   │   │   ├── add.md
│   │   │   ├── check-install.md
│   │   │   ├── dump.md
│   │   │   ├── install.md
│   │   │   ├── run.md
│   │   │   ├── self-update.md
│   │   │   ├── uninstall.md
│   │   │   ├── validate.md
│   │   │   └── version.md
│   │   ├── envs/
│   │   │   ├── CI.md
│   │   │   ├── CLICOLOR_FORCE.md
│   │   │   ├── LEFTHOOK.md
│   │   │   ├── LEFTHOOK_BIN.md
│   │   │   ├── LEFTHOOK_CONFIG.md
│   │   │   ├── LEFTHOOK_EXCLUDE.md
│   │   │   ├── LEFTHOOK_OUTPUT.md
│   │   │   ├── LEFTHOOK_VERBOSE.md
│   │   │   └── NO_COLOR.md
│   │   └── features/
│   │       ├── git-args.md
│   │       ├── git-lfs.md
│   │       ├── interactive.md
│   │       ├── local.md
│   │       └── pass-stdin.md
│   └── usage.md
├── examples/
│   ├── commitlint/
│   │   ├── README.md
│   │   ├── commitlint.config.js
│   │   └── lefthook.yml
│   ├── complete/
│   │   └── lefthook.yml
│   ├── remote/
│   │   └── ping.yml
│   ├── verbose/
│   │   └── lefthook.yml
│   └── with_scripts/
│       └── lefthook.yml
├── gen/
│   └── jsonschema.go
├── go.mod
├── go.sum
├── integration_test.go
├── internal/
│   ├── command/
│   │   ├── add.go
│   │   ├── add_test.go
│   │   ├── check_install.go
│   │   ├── dump.go
│   │   ├── install.go
│   │   ├── install_test.go
│   │   ├── lefthook.go
│   │   ├── run.go
│   │   ├── run_test.go
│   │   ├── uninstall.go
│   │   ├── uninstall_test.go
│   │   └── validate.go
│   ├── config/
│   │   ├── available_hooks.go
│   │   ├── command.go
│   │   ├── command_executor.go
│   │   ├── command_test.go
│   │   ├── config.go
│   │   ├── files.go
│   │   ├── hook.go
│   │   ├── job.go
│   │   ├── jsonc_parser.go
│   │   ├── jsonschema.go
│   │   ├── jsonschema.json
│   │   ├── load.go
│   │   ├── load_test.go
│   │   ├── remote.go
│   │   ├── script.go
│   │   ├── script_test.go
│   │   ├── skip_checker.go
│   │   └── skip_checker_test.go
│   ├── git/
│   │   ├── command_executor.go
│   │   ├── command_executor_test.go
│   │   ├── lfs.go
│   │   ├── remote.go
│   │   ├── repository.go
│   │   ├── repository_test.go
│   │   └── state.go
│   ├── log/
│   │   ├── builder.go
│   │   ├── execution.go
│   │   ├── log.go
│   │   ├── log_test.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── setup.go
│   │   └── skip.go
│   ├── run/
│   │   ├── controller/
│   │   │   ├── command/
│   │   │   │   ├── build.go
│   │   │   │   ├── build_command.go
│   │   │   │   ├── build_script.go
│   │   │   │   ├── replacer/
│   │   │   │   │   ├── replacer.go
│   │   │   │   │   └── replacer_test.go
│   │   │   │   └── skip_error.go
│   │   │   ├── controller.go
│   │   │   ├── controller_test.go
│   │   │   ├── exec/
│   │   │   │   ├── exec_unix.go
│   │   │   │   ├── exec_windows.go
│   │   │   │   └── executor.go
│   │   │   ├── filter/
│   │   │   │   ├── detect_text.go
│   │   │   │   ├── detect_text_test.go
│   │   │   │   ├── filter.go
│   │   │   │   └── filter_test.go
│   │   │   ├── guard.go
│   │   │   ├── guard_test.go
│   │   │   ├── job.go
│   │   │   ├── lfs.go
│   │   │   ├── run.go
│   │   │   ├── scope.go
│   │   │   ├── scope_test.go
│   │   │   ├── setup.go
│   │   │   └── utils/
│   │   │       ├── cached_reader.go
│   │   │       ├── cached_reader_test.go
│   │   │       ├── firstNonBlank.go
│   │   │       └── intersect.go
│   │   ├── result/
│   │   │   ├── result.go
│   │   │   └── result_test.go
│   │   └── run.go
│   ├── system/
│   │   ├── command.go
│   │   ├── limits.go
│   │   ├── null_reader.go
│   │   ├── null_reader_test.go
│   │   ├── sh_unix.go
│   │   └── sh_windows.go
│   ├── templates/
│   │   ├── config.tmpl
│   │   ├── hook.tmpl
│   │   └── templates.go
│   ├── updater/
│   │   ├── updater.go
│   │   └── updater_test.go
│   └── version/
│       ├── version.go
│       └── version_test.go
├── main.go
├── packaging/
│   ├── .gitignore
│   ├── registries/
│   │   ├── aur/
│   │   │   ├── lefthook/
│   │   │   │   └── PKGBUILD
│   │   │   └── lefthook-bin/
│   │   │       └── PKGBUILD
│   │   ├── npm/
│   │   │   ├── lefthook/
│   │   │   │   ├── bin/
│   │   │   │   │   └── index.js
│   │   │   │   ├── get-exe.js
│   │   │   │   ├── package.json
│   │   │   │   └── postinstall.js
│   │   │   ├── lefthook-darwin-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-darwin-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-freebsd-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-freebsd-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-linux-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-linux-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-openbsd-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-openbsd-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-windows-arm64/
│   │   │   │   └── package.json
│   │   │   └── lefthook-windows-x64/
│   │   │       └── package.json
│   │   ├── npm-bundled/
│   │   │   ├── bin/
│   │   │   │   └── index.js
│   │   │   ├── get-exe.js
│   │   │   ├── package.json
│   │   │   └── postinstall.js
│   │   ├── npm-installer/
│   │   │   ├── bin/
│   │   │   │   └── index.js
│   │   │   ├── install.js
│   │   │   └── package.json
│   │   ├── pypi/
│   │   │   ├── LICENSE
│   │   │   ├── README.md
│   │   │   ├── hatch_build.py
│   │   │   ├── lefthook/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── __main__.py
│   │   │   │   ├── bin/
│   │   │   │   │   └── .keep
│   │   │   │   └── main.py
│   │   │   └── pyproject.toml
│   │   └── rubygems/
│   │       ├── Gemfile
│   │       ├── README.md
│   │       ├── Rakefile
│   │       ├── bin/
│   │       │   └── lefthook
│   │       ├── lefthook.gemspec
│   │       ├── lib/
│   │       │   └── lefthook.rb
│   │       └── libexec/
│   │           └── .keep
│   └── scripts/
│       ├── META6.json
│       ├── clean.raku
│       ├── lib/
│       │   ├── Constants.rakumod
│       │   ├── Packager.rakumod
│       │   ├── Registries/
│       │   │   ├── AUR/
│       │   │   │   └── Publishing.rakumod
│       │   │   ├── AUR-Bin.rakumod
│       │   │   ├── AUR.rakumod
│       │   │   ├── NPM.rakumod
│       │   │   ├── PyPI.rakumod
│       │   │   └── RubyGems.rakumod
│       │   ├── Registry.rakumod
│       │   ├── System.rakumod
│       │   └── SystemAPI.rakumod
│       ├── prepare.raku
│       ├── publish.raku
│       ├── set-version.raku
│       └── t/
│           ├── 01-system.rakutest
│           ├── 02-npm.rakutest
│           ├── 03-rubygems.rakutest
│           ├── 04-pypi.rakutest
│           └── lib/
│               ├── FakeSystem.rakumod
│               └── TestRegistry.rakumod
├── schema.json
├── tea.yaml
└── tests/
    ├── helpers/
    │   ├── cmdtest/
    │   │   ├── cmdtest.go
    │   │   ├── dumb.go
    │   │   ├── ordered.go
    │   │   ├── ordered_test.go
    │   │   ├── tracking.go
    │   │   └── tracking_test.go
    │   ├── configtest/
    │   │   ├── config.go
    │   │   └── config_test.go
    │   └── gittest/
    │       ├── gittest.go
    │       └── gittest_test.go
    └── integration/
        ├── add.txt
        ├── check_install.txt
        ├── cli_run_only.txt
        ├── dump.txt
        ├── env_overwrite_issue_1137.txt
        ├── exclude.txt
        ├── exclude_arg.txt
        ├── fail_on_changes.txt
        ├── fail_on_changes_issue_1125.txt
        ├── fail_on_changes_recover_previous_change.txt
        ├── fail_text.txt
        ├── files_override.txt
        ├── files_skip_if_empty.txt
        ├── filter_by_file_type.txt
        ├── filter_by_mime_type.txt
        ├── group_envs.txt
        ├── hide_unstaged.txt
        ├── install.txt
        ├── install_specific.txt
        ├── job_fail_text.txt
        ├── job_filter_by_file_type.txt
        ├── job_merging.txt
        ├── job_stage_fixed.txt
        ├── lefthook_job_name_issue_1345.txt
        ├── lefthook_option.txt
        ├── many_extends_levels.txt
        ├── min_version.txt
        ├── pre-commit_issue_919.txt
        ├── remotes.txt
        ├── run_deleted_only.txt
        ├── run_interrupt.txt
        ├── run_json.txt
        ├── run_jsonc.txt
        ├── run_non_existing.txt
        ├── run_script.txt
        ├── run_script_with_args.txt
        ├── run_toml.txt
        ├── run_yml.txt
        ├── setup_instructions.txt
        ├── sh_syntax_in_files.txt
        ├── skip_group_issue_1083.txt
        ├── skip_merge_commit.txt
        ├── skip_run.txt
        ├── stage_fixed.txt
        ├── stage_fixed_505.txt
        ├── templates.txt
        ├── timeout.txt
        ├── timeout_success.txt
        ├── uninstall.txt
        ├── validate.txt
        ├── validate_fail.txt
        └── version.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/CODEOWNERS
================================================
* @mrexox


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: 🐞 Report a bug
about: Found something broken? Let us know! If it's not yet reproducible, please `Ask a question` instead.
labels: 'bug'
---

### Description



### `lefthook.yml`

<!-- If the bug relates to some configuration options, please provide a config example -->

### Commands to reproduce

<!-- Don't forget to enable verbose logs. -->
```bash
export LEFTHOOK_VERBOSE=true
```

### Lefthook version

<!-- `lefthook version -f` -->

### Possible solution

<!-- Your ideas -->



================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
---
blank_issues_enabled: false

contact_links:
  - name: 💡 Discuss an idea
    url: https://github.com/evilmartians/lefthook/discussions/new?category=ideas
    about: Suggest a feature or an improvement.

  - name: ❔ Ask a question
    url: https://github.com/evilmartians/lefthook/discussions/new
    about: Ask questions and discuss with other `lefthook` users or maintainers.

  - name: 🙏 Request help
    url: https://github.com/evilmartians/lefthook/discussions/new
    about: Ask the `lefthook` community for help.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: ⭐ Feature request
about: Want something to be implemented in `lefthook`? Create a feature request! If you are not sure, or just have an idea, please `Discuss an idea` instead.
labels: 'feature request'
---

### Description



### What problem it is solving?




================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Closes # (issue)

### Context

<!-- Brief description of what problem PR is solving -->

### Changes

<!-- Summary for changes in the code -->


================================================
FILE: .github/dependabot.yml
================================================
---
version: 2
updates:
  - package-ecosystem: "gomod"
    directory: "/"
    target-branch: "dependencies"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "06:00"  # 6:00 UTC
    commit-message:
      prefix: "deps"
    assignees:
      - "mrexox"


================================================
FILE: .github/workflows/codeql.yml
================================================
name: "CodeQL"

on:
  push:
    branches: [ "master" ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ "master" ]
  schedule:
    # 6:00 UTC on Monday
    - cron: '0 6 * * 1'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'go' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.

        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
        # queries: security-extended,security-and-quality


    # Autobuild attempts to build any compiled languages  (C/C++, C#, Go, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v3

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

    #   If the Autobuild fails above, remove it and uncomment the following three lines.
    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

    # - run: |
    #   echo "Run, Build Application using script"
    #   ./location_of_script_within_repo/buildscript.sh

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3
      with:
        category: "/language:${{matrix.language}}"



================================================
FILE: .github/workflows/gh-pages.yml
================================================
name: Publish Docs

on:
  push:
    branches:
      - master
  pull_request:

jobs:
  gh-pages:
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v2

      - name: Setup docmd
        run: npm install -g @docmd/core@0.4.11

      - run: docmd build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/master' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./site
          cname: lefthook.dev


================================================
FILE: .github/workflows/lint.yml
================================================
on:
  push:
    branches:
      - master
  pull_request:

name: Lint
jobs:
  golangci:
    name: golangci-lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          version-file: .tool-versions


================================================
FILE: .github/workflows/release.yml
================================================
name: release

on:
  push:
    tags:
      - "*"

permissions:
  attestations: write
  contents: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Fetch all tags
        run: git fetch --force --tags

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod

      - name: Install Snapcraft
        uses: samuelmeuli/action-snapcraft@v2

      - name: Prevent from snapcraft fail
        run: |
          mkdir -p $HOME/.cache/snapcraft/download
          mkdir -p $HOME/.cache/snapcraft/stage-packages

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: 'v2.10.2'
          args: release --clean --verbose
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}

      - name: Generate artifact attestations
        uses: actions/attest@v4
        with:
          subject-checksums: dist/lefthook_checksums.txt

      - name: Preserve artifacts permissions with tar
        run: tar -cvf dist.tar dist/
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist.tar

  publish-npm:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: git fetch --force --tags

      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: tar -xvf dist.tar

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - name: Update npm
        run: npm install -g npm@latest

      - uses: Raku/setup-raku@v1
      - name: Publish to NPM
        env:
          NPM_API_KEY: ${{ secrets.NPM_API_KEY }}
        run: |
          raku packaging/scripts/prepare.raku --target=npm
          raku packaging/scripts/publish.raku --target=npm

  publish-gem:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: git fetch --force --tags

      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: tar -xvf dist.tar

      - uses: Raku/setup-raku@v1
      - name: Publish to Rubygems
        env:
          RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
        run: |
          mkdir -p ~/.gem/
          cat << EOF > ~/.gem/credentials
          ---
          :rubygems_api_key: ${RUBYGEMS_API_KEY}
          EOF
          chmod 0600 ~/.gem/credentials
          raku packaging/scripts/prepare.raku --target=rubygems
          raku packaging/scripts/publish.raku --target=rubygems

  publish-pypi:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: git fetch --force --tags

      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: tar -xvf dist.tar

      - name: Setup uv with python
        uses: astral-sh/setup-uv@v7
        with:
          enable-cache: false
          python-version: "3.12"
          version: "latest"

      - uses: Raku/setup-raku@v1
      - name: Publish to PyPI
        env:
          UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_KEY }}
        run: |
          raku packaging/scripts/prepare.raku --target=pypi
          raku packaging/scripts/publish.raku --target=pypi

  publish-homebrew:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Update Homebrew formula
        uses: dawidd6/action-homebrew-bump-formula@v3
        with:
          formula: lefthook
          token: ${{ secrets.HOMEBREW_TOKEN }}

  publish-winget:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Publish to Winget
        uses: vedantmgoyal2009/winget-releaser@v2
        with:
          identifier: evilmartians.lefthook
          fork-user: mrexox
          token: ${{ secrets.WINGET_TOKEN }}

  publish-distro-packages:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: tar -xvf dist.tar

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: python -m pip install --upgrade cloudsmith-cli

      - name: Push packages to Cloudsmith
        env:
          CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
        run: |
          cloudsmith push deb evilmartians/lefthook/any-distro/any-version dist/lefthook_*_amd64.deb
          cloudsmith push deb evilmartians/lefthook/any-distro/any-version dist/lefthook_*_arm64.deb
          cloudsmith push rpm evilmartians/lefthook/any-distro/any-version dist/lefthook_*_amd64.rpm
          cloudsmith push rpm evilmartians/lefthook/any-distro/any-version dist/lefthook_*_arm64.rpm
          cloudsmith push alpine evilmartians/lefthook/alpine/any-version dist/lefthook_*_amd64.apk
          cloudsmith push alpine evilmartians/lefthook/alpine/any-version dist/lefthook_*_arm64.apk

  publish-aur_lefthook:
    needs: build
    runs-on: ubuntu-latest
    container:
      image: archlinux:latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: Raku/setup-raku@v1
      - name: Update AUR package
        run: |
          pacman -Syu --noconfirm
          pacman -S --noconfirm openssh git go base-devel curl

          useradd -m -G wheel runner
          echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

          chown -R runner:runner .

          su runner -c '
            mkdir -p ~/.ssh
            echo "${{ secrets.AUR_SSH_KEY }}" > ~/.ssh/aur
            chmod 600 ~/.ssh/aur
            echo "Host aur.archlinux.org" >> ~/.ssh/config
            echo "  IdentityFile ~/.ssh/aur" >> ~/.ssh/config
            echo "  User aur" >> ~/.ssh/config
            ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts

            raku packaging/scripts/publish.raku --target=aur
          '

  publish-aur_lefthook-bin:
    needs: build
    runs-on: ubuntu-latest
    container:
      image: archlinux:latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: Raku/setup-raku@v1
      - name: Update AUR package
        run: |
          pacman -Syu --noconfirm
          pacman -S --noconfirm openssh git base-devel curl

          useradd -m -G wheel runner
          echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers

          chown -R runner:runner .

          su runner -c '
            mkdir -p ~/.ssh
            echo "${{ secrets.AUR_SSH_KEY }}" > ~/.ssh/aur
            chmod 600 ~/.ssh/aur
            echo "Host aur.archlinux.org" >> ~/.ssh/config
            echo "  IdentityFile ~/.ssh/aur" >> ~/.ssh/config
            echo "  User aur" >> ~/.ssh/config
            ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts

            raku packaging/scripts/publish.raku --target=aur-bin
          '


================================================
FILE: .github/workflows/test.yml
================================================
on:
  push:
    branches:
      - master
  pull_request:

name: Test
jobs:
  unit:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - name: Test
        run: go test $(go list ./... | grep -v '/gen$') -coverprofile coverage.out
      - name: Report coverage
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: unit
          name: ${{ join(matrix.*, ' ') }}

  integration:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    env:
      GOCOVERDIR: ${{ github.workspace }}/_icoverdir_
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - name: Prepare lefthook
        run: |
          mkdir _icoverdir_
          go install -cover
      - name: Run integration tests
        uses: nick-fields/retry@v3
        with:
          timeout_minutes: 5
          max_attempts: 3
          command: go test integration_test.go -tags=integration
      - name: Collect coverage
        run: |
          go tool covdata textfmt -i _icoverdir_ -o coverage.out
      - name: Report coverage
        uses: codecov/codecov-action@v5
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: integration
          name: integration-${{ join(matrix.*, ' ') }}

  packaging:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install Raku
        uses: Raku/setup-raku@v1
      - name: Run packaging tests
        run: |
          cd packaging/scripts/
          zef install . --deps-only
          raku -I lib t/*.rakutest

          raku prepare.raku --target=npm --dry-run
          raku prepare.raku --target=rubygems --dry-run
          raku prepare.raku --target=pypi --dry-run
          raku prepare.raku --target=aur --dry-run
          raku prepare.raku --target=aur-bin --dry-run

          raku publish.raku --target=npm --dry-run
          mkdir -p ../registries/rubygems/pkg
          touch ../registries/rubygems/pkg/lefthook_99.gem
          raku publish.raku --target=rubygems --dry-run
          raku publish.raku --target=pypi --dry-run
          raku publish.raku --target=aur --dry-run
          raku publish.raku --target=aur-bin --dry-run

  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
      - name: Build binaries
        uses: goreleaser/goreleaser-action@v6
        with:
          distribution: goreleaser
          version: '~> v2'
          args: release --snapshot --skip=publish --skip=snapcraft --skip=validate --clean --verbose
      - name: Tar binaries to preserve executable bit
        run: 'tar -cvf lefthook-binaries.tar --directory dist/ $(find dist/ -executable -type f -printf "%P\0" | xargs --null)'
      - name: Upload binaries as artifacts
        uses: actions/upload-artifact@v4
        with:
          name: Executables
          path: lefthook-binaries.tar


================================================
FILE: .gitignore
================================================
/lefthook
/lefthook-local.yml
/bin/
/dist/
/book/
/site/
/vscode/
/.idea/

tmp/


================================================
FILE: .golangci.yml
================================================
version: "2"

linters:
  default: none
  enable:
    - asasalint
    - asciicheck
    - bidichk
    - bodyclose
    - containedctx
    - contextcheck
    - copyloopvar
    - dogsled
    - dupl
    - dupword
    - durationcheck
    - errcheck
    - errchkjson
    - errname
    - errorlint
    - exhaustive
    - forbidigo
    - gochecknoinits
    - goconst
    - gocritic
    - gocyclo
    - godoclint
    - godot
    - godox
    - goheader
    - goprintffuncname
    - govet
    - ineffassign
    - intrange
    - makezero
    - mirror
    - misspell
    - mnd
    - modernize
    - nestif
    - noctx
    - nolintlint
    - perfsprint
    - prealloc
    - predeclared
    - reassign
    - revive
    - staticcheck
    - tagalign
    - usetesting
    - unconvert
    - unparam
    - unused
    - usestdlibvars
    - whitespace
  settings:
    gocritic:
      disabled-checks:
        - hugeParam
      enabled-tags:
        - performance
    govet:
      enable:
        - shadow
    goconst:
      ignore-string-values:
        - "false"
    misspell:
      locale: US
    perfsprint:
      strconcat: false
    revive:
      rules:
        - name: unused-parameter
          disabled: true
    unused:
      field-writes-are-uses: false
      post-statements-are-reads: true
      exported-fields-are-used: false
      local-variables-are-used: false
      generated-is-used: false

formatters:
  enable:
    - gci
    - gofumpt
    - goimports
  settings:
    gci:
      sections:
        - standard
        - default
        - prefix(github.com/evilmartians/lefthook)


================================================
FILE: .goreleaser.yml
================================================
version: 2
project_name: lefthook
before:
  hooks:
    - go generate ./...
builds:
  # Builds the binaries without `lefthook upgrade`
  - id: no_self_update
    tags:
      - no_self_update
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
      - windows
      - freebsd
      - openbsd
    goarch:
      - amd64
      - arm64
      - 386
    ignore:
      - goos: darwin
        goarch: 386
      - goos: linux
        goarch: 386
      - goos: freebsd
        goarch: 386
      - goos: openbsd
        goarch: 386
    flags:
      - -trimpath
    ldflags:
      - -s -w -X github.com/evilmartians/lefthook/internal/version.commit={{.Commit}}

  # Full lefthook binary
  - id: lefthook
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - darwin
      - windows
      - freebsd
      - openbsd
    goarch:
      - amd64
      - arm64
      - 386
    ignore:
      - goos: darwin
        goarch: 386
      - goos: linux
        goarch: 386
      - goos: freebsd
        goarch: 386
      - goos: openbsd
        goarch: 386
    flags:
      - -trimpath
    ldflags:
      - -s -w -X github.com/evilmartians/lefthook/internal/version.commit={{.Commit}}

  - id: lefthook-linux-aarch64
    env:
      - CGO_ENABLED=0
    goos:
      - linux
    goarch:
      - arm64
    flags:
      - -trimpath
    ldflags:
      - -s -w -X github.com/evilmartians/lefthook/internal/version.commit={{.Commit}}

archives:
  - id: lefthook
    formats: [binary]
    ids:
      - lefthook
    files:
      - none*
    name_template: >-
      {{ .ProjectName }}_
      {{- .Version }}_
      {{- if eq .Os "darwin" }}MacOS
      {{- else }}{{ title .Os }}{{ end }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else }}{{ .Arch }}{{ end }}

  - id: lefthook-gz
    formats: [gz]
    ids:
      - lefthook
    files:
    - none*
    name_template: >-
      {{ .ProjectName }}_
      {{- .Version }}_
      {{- if eq .Os "darwin" }}MacOS
      {{- else }}{{ title .Os }}{{ end }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else }}{{ .Arch }}{{ end }}

  - id: lefthook-linux-aarch64
    formats: [binary]
    ids:
      - lefthook-linux-aarch64
    files:
      - none*
    name_template: >-
      {{ .ProjectName }}_
      {{- .Version }}_
      {{- if eq .Os "darwin" }}MacOS
      {{- else }}{{ title .Os }}{{ end }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else if eq .Arch "arm64" }}aarch64
      {{- else }}{{ .Arch }}{{ end }}

  - id: lefthook-linux-aarch64-gz
    formats: [gz]
    ids:
      - lefthook-linux-aarch64
    files:
      - none*
    name_template: >-
      {{ .ProjectName }}_
      {{- .Version }}_
      {{- if eq .Os "darwin" }}MacOS
      {{- else }}{{ title .Os }}{{ end }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else if eq .Arch "arm64" }}aarch64
      {{- else }}{{ .Arch }}{{ end }}

checksum:
  name_template: "{{ .ProjectName }}_checksums.txt"
  algorithm: sha256

snapshot:
  version_template: "{{ .Tag }}"

changelog:
  sort: asc
  filters:
    exclude:
    - '^docs:'
    - '^test:'
    - '^spec:'
    - '^tmp:'
    - '^context:'
    - '^\d+\.\d+\.\d+:'

snapcrafts:
  - summary: Fast and powerful Git hooks manager for any type of projects.
    description: |
      Lefthook is a single dependency-free binary to manage all your git hooks. It works with any language in any environment, and in all common team workflows.
    grade: stable
    confinement: classic
    publish: true
    license: MIT
    ids:
      - no_self_update

nfpms:
  - file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
    homepage:  https://github.com/evilmartians/lefthook
    description: Lefthook a single dependency-free binary to manage all your git hooks that works with any language in any environment, and in all common team workflows
    maintainer: Evil Martians <lefthook@evilmartians.com>
    license: MIT
    vendor: Evil Martians
    ids:
      - no_self_update
    formats:
      - apk
      - deb
      - rpm
    dependencies:
      - git


================================================
FILE: .lefthook.yml
================================================
assert_lefthook_installed: true
skip_lfs: true

output:
  - meta
  - summary
  - jobs

pre-commit:
  parallel: true
  setup:
    - run: command -v typos || brew install typos-cli
    - run: command -v lychee || brew install lychee
  jobs:
    - name: lint & test
      glob: "*.go"
      group:
        jobs:
          - run: make lint
            tags: lint
            stage_fixed: true

          - run: make test
            tags: test

    - name: check links
      tags: docs
      run: lychee --max-concurrency 3 -- {staged_files}
      glob: '*.md'
      exclude:
        - CHANGELOG.md

    - name: fix typos
      tags: lint
      run: typos --write-changes {staged_files}
      exclude:
        - "*.svg"
        - "*.png"
      stage_fixed: true

    - name: update JSON schema
      tags: docs
      run: |
        go generate gen/jsonschema.go > internal/config/jsonschema.json
        go generate gen/jsonschema.go > schema.json
        git add internal/config/jsonschema.json
        git add schema.json
      glob:
        - 'gen/jsonschema.go'
        - 'internal/config/command.go'
        - 'internal/config/config.go'
        - 'internal/config/hook.go'
        - 'internal/config/job.go'
        - 'internal/config/remote.go'
        - 'internal/config/script.go'


================================================
FILE: .tool-versions
================================================
golangci-lint 2.10.1


================================================
FILE: .typos.toml
================================================
[default.extend-identifiers]
"PnP" = "PnP"
[default.extend-words]
slq = "slq"

================================================
FILE: AGENTS.md
================================================
# AGENTS.md

Lefthook is a CLI-first Git hooks manager. Contributions must be predictable,
backwards-compatible, and dependency-light.

## Requirements

- Go 1.26+ (respect `go.mod` toolchain)
- Git, Make
```
make build            # compile
make test             # unit tests
make test-integration # integration tests
make lint             # golangci-lint
make jsonschema       # regenerate schema.json after config changes
```

## Codebase map

| Path | Purpose |
|---|---|
| `cmd/` | CLI commands |
| `internal/config/` | Config parsing, validation, JSON schema |
| `internal/run/` | Hook runner, parallelism |
| `internal/command/` | Top-level orchestrator |
| `internal/git/` | Git utilities |
| `docs/` | documentation source → lefthook.dev |
| `tests/` | Integration/fixture tests |

## Rules

**Errors** — always wrap with context; never silently ignore; no panic in production paths.

**Concurrency** — no goroutine leaks; use `context.Context`; deterministic output when order matters.

**CLI** — preserve exit codes, flag names, and output format. Update docs and tests for any behavior change.

**Config** — edit structs in `internal/config/`, then run `make jsonschema`. Both `schema.json` and `internal/config/jsonschema.json` must be committed.

**Security** — treat user input as untrusted; no unsafe shell concatenation; sanitize paths.

## Testing

Prefer table-driven unit tests. Integration tests should validate CLI behavior and real git interaction — not internal implementation details.

## PR checklist

- [ ] `make lint` passes
- [ ] `make test` passes
- [ ] Docs updated if behavior changed or new config option added

When in doubt, follow existing patterns. Consistency over cleverness.


================================================
FILE: CHANGELOG.md
================================================
# Change log

## 2.1.4 (2026-03-12)

- pkg: fix scripts ([#1348](https://github.com/evilmartians/lefthook/pull/1348)) by [@mrexox](https://github.com/mrexox)
- fix: bring back {lefthook_job_name} template ([#1347](https://github.com/evilmartians/lefthook/pull/1347)) by [@mrexox](https://github.com/mrexox)
- pkg: refactor packaging (2) ([#1346](https://github.com/evilmartians/lefthook/pull/1346)) by [@mrexox](https://github.com/mrexox)
- fix: separate more commands' non-option args with -- ([#1339](https://github.com/evilmartians/lefthook/pull/1339)) by [@scop](https://github.com/scop)
- docs: change logo to point to landing page instead of itself ([#1343](https://github.com/evilmartians/lefthook/pull/1343)) by [@igas](https://github.com/igas)
- pkg: make it easier to read ([#1340](https://github.com/evilmartians/lefthook/pull/1340)) by [@mrexox](https://github.com/mrexox)
- pkg: refactor packaging scripts ([#1308](https://github.com/evilmartians/lefthook/pull/1308)) by [@mrexox](https://github.com/mrexox)

## 2.1.3 (2026-03-07)

- chore: switch artifact attestations gen to actions/attest v4 ([#1338](https://github.com/evilmartians/lefthook/pull/1338)) by [@scop](https://github.com/scop)
- chore: describe ENV variables usage in CLI help output ([#1337](https://github.com/evilmartians/lefthook/pull/1337)) by [@mrexox](https://github.com/mrexox)
- fix: support git debug versions ([#1334](https://github.com/evilmartians/lefthook/pull/1334)) by [@mrexox](https://github.com/mrexox)
- deps: March 2026 ([#1330](https://github.com/evilmartians/lefthook/pull/1330)) by [@mrexox](https://github.com/mrexox)
- feat: update minimum go version ([#1331](https://github.com/evilmartians/lefthook/pull/1331)) by [@mrexox](https://github.com/mrexox)

## 2.1.2 (2026-03-01)

- feat: introduce setup hook option ([#1326](https://github.com/evilmartians/lefthook/pull/1326)) by [@mrexox](https://github.com/mrexox)
- refactor: recovering logic for changesets ([#1324](https://github.com/evilmartians/lefthook/pull/1324)) by [@mrexox](https://github.com/mrexox)
- fix: rollback auto-staged changes if unwanted changes detected ([#1251](https://github.com/evilmartians/lefthook/pull/1251)) by [@tuchfarber](https://github.com/tuchfarber)
- docs: improve docs ui ([#1323](https://github.com/evilmartians/lefthook/pull/1323)) by [@mrexox](https://github.com/mrexox)
- docs: additional skip example and note about reinstallation ([#1319](https://github.com/evilmartians/lefthook/pull/1319)) by [@iloveitaly](https://github.com/iloveitaly)
- docs: fix incorrect --verbose usage ([#1318](https://github.com/evilmartians/lefthook/pull/1318)) by [@iloveitaly](https://github.com/iloveitaly)
- pkg: fix python packages publishing by [@mrexox](https://github.com/mrexox)

## 2.1.1 (2026-02-12)

- ci: fix publishing to PyPi by [@mrexox](https://github.com/mrexox)
- fix: reset colors on config read ([#1309](https://github.com/evilmartians/lefthook/pull/1309)) by [@mrexox](https://github.com/mrexox)
- chore: reduce verbosity of hints in lefthook install ([#1303](https://github.com/evilmartians/lefthook/pull/1303)) by [@joevin-slq-docto](https://github.com/joevin-slq-docto)
- docs: add missing /v2 suffix for go get -tool ([#1304](https://github.com/evilmartians/lefthook/pull/1304)) by [@alexandregv](https://github.com/alexandregv)

## 2.1.0 (2026-02-03)

- ci: skip Python publishing by [@mrexox][]
- chore: fancy wording and indentation for hits by [@mrexox][]
- feat: check core.hooksPath when lefthook install ([#1292](https://github.com/evilmartians/lefthook/pull/1292)) by [@joevin-slq-docto][]
- feat: allow installing non-git hooks ([#1301](https://github.com/evilmartians/lefthook/pull/1301)) by [@mrexox][]

## 2.0.16 (2026-01-27)

- chore: timeout cleanup ([#1297](https://github.com/evilmartians/lefthook/pull/1297)) by [@mrexox](https://github.com/mrexox)
- feat: add timeout argument ([#1263](https://github.com/evilmartians/lefthook/pull/1263)) by [@franzramadhan](https://github.com/franzramadhan)
- deps: January 2026 ([#1285](https://github.com/evilmartians/lefthook/pull/1285)) by [@mrexox](https://github.com/mrexox)
- pkg: pack one binary per platform into python wheels ([#1181](https://github.com/evilmartians/lefthook/pull/1181)) by [@danfimov](https://github.com/danfimov)
- fix: accept string in file_types ([#1288](https://github.com/evilmartians/lefthook/pull/1288)) by [@scop](https://github.com/scop)
- docs: elaborate on when to refetch and failure mode ([#1287](https://github.com/evilmartians/lefthook/pull/1287)) by [@scop](https://github.com/scop)
- fix: try reading direct file instead of all remotes ([#1243](https://github.com/evilmartians/lefthook/pull/1243)) by [@mrexox](https://github.com/mrexox)
- perf: [**breaking**] skip ghost hook when hooks are already configured ([#1255](https://github.com/evilmartians/lefthook/pull/1255)) by [@WooWan](https://github.com/WooWan)
- chore: upgrade to 2.8.0 ([#1278](https://github.com/evilmartians/lefthook/pull/1278)) by [@scop](https://github.com/scop)

## 2.0.15 (2026-01-13)

- docs: clarify remote settings ([#1260](https://github.com/evilmartians/lefthook/pull/1260)) by [@mrexox](https://github.com/mrexox)
- feat: skip scripts if args given with empty file template ([#1277](https://github.com/evilmartians/lefthook/pull/1277)) by [@mrexox](https://github.com/mrexox)

## 2.0.14 (2026-01-12)

- fix: skip if any files template is empty ([#1275](https://github.com/evilmartians/lefthook/pull/1275)) by [@mrexox](https://github.com/mrexox)
- feat: add jsonc support ([#1274](https://github.com/evilmartians/lefthook/pull/1274)) by [@mrexox](https://github.com/mrexox)
- deps: switch from gopkg.in/yaml.v3 to go.yaml.in/yaml/v3 ([#1261](https://github.com/evilmartians/lefthook/pull/1261)) by [@scop](https://github.com/scop)
- fix: don't install custom hooks to hooks dir ([#1246](https://github.com/evilmartians/lefthook/pull/1246)) by [@scop](https://github.com/scop)
- deps: December 2025 ([#1209](https://github.com/evilmartians/lefthook/pull/1209)) by [@mrexox](https://github.com/mrexox)

## 2.0.13 (2025-12-26)

- fix: set extends to empty slice after loading remotes ([#1259](https://github.com/evilmartians/lefthook/pull/1259)) by [@mrexox]()
- fix: allow custom hooks in JSON schema by updating generator ([#1250](https://github.com/evilmartians/lefthook/pull/1250)) by [@jeonghoon11]()
- docs: remove duplicate config: false description ([#1245](https://github.com/evilmartians/lefthook/pull/1245)) by [@scop]()
- chore: add more tests ([#1244](https://github.com/evilmartians/lefthook/pull/1244)) by [@mrexox]()

## 2.0.12 (2025-12-15)

- chore: small changes on diff printing ([#1242](https://github.com/evilmartians/lefthook/pull/1242)) by [@mrexox](https://github.com/mrexox)
- feat: ability to show diff when failing on changes ([#1227](https://github.com/evilmartians/lefthook/pull/1227)) by [@scop](https://github.com/scop)
- fix: make short status parser more robust ([#1236](https://github.com/evilmartians/lefthook/pull/1236)) by [@scop](https://github.com/scop)
- docs: fix readme ([#1235](https://github.com/evilmartians/lefthook/pull/1235)) by [@matdibu](https://github.com/matdibu)

## 2.0.11 (2025-12-12)

- feat: refetch and cleanup on ref change ([#1210](https://github.com/evilmartians/lefthook/pull/1210)) by [@mrexox](https://github.com/mrexox)
- ci: npm trusted publishing ([#1234](https://github.com/evilmartians/lefthook/pull/1234)) by [@mrexox](https://github.com/mrexox)
- feat: more rudimentary shell completions ([#1230](https://github.com/evilmartians/lefthook/pull/1230)) by [@scop](https://github.com/scop)

## 2.0.10 (2025-12-12)

- feat: add no_auto_install to lefthook.yml ([#1231](https://github.com/evilmartians/lefthook/pull/1231)) by [@pavelzw](https://github.com/pavelzw)
- fix: skip if empty files template ([#1233](https://github.com/evilmartians/lefthook/pull/1233)) by [@mrexox](https://github.com/mrexox)

## 2.0.9 (2025-12-08)

- fix: skip pre commit hook if no staged files ([#1229](https://github.com/evilmartians/lefthook/pull/1229)) by [@mrexox](https://github.com/mrexox)
- fix: do not try to hash-object directories ([#1220](https://github.com/evilmartians/lefthook/pull/1220)) by [@scop](https://github.com/scop)
- fix: check and report Scanner errors ([#1222](https://github.com/evilmartians/lefthook/pull/1222)) by [@scop](https://github.com/scop)
- refactor: command executor tweaks ([#1224](https://github.com/evilmartians/lefthook/pull/1224)) by [@scop](https://github.com/scop)
- refactor: remove some redundant code ([#1221](https://github.com/evilmartians/lefthook/pull/1221)) by [@scop](https://github.com/scop)
- fix: improve separation of options and filenames for more git commands ([#1225](https://github.com/evilmartians/lefthook/pull/1225)) by [@scop](https://github.com/scop)
- chore: upgrade golangci-lint to 2.7.1, add godoclint ([#1223](https://github.com/evilmartians/lefthook/pull/1223)) by [@scop](https://github.com/scop)
- chore: remove unnecessary .svg executable permissions ([#1219](https://github.com/evilmartians/lefthook/pull/1219)) by [@scop](https://github.com/scop)

## 2.0.8 (2025-12-05)

- fix: do not escape custom templates in command replacement ([#1213](https://github.com/evilmartians/lefthook/pull/1213)) by [@joevin-sql-docto]()

## 2.0.7 (2025-12-04)

- fix: prefer using lefthook from the $PATH ([#1211](https://github.com/evilmartians/lefthook/pull/1211)) by [@joevin-sql-docto]()

## 2.0.6 (2025-12-03)

- feat: save original executable location in hooks ([#1208](https://github.com/evilmartians/lefthook/pull/1208)) by [@mrexox]()
- docs: encourage python install using pipx ([#1207](https://github.com/evilmartians/lefthook/pull/1207)) by [@franzramadhan]()

## 2.0.5 (2025-12-02)

- feat: add optional args to scripts ([#1206](https://github.com/evilmartians/lefthook/pull/1206)) by [@mrexox]()
- deps: November 2025 ([#1200](https://github.com/evilmartians/lefthook/pull/1200)) by [@mrexox]()
- chore: upgrade golangci-lint to 2.6.1, add modernize ([#1190](https://github.com/evilmartians/lefthook/pull/1190)) by [@scop]()
- chore: publish artifact attestations ([#1189](https://github.com/evilmartians/lefthook/pull/1189)) by [@scop]()

## 2.0.4 (2025-11-13)

- fix: glob_matcher jsonschema values
- feat: add optional standard glob matcher (doublestar) ([#1188](https://github.com/evilmartians/lefthook/pull/1188)) by [@jasonwbarnett]()

## 2.0.3 (2025-11-10)

- feat: fail_on_changes non-ci option ([#1186](https://github.com/evilmartians/lefthook/pull/1186)) by [@scop](https://github.com/scop)
- deps: update mimetypes ([#1185](https://github.com/evilmartians/lefthook/pull/1185)) by [@mrexox](https://github.com/mrexox)

## 2.0.2 (2025-10-29)

- fix: add mutex lock before all git commands ([#1178](https://github.com/evilmartians/lefthook/pull/1178)) by [@mrexox]()

## 2.0.0 (2025-10-20)

**Breaking changes**

- `exclude` option no longer accepts regexp, only globs.
- `skip_output` option is dropped, use `output` instead.
- Some CLI arguments have changed their names to make it more consistent. See `lefthook run -h` for details.
- for `only` and `skip` options with `- run: '...'` values the command executer was changed to Bourne Shell.

**Commits**

- fix: accept --fail-on-changes=false as override value ([#1168](https://github.com/evilmartians/lefthook/pull/1168)) by [@mrexox]()
- feat: [**breaking**] use sh as command executor on Windows ([#1166](https://github.com/evilmartians/lefthook/pull/1166)) by [@mrexox]()
- refactor: [**breaking**] drop support for exclude regexp ([#1162](https://github.com/evilmartians/lefthook/pull/1162)) by [@mrexox]()
- refactor: [**breaking**] drop deprecated skip_output option ([#1159](https://github.com/evilmartians/lefthook/pull/1159)) by [@mrexox]()
- refactor: [**breaking**] use another cli framework ([#1155](https://github.com/evilmartians/lefthook/pull/1155)) by [@mrexox]()

## 1.13.6 (2025-09-30)

- fix: embed jsonschema into binary ([#1158](https://github.com/evilmartians/lefthook/pull/1158)) by [@mrexox]()

## 1.13.5 (2025-09-29)

- chore: a small cleanup by [@mrexox]()
- refactor: use semver to check versions ([#1152](https://github.com/evilmartians/lefthook/pull/1152)) by [@mrexox]()
- fix: add comprehensive tests for spinner name formatting ([#1145](https://github.com/evilmartians/lefthook/pull/1145)) [@technicalpickles]()
- docs: add LEFTHOOK_BIN environment variable to documentation ([#1151](https://github.com/evilmartians/lefthook/pull/1151)) [@technicalpickles]()
- chore: tests improvements ([#1148](https://github.com/evilmartians/lefthook/pull/1148)) by [@mrexox]()
- chore: fix naming for integration tests ([#1146](https://github.com/evilmartians/lefthook/pull/1146)) by [@mrexox]()
- docs: use codecov coverage badge by [@mrexox]()
- ci: codecov ([#1147](https://github.com/evilmartians/lefthook/pull/1147)) by [@mrexox]()
- docs: use actual latest version ([#1143](https://github.com/evilmartians/lefthook/pull/1143)) by [@mrexox]()
- docs: add exclude to hook-level settings by [@mrexox]()

## 1.13.4 (2025-09-23)

- fix: add exclude option to hook level ([#1141](https://github.com/evilmartians/lefthook/pull/1141)) by [@mrexox]()
- fix: allow skipping groups ([#1140](https://github.com/evilmartians/lefthook/pull/1140)) by [@mrexox]()

## 1.13.3 (2025-09-23)

- deps: September 2025 ([#1139](https://github.com/evilmartians/lefthook/pull/1139)) by [@mrexox]()
- fix: concurrent map access issue ([#1138](https://github.com/evilmartians/lefthook/pull/1138)) by [@mrexox]()

## 1.13.2 (2025-09-22)

- feat: inherit file_types from parent jobs ([#1135](https://github.com/evilmartians/lefthook/pull/1135)) by [@mrexox]()
- fix: move gen at root ([#1133](https://github.com/evilmartians/lefthook/pull/1133)) by [@mrexox]()
- refactor: better scope subpackages ([#1132](https://github.com/evilmartians/lefthook/pull/1132)) by [@mrexox]()

## 1.13.1 (2025-09-17)

- feat: add no stage fixed argument ([#1130](https://github.com/evilmartians/lefthook/pull/1130)) by [@mrexox]()
- refactor: reduce the amount of code in a single file ([#1131](https://github.com/evilmartians/lefthook/pull/1131)) by [@mrexox]()
- fix: re-evaluate status for changeset ([#1129](https://github.com/evilmartians/lefthook/pull/1129)) by [@mrexox]()
- refactor: reduce the amount of code in a single file ([#1118](https://github.com/evilmartians/lefthook/pull/1118)) by [@mrexox]()
- chore: update issue templates by [@mrexox](https://github.com/mrexox)
- docs: add fail_on_changes to configuration/README.md ([#1119](https://github.com/evilmartians/lefthook/pull/1119)) by [@7crabs](https://github.com/7crabs)
- docs: update go installation note ([#1117](https://github.com/evilmartians/lefthook/pull/1117)) by [@leakedmemory](https://github.com/leakedmemory)


## 1.13.0 (2025-09-11)

- fix: use batched cmd for calculating git hashes ([#1116](https://github.com/evilmartians/lefthook/pull/1116)) by [@mrexox]()
- fix: add mutex to prevent concurrent git adds ([#1115](https://github.com/evilmartians/lefthook/pull/1115)) by [@mrexox]()
- refactor: improve structuring ([#1103](https://github.com/evilmartians/lefthook/pull/1103)) by [@mrexox]()
- feat: fail on change ([#1095](https://github.com/evilmartians/lefthook/pull/1095)) by [@olivier-lacroix]()
- fix: set --force for git add command ([#1104](https://github.com/evilmartians/lefthook/pull/1104)) by [@michaelm]()
- feat: recursively log successful results in summary ([#1108](https://github.com/evilmartians/lefthook/pull/1108)) by [@siler]()
- fix: groups with successes and skips are successful ([#1107](https://github.com/evilmartians/lefthook/pull/1107)) by [@siler]()

## 1.12.4 (2025-09-05)

- deps: September 2025 ([#1102](https://github.com/evilmartians/lefthook/pull/1102)) by [@mrexox]()
- feat: add tags argument ([#1101](https://github.com/evilmartians/lefthook/pull/1101)) by [@mrexox]()
- chore: bump github.com/go-viper/mapstructure/v2 ([#1094](https://github.com/evilmartians/lefthook/pull/1094))

## 1.12.3 (2025-08-12)

- feat: add MIME types to file_types filters ([#1092](https://github.com/evilmartians/lefthook/pull/1092))
- fix: respect LEFTHOOK_CONFIG in lefthook install ([#1090](https://github.com/evilmartians/lefthook/pull/1090)) by [@TECHNOFAB11](https://github.com/TECHNOFAB11)
- docs: update pnpm installation note ([#1089](https://github.com/evilmartians/lefthook/pull/1089)) by [@skoch13](https://github.com/skoch13)
- docs: improve wording of `run`, `files`, and `files-global` config descriptions, document that the `sh` shell is used ([#1086](https://github.com/evilmartians/lefthook/pull/1086)) by [@ItsHarper](https://github.com/ItsHarper)
- docs: 404 for local-config ([#1082](https://github.com/evilmartians/lefthook/pull/1082)) by [@rammanoj](https://github.com/rammanoj)
- docs: fix typo ([#1079](https://github.com/evilmartians/lefthook/pull/1079)) by [@eai04191](https://github.com/eai04191)

## 1.12.2 (2025-07-11)

- feat: add implicit template lefthook_job_name ([#1074](https://github.com/evilmartians/lefthook/pull/1074))
- docs: restructure documentation ([#1075](https://github.com/evilmartians/lefthook/pull/1075)) by [@mrexox](https://github.com/mrexox)
- feat: allow overriding config path using LEFTHOOK_CONFIG env ([#1072](https://github.com/evilmartians/lefthook/pull/1072)) by [@TECHNOFAB11](https://github.com/TECHNOFAB11)

## 1.12.1 (2025-07-09)

- feat: add check-install command ([#1064](https://github.com/evilmartians/lefthook/pull/1064)) by [@mrexox](https://github.com/mrexox)
- chore: only check if local configs exist by [@mrexox](https://github.com/mrexox)
- feat: allow using local config only ([#1071](https://github.com/evilmartians/lefthook/pull/1071)) by [@sj26](https://github.com/sj26)

## 1.12.0 (2025-07-08)

- feat: allow installing only specific hooks ([#1069](https://github.com/evilmartians/lefthook/pull/1069))
- refactor: [**breaking**] restructure files and folders, remove deprecated options ([#1067](https://github.com/evilmartians/lefthook/pull/1067))

## 1.11.16 (2025-07-03)

- fix: race condition on repo state ([#1066](https://github.com/evilmartians/lefthook/pull/1066))

## 1.11.15 (2025-07-03)

- feat: add exclude arg ([#1063](https://github.com/evilmartians/lefthook/pull/1063))
- feat: inherit group envs ([#1061](https://github.com/evilmartians/lefthook/pull/1061))
- fix: apply implicit staged files filter to all files when all files arg given ([#1062](https://github.com/evilmartians/lefthook/pull/1062))
- deps: bump github.com/kaptinlin/jsonschema to 0.4.5
- deps: bump github.com/knadh/koanf/parsers/yaml to 1.1.0
- deps: bump github.com/knadh/koanf/v2 to 2.2.1 ([#1043](https://github.com/evilmartians/lefthook/pull/1043))
- fix: friendlier updater error message
- fix: bump goreleaser

## 1.11.14 (2025-06-16)

- feat: show time for jobs ([#1044](https://github.com/evilmartians/lefthook/pull/1044)) by [@adeebshihadeh](https://github.com/adeebshihadeh)
- ci: update GoReleaser configurations ([#1040](https://github.com/evilmartians/lefthook/pull/1040)) by [@emmanuel-ferdman](https://github.com/emmanuel-ferdman)
- feat: support devbox ([#1031](https://github.com/evilmartians/lefthook/pull/1031)) by [@misogihagi](https://github.com/misogihagi)
- chore: regexp use improvements ([#1034](https://github.com/evilmartians/lefthook/pull/1034)) by [@scop](https://github.com/scop)
- chore: upgrade golangci-lint to v2, address findings ([#1027](https://github.com/evilmartians/lefthook/pull/1027)) by [@scop](https://github.com/scop)

## 1.11.13 (2025-05-16)

- deps: May 2025 ([#1024](https://github.com/evilmartians/lefthook/pull/1024)) by [@mrexox](https://github.com/mrexox)
- fix: load scripts from .config too ([#1018](https://github.com/evilmartians/lefthook/pull/1018)) by [@mrexox](https://github.com/mrexox)
- chore: change "existed" to "existing" ([#1022](https://github.com/evilmartians/lefthook/pull/1022)) by [@assyrus-favolo](https://github.com/assyrus-favolo)
- docs: fix grammatical error in `Local config` section ([#1019](https://github.com/evilmartians/lefthook/pull/1019)) by [@dev-kas](https://github.com/dev-kas)

## 1.11.12 (2025-04-28)

- feat: load from .config dir ([#1017](https://github.com/evilmartians/lefthook/pull/1017)) by [@mrexox](https://github.com/mrexox)
- feat: complete all job names, recursively ([#1015](https://github.com/evilmartians/lefthook/pull/1015)) by [@scop](https://github.com/scop)
- docs: update links to mise by [@mrexox](https://github.com/mrexox)

## 1.11.11 (2025-04-21)

- deps: koanf and jsonschema ([#1013](https://github.com/evilmartians/lefthook/pull/1013)) by [@mrexox](https://github.com/mrexox)
- feat: add support for mise ([#1007](https://github.com/evilmartians/lefthook/pull/1007)) by [@shahar-py](https://github.com/shahar-py)

## 1.11.10 (2025-04-14)

- deps: bump github.com/pelletier/go-toml/v2 from 2.2.3 to 2.2.4 ([#1005](https://github.com/evilmartians/lefthook/pull/1005)) ([#1006](https://github.com/evilmartians/lefthook/pull/1006)) by [@mrexox](https://github.com/mrexox)
- feat: add support for uv ([#1004](https://github.com/evilmartians/lefthook/pull/1004)) by [@toshok](https://github.com/toshok)

## 1.11.9 (2025-04-11)

- fix: better logging ([#1003](https://github.com/evilmartians/lefthook/pull/1003)) by [@mrexox](https://github.com/mrexox)
- feat: allow installing hooks in CI ([#1001](https://github.com/evilmartians/lefthook/pull/1001)) by [@caugner](https://github.com/caugner)
- deps: Dependencies upgrade [@mrexox](https://github.com/mrexox)

## 1.11.8 (2025-04-08)

- fix: sh lookup on Windows ([#997](https://github.com/evilmartians/lefthook/pull/997)) by [@mrexox](https://github.com/mrexox)
- fix: fix command execution error on Windows #989 ([#992](https://github.com/evilmartians/lefthook/pull/992)) by [@atsushifx](https://github.com/atsushifx)

## 1.11.7 (2025-04-07)

- fix: avoid error logging when determining pre push files ([#995](https://github.com/evilmartians/lefthook/pull/995)) by [@mrexox](https://github.com/mrexox)
- docs: allow duplicate files in SUMMARY ([#988](https://github.com/evilmartians/lefthook/pull/988)) by [@mrexox](https://github.com/mrexox)
- fix: unquote paths to valid UTF-8 ([#987](https://github.com/evilmartians/lefthook/pull/987)) by [@mrexox](https://github.com/mrexox)
- packaging: aur fixes ([#985](https://github.com/evilmartians/lefthook/pull/985)) by [@mrexox](https://github.com/mrexox)

## 1.11.6 (2025-03-31)

- fix: print git errors  ([#984](https://github.com/evilmartians/lefthook/pull/984)) by [@mrexox](https://github.com/mrexox)
- packaging: maintain lefthook-bin AUR package ([#982](https://github.com/evilmartians/lefthook/pull/982)) by [@mrexox](https://github.com/mrexox)
- chore: fancier logging ([#983](https://github.com/evilmartians/lefthook/pull/983)) by [@mrexox](https://github.com/mrexox)
- docs: remove a note about the difference for unix-like and windows by [@mrexox](https://github.com/mrexox)

## 1.11.5 (2025-03-25)

- fix: windows scripts issues ([#979](https://github.com/evilmartians/lefthook/pull/979)) by [@mrexox](https://github.com/mrexox)

## 1.11.4 (2025-03-24)

- feat: support lefthook as go tool ([#976](https://github.com/evilmartians/lefthook/pull/976)) by [@nmoniz](https://github.com/nmoniz)
- fix: use dedicated build path for swift plugin ([#978](https://github.com/evilmartians/lefthook/pull/978)) by [@csjones](https://github.com/csjones)
- deps: March 2025 ([#977](https://github.com/evilmartians/lefthook/pull/977)) by [@mrexox](https://github.com/mrexox)
- docs: update pnpm install command in the installation guide ([#974](https://github.com/evilmartians/lefthook/pull/974)) by [@hoosierhuy](https://github.com/hoosierhuy)

## 1.11.3 (2025-03-07)

- fix: remote cloning issues ([#969](https://github.com/evilmartians/lefthook/pull/969)) by [@mrexox](https://github.com/mrexox)

## 1.11.2 (2025-02-26)

- fix: do not inherit envs in remote Git commands ([#963](https://github.com/evilmartians/lefthook/pull/963)) by [@mrexox](https://github.com/mrexox)

## 1.11.1 (2025-02-25)

- fix: remote issue with worktrees ([#960](https://github.com/evilmartians/lefthook/pull/960)) by [@mrexox](https://github.com/mrexox)

## 1.11.0 (2025-02-23)

- perf: speed up git commands ([#956](https://github.com/evilmartians/lefthook/pull/956)) by [@judofyr](https://github.com/judofyr)

## 1.10.11 (2025-02-21)

- deps: bump github.com/spf13/cobra from 1.8.1 to 1.9.1 ([#952](https://github.com/evilmartians/lefthook/pull/952)) ([#958](https://github.com/evilmartians/lefthook/pull/958)) by [@mrexox](https://github.com/mrexox)
- fix: add $schema property ([#942](https://github.com/evilmartians/lefthook/pull/942)) by [@mst-mkt](https://github.com/mst-mkt)
- deps: bump github.com/briandowns/spinner from 1.23.1 to 1.23.2 ([#935](https://github.com/evilmartians/lefthook/pull/935)) ([#940](https://github.com/evilmartians/lefthook/pull/940)) by [@mrexox](https://github.com/mrexox)

## 1.10.10 (2025-01-21)

- feat: allow providing a list of globs ([#937](https://github.com/evilmartians/lefthook/pull/937)) by [@mrexox](https://github.com/mrexox)
- fix: properly inherit exclude options when not overwritten ([#936](https://github.com/evilmartians/lefthook/pull/936)) by [@mrexox](https://github.com/mrexox)

## 1.10.9 (2025-01-20)

- fix: make uninstall --remove-configs description more accurate ([#934](https://github.com/evilmartians/lefthook/pull/934)) by [@scop](https://github.com/scop)

## 1.10.8 (2025-01-17)

- feat: add custom plain templates ([#930](https://github.com/evilmartians/lefthook/pull/930)) by [@mrexox](https://github.com/mrexox)
- fix: unique names for nested operations ([#931](https://github.com/evilmartians/lefthook/pull/931)) by [@mrexox](https://github.com/mrexox)

## 1.10.7 (2025-01-15)

- fix: use lefthook option in ghost hook too ([#929](https://github.com/evilmartians/lefthook/pull/929)) by [@mrexox](https://github.com/mrexox)
- feat: add schema.json to npm packages ([#928](https://github.com/evilmartians/lefthook/pull/928)) by [@mrexox](https://github.com/mrexox)
- fix: increase timeout for self-update to 2 mins by [@mrexox](https://github.com/mrexox)

## 1.10.5 (2025-01-14)

- feat: add lefthook option for custom path or command ([#927](https://github.com/evilmartians/lefthook/pull/927)) by [@mrexox](https://github.com/mrexox)
- chore: update config template with new jobs by [@mrexox](https://github.com/mrexox)

## 1.10.4 (2025-01-13)

- fix: avoid skipping pre commit when deleted files staged ([#925](https://github.com/evilmartians/lefthook/pull/925)) by [@mrexox](https://github.com/mrexox)
- fix: use roots from jobs for possible npm package location ([#924](https://github.com/evilmartians/lefthook/pull/924)) by [@mrexox](https://github.com/mrexox)
- deps: January 2025 ([#926](https://github.com/evilmartians/lefthook/pull/926)) by [@mrexox](https://github.com/mrexox)

## 1.10.3 (2025-01-10)

- fix: replace cmd in jobs ([#918](https://github.com/evilmartians/lefthook/pull/918)) by [@mrexox](https://github.com/mrexox)

## 1.10.2 (2025-01-10)

- feat: add validate command ([#915](https://github.com/evilmartians/lefthook/pull/915)) by [@mrexox](https://github.com/mrexox)
- feat: inherit exclude option in groups ([#916](https://github.com/evilmartians/lefthook/pull/916)) by [@mrexox](https://github.com/mrexox)
- chore: auto generate json schema ([#914](https://github.com/evilmartians/lefthook/pull/914)) by [@mrexox](https://github.com/mrexox)
- feat: run --jobs completion ([#913](https://github.com/evilmartians/lefthook/pull/913)) by [@scop](https://github.com/scop)
- ci: add gzipped linux aarch64 binary to release artifacts ([#908](https://github.com/evilmartians/lefthook/pull/908)) by [@mrexox](https://github.com/mrexox)
-
## 1.10.1 (2024-12-26)

- feat: add ability to specify job names for command run ([#904](https://github.com/evilmartians/lefthook/pull/904)) by [@mrexox](https://github.com/mrexox)
- ci: add linux aarch64 binary to release ([#903](https://github.com/evilmartians/lefthook/pull/903)) by [@mrexox](https://github.com/mrexox)
- ci: fix aur build ([#905](https://github.com/evilmartians/lefthook/pull/905)) by [@mrexox](https://github.com/mrexox)

## 1.10.0 (2024-12-19)

- feat: add jobs option ([#861](https://github.com/evilmartians/lefthook/pull/861)) by [@mrexox](https://github.com/mrexox)
- ci: automate aur package update ([#899](https://github.com/evilmartians/lefthook/pull/899)) by [@mrexox](https://github.com/mrexox)

## 1.9.3 (2024-12-18)

- fix: correctly parse config options ([#895](https://github.com/evilmartians/lefthook/pull/895)) by [@mrexox](https://github.com/mrexox)
- chore: add mdbook ([#894](https://github.com/evilmartians/lefthook/pull/894)) by [@mrexox](https://github.com/mrexox)

## 1.9.2 (2024-12-12)

- fix: use correct remote scripts folder ([#891](https://github.com/evilmartians/lefthook/pull/891)) by [@mrexox](https://github.com/mrexox)

## 1.9.1 (2024-12-12)

- fix: skip_lfs config option ([#889](https://github.com/evilmartians/lefthook/pull/889)) by [@zachahn](https://github.com/zachahn)

## 1.9.0 (2024-12-06)

- chore: add minimum git version support warning ([#886](https://github.com/evilmartians/lefthook/pull/886)) by [@mrexox](https://github.com/mrexox)
- fix: reorder available hooks list ([#884](https://github.com/evilmartians/lefthook/pull/884)) by [@scop](https://github.com/scop)
- docs: correct typo in 'Scoop for Windows' section ([#883](https://github.com/evilmartians/lefthook/pull/883)) by [@Daniil-Oberlev](https://github.com/Daniil-Oberlev)
- refactor: [**breaking**] replace viper with koanf ([#813](https://github.com/evilmartians/lefthook/pull/813)) by [@mrexox](https://github.com/mrexox)
- ci: fix packages release ([#881](https://github.com/evilmartians/lefthook/pull/881)) by [@mrexox](https://github.com/mrexox)

## 1.8.5 (2024-12-02)

- ci: automate publishing to cloudsmith ([#875](https://github.com/evilmartians/lefthook/pull/875)) by [@mrexox](https://github.com/mrexox)
- feat: add option to skip running LFS hooks ([#879](https://github.com/evilmartians/lefthook/pull/879)) by [@zachah](https://github.com/zachah)

## 1.8.4 (2024-11-18)

- ci: fix goreleaser update changes ([#874](https://github.com/evilmartians/lefthook/pull/874)) by [@mrexox](https://github.com/mrexox)
- deps: November 2024 ([#867](https://github.com/evilmartians/lefthook/pull/867)) by [@mrexox](https://github.com/mrexox)
- docs: add docs for fnm configuration ([#869](https://github.com/evilmartians/lefthook/pull/869)) by [@vasylnahuliak](https://github.com/vasylnahuliak)
- docs: add `output` to list of config options ([#868](https://github.com/evilmartians/lefthook/pull/868)) by [@cr7pt0gr4ph7](https://github.com/cr7pt0gr4ph7)

## 1.8.3 (2024-11-18)

- fix: use absolute paths when cloning remotes ([#873](https://github.com/evilmartians/lefthook/pull/873)) by [@mrexox](https://github.com/mrexox)

## 1.8.2 (2024-10-29)

- chore: fix linter and tests by [@mrexox](https://github.com/mrexox)
- feat: add refetch_frequency parameter to settings ([#857](https://github.com/evilmartians/lefthook/pull/857)) by [@gabriel-ss](https://github.com/gabriel-ss)
- docs: call commitizen properly ([#858](https://github.com/evilmartians/lefthook/pull/858)) by [@politician](https://github.com/politician)

## 1.8.1 (2024-10-23)

- chore: bump Go to 1.23 ([#856](https://github.com/evilmartians/lefthook/pull/856)) by Valentin Kiselev
- fix: skip git lfs hook when calling manually ([#855](https://github.com/evilmartians/lefthook/pull/855)) by Valentin Kiselev

## 1.8.0 (2024-10-22)

- fix: [**breaking**] don't auto-install lefthook with npx if not found ([#602](https://github.com/evilmartians/lefthook/pull/602)) by [@anthony-hayes](https://github.com/anthony-hayes)
- fix: [**breaking**] execute files command within configured root ([#607](https://github.com/evilmartians/lefthook/pull/607)) by [@mrexox](https://github.com/mrexox)
- fix: calculate hashsum of the full config ([#854](https://github.com/evilmartians/lefthook/pull/854)) by [@mrexox](https://github.com/mrexox)
- feat: support globs in extends ([#853](https://github.com/evilmartians/lefthook/pull/853)) by [@mrexox](https://github.com/mrexox)
- docs: simplify configuration docs ([#851](https://github.com/evilmartians/lefthook/pull/851)) by [@mrexox](https://github.com/mrexox)

## 1.7.22 (2024-10-18)

- feat: add skip option merge-commit ([#850](https://github.com/evilmartians/lefthook/pull/850)) by [@mrexox](https://github.com/mrexox)
- ci: parallelize publishing ([#847](https://github.com/evilmartians/lefthook/pull/847)) by [@mrexox](https://github.com/mrexox)
- fix: increase self update download timeout ([#849](https://github.com/evilmartians/lefthook/pull/849)) by [@mrexox](https://github.com/mrexox)
- docs: update docs with new packages ([#848](https://github.com/evilmartians/lefthook/pull/848)) by [@mrexox](https://github.com/mrexox)

## 1.7.21 (2024-10-17)

- feat: maintain Python package too ([#845](https://github.com/evilmartians/lefthook/pull/845)) by [@mrexox](https://github.com/mrexox)
- ci: generate apk files ([#843](https://github.com/evilmartians/lefthook/pull/843)) by [@mrexox](https://github.com/mrexox)
- docs: mention to uninstall npm package ([#842](https://github.com/evilmartians/lefthook/pull/842)) by [@mrexox](https://github.com/mrexox)
- chore: hide remaining wiki links ([#841](https://github.com/evilmartians/lefthook/pull/841)) by [@midskyey](https://github.com/midskyey)
- docs: update info about merge order ([#838](https://github.com/evilmartians/lefthook/pull/838)) by [@mrexox](https://github.com/mrexox)
- docs: actualize ([#831](https://github.com/evilmartians/lefthook/pull/831)) by [@mrexox](https://github.com/mrexox)

## 1.7.19 and 1.7.20 – failed to build

## 1.7.18 (2024-09-30)

- fix: force remote name origin when using remotes ([#830](https://github.com/evilmartians/lefthook/pull/830)) by [@mrexox](https://github.com/mrexox)
- deps: September 2024 ([#829](https://github.com/evilmartians/lefthook/pull/829)) by [@mrexox](https://github.com/mrexox)

## 1.7.17 (2024-09-26)

- feat: skip LFS hooks when pre-push hook is skipped ([#818](https://github.com/evilmartians/lefthook/pull/818)) by [@zachahn](https://github.com/zachahn)

## 1.7.16 (2024-09-23)

- chore: enhance some code parts ([#824](https://github.com/evilmartians/lefthook/pull/824)) by [@mrexox](https://github.com/mrexox)
- fix: quote script path ([#823](https://github.com/evilmartians/lefthook/pull/823)) by [@mrexox](https://github.com/mrexox)
- docs: fix typo for command names in configuration.md ([#814](https://github.com/evilmartians/lefthook/pull/814)) by [@nack43](https://github.com/nack43)

## 1.7.15 (2024-09-02)

- fix: add better colors control ([#812](https://github.com/evilmartians/lefthook/pull/812)) by [@mrexox](https://github.com/mrexox)
- deps: August 2024 ([#802](https://github.com/evilmartians/lefthook/pull/802)) by [@mrexox](https://github.com/mrexox)

## 1.7.14 (2024-08-17)

Fix lefthook NPM package to include OpenBSD package as optional dependency.

## 1.7.13 (2024-08-16)

- feat: support openbsd ([#808](https://github.com/evilmartians/lefthook/pull/808)) by [@mrexox](https://github.com/mrexox)

## 1.7.12 (2024-08-09)

- fix: log stderr in debug logs only ([#804](https://github.com/evilmartians/lefthook/pull/804)) by [@mrexox](https://github.com/mrexox)

## 1.7.11 (2024-07-29)

- fix: revert packaging change ([#796](https://github.com/evilmartians/lefthook/pull/796)) by [@mrexox](https://github.com/mrexox)

## 1.7.10 (2024-07-29)

- deps: July 2024 ([#795](https://github.com/evilmartians/lefthook/pull/795)) by [@mrexox](https://github.com/mrexox)
- packaging(npm): try direct reference for lefthook executable ([#794](https://github.com/evilmartians/lefthook/pull/794)) by [@mrexox](https://github.com/mrexox)

## 1.7.9 (2024-07-26)

- fix: typo CGO_ENABLED instead of GCO_ENABLED ([#791](https://github.com/evilmartians/lefthook/pull/791)) by [@mrexox](https://github.com/mrexox)

## 1.7.8 (2024-07-26)

- fix: npm fix packages ([#789](https://github.com/evilmartians/lefthook/pull/789)) by [@mrexox](https://github.com/mrexox)
- fix: explicitly pass static flag to linker ([#788](https://github.com/evilmartians/lefthook/pull/788)) by [@mrexox](https://github.com/mrexox)
- ci: update workflow files ([#787](https://github.com/evilmartians/lefthook/pull/787)) by [@mrexox](https://github.com/mrexox)
- ci: use latest goreleaser ([#784](https://github.com/evilmartians/lefthook/pull/784)) by [@mrexox](https://github.com/mrexox)

## 1.7.7 (2024-07-24)

- fix: multiple excludes ([#782](https://github.com/evilmartians/lefthook/pull/782)) by [@mrexox](https://github.com/mrexox)

## 1.7.6 (2024-07-24)

- feat: add self-update command ([#778](https://github.com/evilmartians/lefthook/pull/778)) by [@mrexox](https://github.com/mrexox)

## 1.7.5 (2024-07-22)

- feat: use glob in exclude array ([#777](https://github.com/evilmartians/lefthook/pull/777)) by [@mrexox](https://github.com/mrexox)

## 1.7.4 (2024-07-19)

- fix: rollback packaging changes ([#776](https://github.com/evilmartians/lefthook/pull/776)) by [@mrexox](https://github.com/mrexox)

## 1.7.3 (2024-07-18)

- feat: allow list of files in exclude option ([#772](https://github.com/evilmartians/lefthook/pull/772)) by [@mrexox](https://github.com/mrexox)
- docs: add docs for LEFTHOOK_OUTPUT var ([#771](https://github.com/evilmartians/lefthook/pull/771)) by [@manbearwiz](https://github.com/manbearwiz)
- fix: use direct lefthook package ([#774](https://github.com/evilmartians/lefthook/pull/774)) by [@mrexox](https://github.com/mrexox)

## 1.7.2 (2024-07-11)

- fix: add missing sub directory in hook template ([#768](https://github.com/evilmartians/lefthook/pull/768)) by [@nikeee](https://github.com/nikeee)

## 1.7.1 (2024-07-08)

- fix: use correct extension in hook.tmpl ([#767](https://github.com/evilmartians/lefthook/pull/767)) by [@apfohl](https://github.com/apfohl)

## 1.7.0 (2024-07-08)

- fix: publishing ([#765](https://github.com/evilmartians/lefthook/pull/765)) by [@mrexox](https://github.com/mrexox)
- perf: startup time reduce ([#705](https://github.com/evilmartians/lefthook/pull/705)) by [@dalisoft](https://github.com/dalisoft)
- docs: add a note about pnpm package installation ([#761](https://github.com/evilmartians/lefthook/pull/761)) by [@mrexox](https://github.com/mrexox)
- ci: retriable integrity tests ([#758](https://github.com/evilmartians/lefthook/pull/758)) by [@mrexox](https://github.com/mrexox)
- ci: universal publisher with Ruby script ([#756](https://github.com/evilmartians/lefthook/pull/756)) by [@mrexox](https://github.com/mrexox)

## 1.6.18 (2024-06-21)

- fix: allow multiple levels of extends ([#755](https://github.com/evilmartians/lefthook/pull/755)) by [@mrexox](https://github.com/mrexox)

## 1.6.17 (2024-06-20)

- fix: apply local extends only if they are present ([#754](https://github.com/evilmartians/lefthook/pull/754)) by [@mrexox](https://github.com/mrexox)
- chore: setting proper error message for missing lefthook file ([#748](https://github.com/evilmartians/lefthook/pull/748)) by [@Cadienvan](https://github.com/Cadienvan)

## 1.6.16 (2024-06-13)

- fix: skip overwriting hooks when fetching data from remotes ([#745](https://github.com/evilmartians/lefthook/pull/745)) by [@mrexox](https://github.com/mrexox)
- fix: fetch remotes only for non ghost hooks ([#744](https://github.com/evilmartians/lefthook/pull/744)) by [@mrexox](https://github.com/mrexox)

## 1.6.15 (2024-06-03)

- feat: add refetch option to remotes config ([#739](https://github.com/evilmartians/lefthook/pull/739)) by [@mrexox](https://github.com/mrexox)
- deps: June, 3, lipgloss (0.11.0) and viper (1.19.0) ([#742](https://github.com/evilmartians/lefthook/pull/742)) by [@mrexox](https://github.com/mrexox)
- chore: enable copyloopvar, intrange, and prealloc ([#740](https://github.com/evilmartians/lefthook/pull/740)) by [@scop](https://github.com/scop)
- perf: delay git and uname commands in hook scripts until needed ([#737](https://github.com/evilmartians/lefthook/pull/737)) by [@scop](https://github.com/scop)
- chore: refactor commands interfaces ([#735](https://github.com/evilmartians/lefthook/pull/735)) by [@mrexox](https://github.com/mrexox)
- chore: upgrade to 1.59.0 ([#738](https://github.com/evilmartians/lefthook/pull/738)) by [@scop](https://github.com/scop)

## 1.6.14 (2024-05-30)

- fix: share STDIN across different commands on pre-push hook ([#732](https://github.com/evilmartians/lefthook/pull/732)) by [@tdesveaux](https://github.com/tdesveaux) and [@mrexox](https://github.com/mrexox)

## 1.6.13 (2024-05-27)

- feat: expand Swift integration with Mint support ([#724](https://github.com/evilmartians/lefthook/pull/724)) by [@levibostian](https://github.com/levibostian)
- deps: May 22 dependencies update ([#706](https://github.com/evilmartians/lefthook/pull/706)) by [@mrexox](https://github.com/mrexox)
- chore: remove go patch version in go.mod ([#726](https://github.com/evilmartians/lefthook/pull/726)) by [@mrexox](https://github.com/mrexox)

# 1.6.12 (2024-05-17)

- fix: more verbose error on versions mismatch ([#721](https://github.com/evilmartians/lefthook/pull/721)) by [@mrexox](https://github.com/mrexox)
- fix: enable interactive scripts ([#720](https://github.com/evilmartians/lefthook/pull/720)) by [@mrexox](https://github.com/mrexox)

## 1.6.11 (2024-05-13)

- feat: add run --no-auto-install flag ([#716](https://github.com/evilmartians/lefthook/pull/716)) by [@mrexox](https://github.com/mrexox)
- fix: add `--porcelain` to `git status --short` ([#711](https://github.com/evilmartians/lefthook/pull/711)) by [@110y](https://github.com/110y)
- chore: bump go to 1.22 ([#701](https://github.com/evilmartians/lefthook/pull/701)) by [@mrexox](https://github.com/mrexox)

## 1.6.10 (2024-04-10)

- feat: add file type filters ([#698](https://github.com/evilmartians/lefthook/pull/698)) by [@mrexox](https://github.com/mrexox)
- ci: update github actions versions ([#699](https://github.com/evilmartians/lefthook/pull/699)) by [@mrexox](https://github.com/mrexox)

## 1.6.9 (2024-04-09)

- fix: enable interactive inputs for windows ([#696](https://github.com/evilmartians/lefthook/pull/696)) by [@mrexox](https://github.com/mrexox)
- fix: add batching to implicit commands ([#695](https://github.com/evilmartians/lefthook/pull/695)) by [@mrexox](https://github.com/mrexox)
- fix: command argument count validations ([#694](https://github.com/evilmartians/lefthook/pull/694)) by [@scop](https://github.com/scop)
- fix: re-download remotes when called install with -f ([#692](https://github.com/evilmartians/lefthook/pull/692)) by [@mrexox](https://github.com/mrexox)
- chore: remove redundant parallelisation ([#690](https://github.com/evilmartians/lefthook/pull/690)) by [@mrexox](https://github.com/mrexox)
- chore: refactor Result handling ([#689](https://github.com/evilmartians/lefthook/pull/689)) by [@mrexox](https://github.com/mrexox)

## 1.6.8 (2024-04-02)

- fix: fallback to empty tree sha when no upstream set ([#687](https://github.com/evilmartians/lefthook/pull/687)) by [@mrexox](https://github.com/mrexox)
- feat: add priorities to scripts ([#684](https://github.com/evilmartians/lefthook/pull/684)) by [@mrexox](https://github.com/mrexox)
- deps: By April, 1 ([#678](https://github.com/evilmartians/lefthook/pull/678)) by [@mrexox](https://github.com/mrexox)

## 1.6.7 (2024-03-15)

- fix: don't apply empty patch files on pre-commit hook ([#676](https://github.com/evilmartians/lefthook/pull/676)) by [@mrexox](https://github.com/mrexox)
- docs: allow only comma divided tags ([#675](https://github.com/evilmartians/lefthook/pull/675)) by [@mrexox](https://github.com/mrexox)

## 1.6.6 (2024-03-14)

- chore: add more tests on skip settings by [@mrexox](https://github.com/mrexox)
- chore: add more linters, address findings ([#670](https://github.com/evilmartians/lefthook/pull/670)) by [@scop](https://github.com/scop)
- chore: skip printing deprecation warning ([#674](https://github.com/evilmartians/lefthook/pull/674)) by [@mrexox](https://github.com/mrexox)
- feat: handle `run` command in skip/only settings ([#634](https://github.com/evilmartians/lefthook/pull/634)) by [@prog-supdex](https://github.com/prog-supdex)
- deps: Dependencies March 2024 ([#673](https://github.com/evilmartians/lefthook/pull/673)) by [@mrexox](https://github.com/mrexox)
- fix: fix printing when using `output` log setting ([#672](https://github.com/evilmartians/lefthook/pull/672)) by [@mrexox](https://github.com/mrexox)
- feat: Add output setting ([#637](https://github.com/evilmartians/lefthook/pull/637)) by [@prog-supdex](https://github.com/prog-supdex)
- fix: use swift package before npx ([#668](https://github.com/evilmartians/lefthook/pull/668)) by [@mrexox](https://github.com/mrexox)
- feat: use configurable path to lefthook (LEFTHOOK_BIN) ([#653](https://github.com/evilmartians/lefthook/pull/653)) by [@technicalpickles](https://github.com/technicalpickles)

## 1.6.5 (2024-03-04)

- fix: decrease max cmd length for windows ([#666](https://github.com/evilmartians/lefthook/pull/666)) by [@mrexox](https://github.com/mrexox)
- deps: Dependencies 04.03.2024 ([#664](https://github.com/evilmartians/lefthook/pull/664)) by [@mrexox](https://github.com/mrexox)
- chore: fix Makefile by [@mrexox](https://github.com/mrexox)
- docs: fix redundant option by [@mrexox](https://github.com/mrexox)

## 1.6.4 (2024-02-28)

- deps: update uniseg ([#650](https://github.com/evilmartians/lefthook/pull/650)) by [@technicalpickles](https://github.com/technicalpickles)

## 1.6.3 (2024-02-27)

- deps: Dependencies (27.02.2024) ([#648](https://github.com/evilmartians/lefthook/pull/648)) by [@mrexox](https://github.com/mrexox)
- chore: remove adaptive colors ([#647](https://github.com/evilmartians/lefthook/pull/647)) by [@mrexox](https://github.com/mrexox)
- docs: update request help url ([#641](https://github.com/evilmartians/lefthook/pull/641)) by [@sbsrnt](https://github.com/sbsrnt)

## 1.6.2 (2024-02-26)

- fix: respect roots in commands for npm packages ([#616](https://github.com/evilmartians/lefthook/pull/616)) by [@mrexox](https://github.com/mrexox)
- fix: don't capture STDIN without interactive or use_stdin options ([#638](https://github.com/evilmartians/lefthook/pull/638)) by [@technicalpickles](https://github.com/technicalpickles)
- fix: handle LEFTHOOK_QUIET when there is no skip_output in config by [@prog-supdex](https://github.com/prog-supdex)
- docs: add stage_fixed to the examples by [@mrexxo](https://github.com/mrexxo)
- docs: clarify the difference between piped and parallel options by [@mrexox](https://github.com/mrexox)

## 1.6.1 (2024-01-24)

- fix: files from stdin only null separated ([#615](https://github.com/evilmartians/lefthook/pull/615)) by [@mrexox](https://github.com/mrexox)
- docs: add a new article link by [@mrexox](https://github.com/mrexox)

## 1.6.0 (2024-01-22)

- feat: add remotes and configs options ([#609](https://github.com/evilmartians/lefthook/pull/609)) by [@NikitaCOEUR](https://github.com/NikitaCOEUR)
- feat: add replaces to all template and parse files from stdin ([#596](https://github.com/evilmartians/lefthook/pull/596)) by [@sanmai-NL](https://github.com/sanmai-NL)

## 1.5.7 (2024-01-17)

- fix: pre push hook handling ([#613](https://github.com/evilmartians/lefthook/pull/613)) by [@mrexox](https://github.com/mrexox)
- chore: hide wiki links ([#608](https://github.com/evilmartians/lefthook/pull/608)) by [@mrexox](https://github.com/mrexox)

## 1.5.6 (2024-01-12)

- feat: shell completion improvements ([#577](https://github.com/evilmartians/lefthook/pull/577)) by [@scop](https://github.com/scop)
- fix: safe execute git commands without sh wrapper ([#606](https://github.com/evilmartians/lefthook/pull/606)) by [@mrexox](https://github.com/mrexox)
- fix: use lefthook package with npx ([#604](https://github.com/evilmartians/lefthook/pull/604)) by [@mrexox](https://github.com/mrexox)
- feat: allow setting a bool value for skip_output ([#601](https://github.com/evilmartians/lefthook/pull/601)) by [@nsklyarov](https://github.com/nsklyarov)
- docs: update exception case about interactive option by [@mrexox](https://github.com/mrexox)

## 1.5.5 (2023-11-30)

- fix: use empty stdin by default ([#590](https://github.com/evilmartians/lefthook/pull/590)) by [@mrexox](https://github.com/mrexox)
- feat: add priorities to commands ([#589](https://github.com/evilmartians/lefthook/pull/589)) by [@mrexox](https://github.com/mrexox)

## 1.5.4 (2023-11-27)

- chore: add typos fixer by [@mrexox](https://github.com/mrexox)
- fix: drop new argument for git diff compatibility ([#586](https://github.com/evilmartians/lefthook/pull/586)) by [@mrexox](https://github.com/mrexox)

## 1.5.3 (2023-11-22)

- fix: don't check checksum file when explicitly calling lefthook install ([#572](https://github.com/evilmartians/lefthook/pull/572)) by [@mrexox](https://github.com/mrexox)
- chore: skip summary separator if nothing is printed ([#575](https://github.com/evilmartians/lefthook/pull/575)) by [@mrexox](https://github.com/mrexox)
- docs: update info about root option by [@mrexox](https://github.com/mrexox)

## 1.5.2 (2023-10-9)

- fix: correctly sort alphanumeric commands ([#562](https://github.com/evilmartians/lefthook/pull/562)) by [@mrexox](https://github.com/mrexox)

## 1.5.1 (2023-10-6)

- feat: add force flag to run command ([#561](https://github.com/evilmartians/lefthook/pull/561)) by [@mrexox](https://github.com/mrexox)
- fix: do not enable export when sourcing rc file ([#553](https://github.com/evilmartians/lefthook/pull/553)) by [@hyperupcall](https://github.com/hyperupcall)
- chore: wrap shell args in quotes for consistency by [@mrexox](https://github.com/mrexox)
- docs: add a note that files template supports directories by [@mrexox](https://github.com/mrexox)
- feat: initial support for Swift Plugins ([#556](https://github.com/evilmartians/lefthook/pull/556)) by [@csjones](https://github.com/csjones)

## 1.5.0 (2023-09-21)

- chore: output enhancements ([#549](https://github.com/evilmartians/lefthook/pull/549)) by [@mrexox](https://github.com/mrexox)
- feat: add interrupt (Ctrl-C) handling ([#550](https://github.com/evilmartians/lefthook/pull/550)) by [@mrcljx](https://github.com/mrcljx)

## 1.4.11 (2023-09-13)

- docs: update docs and readme with tl;dr instructions ([#548](https://github.com/evilmartians/lefthook/pull/548)) by [@mrexox](https://github.com/mrexox)
- fix: add use_stdin option for just reading from stdin ([#547](https://github.com/evilmartians/lefthook/pull/547)) by [@mrexox](https://github.com/mrexox)
- chore: refactor commands passing ([#546](https://github.com/evilmartians/lefthook/pull/546)) by [@mrexox](https://github.com/mrexox)
- fix: fail on non existing hook name ([#545](https://github.com/evilmartians/lefthook/pull/545)) by [@mrexox](https://github.com/mrexox)

## 1.4.10 (2023-09-04)

- fix: split command with file templates into chunks ([#541](https://github.com/evilmartians/lefthook/pull/541)) by [@mrexox](https://github.com/mrexox)
- chore: add git-cliff config for easier changelog generation by [@mrexox](https://github.com/mrexox)
- fix: allow empty staged files diffs ([#543](https://github.com/evilmartians/lefthook/pull/543)) by [@mrexox](https://github.com/mrexox)

## 1.4.9 (2023-08-15)

- chore: fix linter issues ([#537](https://github.com/evilmartians/lefthook/pull/537)) by [@mrexox](https://github.com/mrexox)
- feat: add files, all-files, and commands flags ([#534](https://github.com/evilmartians/lefthook/pull/534)) by [@nihalgonsalves](https://github.com/nihalgonsalves)
- chore: bump go to 1.21 ([#536](https://github.com/evilmartians/lefthook/pull/536)) by [@nihalgonsalves](https://github.com/nihalgonsalves)

## 1.4.8 (2023-07-31)

- feat: add assert_lefthook_installed option ([#533](https://github.com/evilmartians/lefthook/pull/533)) by [@mrexox](https://github.com/mrexox)
- chore: add *Add docs* to PR template ([#532](https://github.com/evilmartians/lefthook/pull/532)) by [@technicalpickles](https://github.com/technicalpickles)
- feat: add support for skipping empty summaries ([#531](https://github.com/evilmartians/lefthook/pull/531)) by [@technicalpickles](https://github.com/technicalpickles)

## 1.4.7 (2023-07-24)

- docs: add scoop installation method ([#527](https://github.com/evilmartians/lefthook/pull/527)) by [@sitiom](https://github.com/sitiom)
- fix: correct merging of extends from remote config ([#529](https://github.com/evilmartians/lefthook/pull/529)) by [@mrexox](https://github.com/mrexox)
- ci: add Winget Releaser action ([#526](https://github.com/evilmartians/lefthook/pull/526)) by [@sitiom](https://github.com/sitiom)
- chore: improve correctness of load_test.go ([#525](https://github.com/evilmartians/lefthook/pull/525)) by [@hyperupcall](https://github.com/hyperupcall)

## 1.4.6 (2023-07-18)

- fix: do not print extraneous newlines when executionInfo output is hidden ([#519](https://github.com/evilmartians/lefthook/pull/519)) by [@hyperupcall](https://github.com/hyperupcall)
- fix: uninstall all possible formats ([#523](https://github.com/evilmartians/lefthook/pull/523)) by [@mrexox](https://github.com/mrexox)
- fix: LEFTHOOK_VERBOSE properly overrides --verbose flag ([#521](https://github.com/evilmartians/lefthook/pull/521)) by [@hyperupcall](https://github.com/hyperupcall)
- feat: support .lefthook.yml and .lefthook-local.yml ([#520](https://github.com/evilmartians/lefthook/pull/520)) by [@hyperupcall](https://github.com/hyperupcall)

## 1.4.5 (2023-07-12)

- docs: improve documentation and examples ([#517](https://github.com/evilmartians/lefthook/pull/517)) by [@hyperupcall](https://github.com/hyperupcall)
- fix: improve hook template ([#516](https://github.com/evilmartians/lefthook/pull/516)) by [@hyperupcall](https://github.com/hyperupcall)

## 1.4.4 (2023-07-10)

- fix: don't render bold ANSI sequence when colors are disabled ([#515](https://github.com/evilmartians/lefthook/pull/515)) by [@adam12](https://github.com/adam12)
- deps: July 2023 ([#514](https://github.com/evilmartians/lefthook/pull/514)) by [@mrexox](https://github.com/mrexox)

## 1.4.3 (2023-06-19)

- fix: auto stage non-standard files ([#506](https://github.com/evilmartians/lefthook/pull/506)) by [@mrexox](https://github.com/mrexox)

## 1.4.2 (2023-06-13)

- deps: June 2023 ([#499](https://github.com/evilmartians/lefthook/pull/499))
- feat: support toml dumpint ([#490](https://github.com/evilmartians/lefthook/pull/490)) by [@mrexox](https://github.com/mrexox)
- feat: support json configs ([#489](https://github.com/evilmartians/lefthook/pull/489)) by [@mrexox](https://github.com/mrexox)

## 1.4.1 (2023-05-22)

- fix: add win32 binary to artifacts (by [@mrexox](https://github.com/mrexox))
- feat: allow dumping with JSON ([#485](https://github.com/evilmartians/lefthook/pull/485) by [@mrexox](https://github.com/mrexox)
- feat: add skip execution_info option ([#484](https://github.com/evilmartians/lefthook/pull/484)) by [@mrexox](https://github.com/mrexox)
- deps: from 05.2023 ([#487](https://github.com/evilmartians/lefthook/pull/487)) by [@mrexox](https://github.com/mrexox)

## 1.4.0 (2023-05-18)

- feat: add adaptive colors ([#482](https://github.com/evilmartians/lefthook/pull/482)) by [@mrexox](https://github.com/mrexox)
- fix: skip output for interactive commands if configured ([#483](https://github.com/evilmartians/lefthook/pull/483)) by [@mrexox](https://github.com/mrexox)
- feat: add dump command ([#481](https://github.com/evilmartians/lefthook/pull/481)) by [@mrexox](https://github.com/mrexox)

## 1.3.13 (2023-05-11)

- feat: add only option ([#478](https://github.com/evilmartians/lefthook/pull/478)) by [@mrexox](https://github.com/mrexox)

## 1.3.12 (2023-04-28)

- fix: allow skipping execution_out with interactive mode ([#476](https://github.com/evilmartians/lefthook/pull/476)) by [@mrexox](https://github.com/mrexox)

## 1.3.11 (2023-04-27)

- feat: add execution_out to skip output settings ([#475](https://github.com/evilmartians/lefthook/pull/475)) by [@mrexox](https://github.com/mrexox)
- chore: add debug logs when hook is skipped ([#474](https://github.com/evilmartians/lefthook/pull/474)) by [@mrexox](https://github.com/mrexox)

## 1.3.10

- feat: don't show when commands are skipped because of no matched files ([#468](https://github.com/evilmartians/lefthook/pull/468)) by [@technicalpickles](https://github.com/technicalpickles)

## 1.3.9 (2023-04-04)

- feat: allow extra hooks in local config ([#462](https://github.com/evilmartians/lefthook/pull/462)) by [@fabn](https://github.com/fabn)
- feat: pass numeric placeholders to files command ([#461](https://github.com/evilmartians/lefthook/pull/461)) by [@fabn](https://github.com/fabn)

## 1.3.8 (2023-03-23)

- fix: make hook template compatible with shells without source command ([#458](https://github.com/evilmartians/lefthook/pull/458)) by [@mdesantis](https://github.com/mdesantis)

## 1.3.7 (2023-03-20)

- fix: allow globs in skip option ([#457](https://github.com/evilmartians/lefthook/pull/457)) by [@mrexox](https://github.com/mrexox)
- deps: dependencies update (March 2023) ([#455](https://github.com/evilmartians/lefthook/pull/455)) by [@mrexox](https://github.com/mrexox)
- fix: don't fail on missing config file ([#450](https://github.com/evilmartians/lefthook/pull/450)) by [@mrexox](https://github.com/mrexox)

## 1.3.6 (2023-03-16)

- fix: stage fixed when root specified ([#449](https://github.com/evilmartians/lefthook/pull/449)) by [@mrexox](https://github.com/mrexox)
- feat: implitic skip on missing files for pre-commit and pre-push hooks ([#448](https://github.com/evilmartians/lefthook/pull/448)) by [@mrexox](https://github.com/mrexox)

## 1.3.5 (2023-03-15)

- feat: add stage_fixed option ([#445](https://github.com/evilmartians/lefthook/pull/445)) by [@mrexox](https://github.com/mrexox)

## 1.3.4 (2023-03-13)

- fix: don't extra extend config if lefthook-local.yml is missing ([#444](https://github.com/evilmartians/lefthook/pull/444)) by [@mrexox](https://github.com/mrexox)

## 1.3.3 (2023-03-01)

- fix: restore release assets name ([#437](https://github.com/evilmartians/lefthook/pull/437)) by [@watarukura](https://github.com/watarukura)

## 1.3.2 (2023-02-27)

- fix: Allow sh syntax in files ([#435](https://github.com/evilmartians/lefthook/pull/435)) by [@mrexox](https://github.com/mrexox)

## 1.3.1 (2023-02-27)

- fix: Force creation of git hooks folder ([#434](https://github.com/evilmartians/lefthook/pull/434)) by [@mrexox](https://github.com/mrexox)

## 1.3.0 (2023-02-22)

- fix: Use correct branch for {push_files} template ([#429](https://github.com/evilmartians/lefthook/pull/429)) by [@mrexox](https://github.com/mrexox)
- feature: Skip unstaged changes for pre-commit hook ([#402](https://github.com/evilmartians/lefthook/pull/402)) by [@mrexox](https://github.com/mrexox)

## 1.2.9 (2023-02-13)

- fix: memory leak dependency ([#426](https://github.com/evilmartians/lefthook/pull/426)) by [@strpc](https://github.com/strpc)

## 1.2.8 (2023-01-23)

- fix: Don't join info path with root ([#418](https://github.com/evilmartians/lefthook/pull/418)) by [@mrexox](https://github.com/mrexox)

## 1.2.7 (2023-01-10)

- fix: Make info dir when it is absent ([#414](https://github.com/evilmartians/lefthook/pull/414)) by [@sato11](https://github.com/sato11)
- deps: bump github.com/mattn/go-isatty from 0.0.16 to 0.0.17 ([#409](https://github.com/evilmartians/lefthook/pull/409)) by [@dependabot](https://github.com/dependabot)
- deps: bump github.com/briandowns/spinner from 1.19.0 to 1.20.0 ([#406](https://github.com/evilmartians/lefthook/pull/406)) by [@dependabot](https://github.com/dependabot)
- fix: Double quote eval statements with $dir ([#404](https://github.com/evilmartians/lefthook/pull/404)) by [@jrfoell](https://github.com/jrfoell)
- chore: Add PR template ([#401](https://github.com/evilmartians/lefthook/pull/401)) by [@mrexox](https://github.com/mrexox)
- chore: Fix yml syntax missing colon ([#399](https://github.com/evilmartians/lefthook/pull/399)) by [@aaronkelton](https://github.com/aaronkelton)

## 1.2.6 (2022-12-14)

- feature: Allow following output ([#397](https://github.com/evilmartians/lefthook/pull/397)) by [@mrexox](https://github.com/mrexox)
- fix: Remove quotes for rc in template ([#398](https://github.com/evilmartians/lefthook/pull/398)) by [@mrexox](https://github.com/mrexox)

## 1.2.5 (2022-12-13)

- feature: Add an option to disable spinner ([#396](https://github.com/evilmartians/lefthook/pull/396)) by [@mrexox](https://github.com/mrexox)
- feature: Use pnpm before npx ([#393](https://github.com/evilmartians/lefthook/pull/393)) by [@mrexox](https://github.com/mrexox)
- chore: Use lipgloss for output ([#395](https://github.com/evilmartians/lefthook/pull/395)) by [@mrexox](https://github.com/mrexox)

## 1.2.4 (2022-12-05)

- feature: Allow providing rc file ([PR #392](https://github.com/evilmartians/lefthook/pull/392) by [@mrexox](https://github.com/mrexox))

## 1.2.3 (2022-11-30)

- feature: Expand env variables ([PR #391](https://github.com/evilmartians/lefthook/pull/391) by [@mrexox](https://github.com/mrexox))
- deps: Update important dependencies ([PR #389](https://github.com/evilmartians/lefthook/pull/389) by [@mrexox](https://github.com/mrexox))

## 1.2.2 (2022-11-23)

- chore: Add FreeBSD OS to packages ([PR #377](https://github.com/evilmartians/lefthook/pull/377) by [@mrexox](https://github.com/mrexox))
- feature: Skip based on branch name and allow global skip rules ([PR #376](https://github.com/evilmartians/lefthook/pull/376) by [@mrexox](https://github.com/mrexox))
- fix: Omit LFS output unless it is required ([PR #373](https://github.com/evilmartians/lefthook/pull/373) by [@mrexox](https://github.com/mrexox))

## 1.2.1 (2022-11-17)

- fix: Remove quoting for scripts ([PR #371](https://github.com/evilmartians/lefthook/pull/371) by [@stonesbg](https://github.com/stonesbg) + [@mrexox](https://github.com/mrexox))
- fix: remove lefthook.checksum on uninstall ([PR #370](https://github.com/evilmartians/lefthook/pull/370) by [@JuliusHenke](https://github.com/JuliusHenke))
- fix: Print prepare-commit-msg hook if it exists in config ([PR #368](https://github.com/evilmartians/lefthook/pull/368) by [@mrexox](https://github.com/mrexox))
- fix: Allow changing refs for remote ([PR #363](https://github.com/evilmartians/lefthook/pull/363) by [@mrexox](https://github.com/mrexox))

## 1.2.0 (2022-11-7)

- fix: Full support for interactive commands and scripts ([PR #352](https://github.com/evilmartians/lefthook/pull/352) by [@mrexox](https://github.com/mrexox))
- chore: Remove deprecated config options ([PR #351](https://github.com/evilmartians/lefthook/pull/351) by [@mrexox](https://github.com/mrexox))
- feature: Add remote config support ([PR #343](https://github.com/evilmartians/lefthook/pull/343) by [@oatovar](https://github.com/oatovar) and [@mrexox](https://github.com/mrexox))

## 1.1.4 (2022-11-1)

- feature: Add `LEFTHOOK_VERBOSE` env ([PR #346](https://github.com/evilmartians/lefthook/pull/346) by [@mrexox](https://github.com/mrexox))

## 1.1.3 (2022-10-15)

- ci: Fix snapcraft trying to create dirs in parallel by [@mrexox](https://github.com/mrexox)
- feature: Allow setting env vars ([PR #337](https://github.com/evilmartians/lefthook/pull/337) by [@mrexox](https://github.com/mrexox))
- feature: Show current running command and script name(s) ([PR #338](https://github.com/evilmartians/lefthook/pull/338) by [@mrexox](https://github.com/mrexox))
- feature: Exclude by command names too ([PR #335](https://github.com/evilmartians/lefthook/pull/335) by [@mrexox](https://github.com/mrexox))
- fix: Don't uninstall lefthook.yml and lefthook-local.yml by default ([PR #334](https://github.com/evilmartians/lefthook/pull/334) by [@mrexox](https://github.com/mrexox))
- fix: Fixing typo in gemspec ([PR #333](https://github.com/evilmartians/lefthook/pull/333) by [@kerrizor](https://github.com/kerrizor))

## 1.1.2 (2022-10-10)

- Fix regression from #314 (chmod missed fix) ([PR #330](https://github.com/evilmartians/lefthook/pull/330) by [@ariccio](https://github.com/ariccio))
- Pass stdin by default ([PR #324](https://github.com/evilmartians/lefthook/pull/324) by [@mrexox](https://github.com/mrexox))

## 1.1.1 (2022-08-22)

- Quote path to script ([PR #321](https://github.com/evilmartians/lefthook/pull/321) by [@mrexox](https://github.com/mrexox))

## 1.1.0 (2022-08-13)

- Add goreleaser action ([PR #307](https://github.com/evilmartians/lefthook/pull/307) by [@mrexox](https://github.com/mrexox))
- Windows escaping issues ([PR #314](https://github.com/evilmartians/lefthook/pull/314) by [@mrexox](https://github.com/mrexox))
- Check for lefthook.bat in hook template ([PR #316](https://github.com/evilmartians/lefthook/pull/316) by [@mrexox](https://github.com/mrexox))
- Update node.md docs ([PR #312](https://github.com/evilmartians/lefthook/pull/312) by [@fantua](https://github.com/fantua))
- Move postinstall script to main lefthook NPM package ([PR #311](https://github.com/evilmartians/lefthook/pull/311) by [@mrexox](https://github.com/mrexox))
- Allow suppressing execution output ([PR #309](https://github.com/evilmartians/lefthook/pull/309) by [@mrexox](https://github.com/mrexox))
- Update dependencies ([PR #308](https://github.com/evilmartians/lefthook/pull/308) by [@mrexox](https://github.com/mrexox))
- Add support for Git LFS ([PR #306](https://github.com/evilmartians/lefthook/pull/306) by [@mrexox](https://github.com/mrexox))
- Bump Go version to 1.19 ([PR #305](https://github.com/evilmartians/lefthook/pull/305) by [@mrexox](https://github.com/mrexox))
- Add tests on runner ([PR #304](https://github.com/evilmartians/lefthook/pull/304) by [@mrexox](https://github.com/mrexox))
- Add fail text option ([PR #301](https://github.com/evilmartians/lefthook/pull/301) by [@mrexox](https://github.com/mrexox))
- Store lefthook checksum in non-hook file ([PR #280](https://github.com/evilmartians/lefthook/pull/280) by [@mrexox](https://github.com/mrexox))

## 1.0.5 (2022-07-19)

- Submodules issue ([PR #300](https://github.com/evilmartians/lefthook/pull/300) by [@mrexox](https://github.com/mrexox))
- Remove rspec tests ([PR #299](https://github.com/evilmartians/lefthook/pull/299) by [@mrexox](https://github.com/mrexox))
- Add `when "mingw" then "windows"` case ([PR #297](https://github.com/evilmartians/lefthook/pull/297) by [@ariccio](https://github.com/ariccio))
- Define security policy and contact method ([PR #293](https://github.com/evilmartians/lefthook/pull/293) by [@Envek](https://github.com/Envek))

# 1.0.4 (2022-06-27)

- Support skipping on rebase ([PR #289](https://github.com/evilmartians/lefthook/pull/289) by [@mrexox](https://github.com/mrexox))

# 1.0.3 (2022-06-25)

- Fix NPM package
- Update email information

# 1.0.2 (2022-06-24)

- Bring auto install back ([PR #286](https://github.com/evilmartians/lefthook/pull/286) by [@mrexox](https://github.com/mrexox))
- Move main.go to root ([PR #285](https://github.com/evilmartians/lefthook/pull/285) by [@mrexox](https://github.com/mrexox))
- Panic on commands structure misuse ([PR #284](https://github.com/evilmartians/lefthook/pull/284) by [@mrexox](https://github.com/mrexox))
- Split npm package by os and cpu ([PR #281](https://github.com/evilmartians/lefthook/pull/281) by [@mrexox](https://github.com/mrexox))

# 1.0.1 (2022-06-20) Ruby gem and NPM package fix

- Fix folders structure for `[@evilmartians](https://github.com/evilmartians)/lefthook` and `[@evilmartians](https://github.com/evilmartians)/lefthook-installer` packages
- Fix folders structure for `lefthook` gem

# 1.0.0 (2022-06-19)

- Refactoring ([PR #275](https://github.com/evilmartians/lefthook/pull/275) by [@mrexox](https://github.com/mrexox), [@skryukov](https://github.com/skryukov), [@markovichecha](https://github.com/markovichecha))
- Replace deprecated `File.exists?` with `exist?` for Ruby script ([PR #263](https://github.com/evilmartians/lefthook/pull/263) by [@pocke](https://github.com/pocke))

# 0.8.0 (2022-06-07)

- Allow skipping hooks in certain git states: merge and/or rebase ([PR #173](https://github.com/evilmartians/lefthook/pull/173) by [@DmitryTsepelev](https://github.com/DmitryTsepelev))
- NPM: installer package that downloads the required binaries during installation ([PR #188](https://github.com/evilmartians/lefthook/pull/188) by [@aminya](https://github.com/aminya), [PR #273](https://github.com/evilmartians/lefthook/pull/273) by [@Envek](https://github.com/Envek))
- Add ability to skip summary output. Also support a `LEFTHOOK_QUIET` env variable ([PR #187](https://github.com/evilmartians/lefthook/pull/187) by [@washtubs](https://github.com/washtubs))
- Make filename globs case-insensitive ([PR #196](https://github.com/evilmartians/lefthook/pull/196) by [@skryukov](https://github.com/skryukov))
- Fix lefthook binary extension on Windows ([PR #188](https://github.com/evilmartians/lefthook/pull/188) by [@aminya](https://github.com/aminya))
- Stop building 32-bit binaries for releases due to low usage ([@Envek](https://github.com/Envek))
- Allow lefthook to work when node_modules is not in root folder for npx ([PR #224](https://github.com/evilmartians/lefthook/pull/224) by [@spearmootz](https://github.com/spearmootz))
- Fix unreachable conditional in hook template ([PR #242](https://github.com/evilmartians/lefthook/pull/242) by [@dannobytes](https://github.com/dannobytes))
- Add cpu arch and os arch to lefthook's filepath in hook template ([PR #260](https://github.com/evilmartians/lefthook/pull/260) by [@rmachado-studocu](https://github.com/rmachado-studocu))

# 0.7.7 (2021-10-02)

- Fix incorrect npx command in git hook script template ([PR #236](https://github.com/evilmartians/lefthook/pull/236)) [@PikachuEXE](https://github.com/PikachuEXE)
- Update project URLs in NPM package.json ([PR #235](https://github.com/evilmartians/lefthook/pull/235)) [@PikachuEXE](https://github.com/PikachuEXE)
- Pass all arguments to downstream hooks ([PR #231](https://github.com/evilmartians/lefthook/pull/231)) [@pablobirukov](https://github.com/pablobirukov)
- Allows lefthook to work when node_modules is not in root folder for npx ([PR #224](https://github.com/evilmartians/lefthook/pull/224)) [@spearmootz](https://github.com/spearmootz)
- Do not initialize git config on `help` and `version` commands ([PR #209](https://github.com/evilmartians/lefthook/pull/209)) [@pwinckles](https://github.com/pwinckles)
- node: fix postinstall: process.cwd is a function and should be called [@Envek](https://github.com/Envek)

# 0.7.6 (2021-06-02)

- Fix lefthook binary extension on Windows. [@aminya](https://github.com/aminya)
- [PR #193](https://github.com/evilmartians/lefthook/pull/193) Fix path for searching npm-installed binary when in worktree. [@Envek](https://github.com/Envek)
- NPM, RPM, and DEB packaging fixes. [@Envek](https://github.com/Envek)

# 0.7.5 (2021-05-14)

- [PR #179](https://github.com/evilmartians/lefthook/pull/179) Fix running on Windows under MSYS and MINGW64 when run from Ruby gem or JS npm package. [@akiver](https://github.com/akiver), [@Envek](https://github.com/Envek)
- [PR #177](https://github.com/evilmartians/lefthook/pull/177) Support non-default git hooks path. [@charlie-wasp](https://github.com/charlie-wasp)
- [PR #182](https://github.com/evilmartians/lefthook/pull/182) Support git workspaces and submodules. [@skryukov](https://github.com/skryukov)
- [PR #184](https://github.com/evilmartians/lefthook/pull/184) Rewrite npm's scripts in JavaScript to support running on Windows without `sh`. [@aminya](https://github.com/aminya)

# 0.7.4 (2021-04-30)

- [PR](https://github.com/evilmartians/lefthook/pull/171) Improve check for installed git [@DmitryTsepelev](https://github.com/DmitryTsepelev)
- [PR](https://github.com/evilmartians/lefthook/pull/169) Create .git/hooks directory when it does not exist [@DmitryTsepelev](https://github.com/DmitryTsepelev)

# 0.7.3 (2021-04-23)

- [PR](https://github.com/evilmartians/lefthook/pull/168) Package versions for all architectures (x86_64, ARM64, x86) into Ruby gem and NPM package [@Envek](https://github.com/Envek)
- [PR](https://github.com/evilmartians/lefthook/pull/167) Fix golang 15+ build [@skryukov](https://github.com/skryukov)

# 0.7.2 (2020-02-02)

- [PR](https://github.com/evilmartians/lefthook/pull/126) Feature multiple extends. Thanks [@Evilweed](https://github.com/Evilweed)

- [PR](https://github.com/evilmartians/lefthook/pull/124) Fix `npx` when only `yarn` exists. Thanks [@dotterian](https://github.com/dotterian)

- [PR](https://github.com/evilmartians/lefthook/pull/116) Fix use '-h' for robust lefthook. Thanks [@fahrinh](https://github.com/fahrinh)

# 0.7.1 (2020-02-02)

- [PR](https://github.com/evilmartians/lefthook/pull/108) Fix `sh` dependency on windows when executing `git`. Thanks [@lionskape](https://github.com/lionskape)

- [PR](https://github.com/evilmartians/lefthook/pull/103) Add possibility for using `yaml` and `yml` extension for config. Thanks [@rbUUbr](https://github.com/rbUUbr)

# 0.7.0 (2019-12-14)

- [PR](https://github.com/evilmartians/lefthook/pull/98) Support relative roots for monorepos. Thanks [@jsmestad](https://github.com/jsmestad)

# 0.6.7 (2019-12-14)

- [Commit](https://github.com/evilmartians/lefthook/commit/e898b5c8ba56c4d6f29a4d1f433baa1779a0845b)
Skip before executing command

- [PR](https://github.com/evilmartians/lefthook/pull/94) Add option --keep-config. Thanks [@justinasposiunas](https://github.com/justinasposiunas)

- [Commit](https://github.com/evilmartians/lefthook/commit/d79a3a46e7d1ee709b97e97f823bfd27e9466eff)
Check if shell is non interactive

# 0.6.6 (2019-12-03)

- [PR](https://github.com/evilmartians/lefthook/pull/94) Use eval instead of exec; Enable tty for the shell. Thanks [@ssnickolay](https://github.com/ssnickolay)

# 0.6.5 (2019-11-15)

- [PR](https://github.com/evilmartians/lefthook/pull/89) Add support for git-worktree. Thanks [@f440](https://github.com/f440)

- [Commit](https://github.com/evilmartians/lefthook/commit/48702a0806d2b2eab13636ba56b0e0b99f346f1c)
Commands and Scripts now can catch Stdin

- [Commit](https://github.com/evilmartians/lefthook/commit/9a226842292ff1dda0f2273b66a0799988aa5289)
Add partial support for monorepos and command execution not from project root

# 0.6.4 (2019-11-08)

- [PR](https://github.com/evilmartians/lefthook/pull/84) Fix return value from shell exit. Thanks [@HaiD84](https://github.com/HaiD84)

- [PR](https://github.com/evilmartians/lefthook/pull/86) Support postinstall script for npm installation for monorepos. Thanks [@sHooKDT](https://github.com/sHooKDT)

- [PR](https://github.com/evilmartians/lefthook/pull/82) Now relative path to scripts supported. Thanks [@AlexeyMatskevich](https://github.com/AlexeyMatskevich)

- [Commit](https://github.com/evilmartians/lefthook/pull/80/commits/1a4b0ee155eb66ae6f3c365164012bee9332605a)
Option `extends` for top level config added. Now you can merge some settings from different places:
```yml
extends: $HOME/work/lefthook-extend.yml
```

- [Commit](https://github.com/evilmartians/lefthook/commit/83cf818106dbf222ea33ba86aafce8f30d7cb5a9)
Add examples to generated lefthook.yml

## 0.6.3 (2019-07-15)

- [Commit](https://github.com/evilmartians/lefthook/commit/0426936f48f248221126f15619932b0dc8c54d7a) Add `-a` means `aggressive` strategy for `install` command
```bash
lefthook install -a # clear .git/hooks dir and reinstall lefthook hooks
```

- [Commit](https://github.com/evilmartians/lefthook/commit/5efb0677a4a9ec1728d3cf1a083075e23315a796) Add Lefthook version indicator for commands and script execution

- [Commit](https://github.com/evilmartians/lefthook/commit/8b55d91eed46643a1674bd4ad96fa211a177e159) Remove `npx` as dependency from node wrapper

Now we will call directly binary from `./node_modules`

- [Commit](https://github.com/evilmartians/lefthook/commit/76ffed4c698bc074984e91f5610c0b98784bd10b) Add `-f` means `force` strategy for `install` command

```bash
lefthook install -f # reinstall lefthook hooks without sync info check
```

- PR [#27](https://github.com/evilmartians/lefthook/pull/27) Move LEFTHOOK env check in hooks files

Now if LEFTHOOK=0 we will not call the binary file

- PR [#26](https://github.com/evilmartians/lefthook/pull/26) + [commit](https://github.com/evilmartians/lefthook/commit/afd67f94631a10975209ed4c5fabc763f44280eb) Add `{push_files}` shortcut

Add shortcut `{push_files}`

```
pre-commit:
  commands:
    rubocop:
      run: rubocop {push_files}
```
It same as:
```
pre-commit:
  commands:
    rubocop:
      files: git diff --name-only HEAD @{push} || git diff --name-only HEAD master
      run: rubocop {push_files}
```

- [Commit](https://github.com/evilmartians/lefthook/commit/af087b032a14952aa1dd235a3d0b5a51bc760a10) Add `min_version` option

You can mark your config for minimum Lefthook version:
```
min_version: 0.6.1
```

## 0.6.0 (2019-07-10)

- PR [#24](https://github.com/palkan/logidze/pull/110) Wrap `run` command in shell context.

Now in `run` option available `sh` syntax.

```
pre-commit:
  commands:
    bashed:
      run: rubocop -a && git add
```
Will be executed in this way:
```
sh -c "rubocop -a && git add"
```

- PR [#23](https://github.com/evilmartians/lefthook/pull/24) Search Lefthook in Gemfile.

Now it's possible to use Lefthook from Gemfile.

```ruby
# Gemfile

gem 'lefthook'
```

[@mrexox]: https://github.com/mrexox
[@olivier-lacroix]: https://github.com/olivier-lacroix
[@michael-pplx]: https://github.com/michael-pplx
[@siler]: https://github.com/siler
[@technicalpickles]: https://github.com/technicalpickles
[@jasonwbarnett]: https://github.com/jasonwbarnett
[@scop]: https://github.com/scop
[@franzramadhan]: https://github.com/franzramadhan
[@joevin-sql-docto]: https://github.com/joevin-slq-docto
[@jeonghoon11]: https://github.com/jeonghoon11


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

First off, thanks for taking the time to contribute! Feel free to make Pull Request with your changes.

# Requirements

Go >= 1.26.0

# Process

1. Fork repo
2. git clone <forked_repo>
3. Make changes
4. Push your changes in <forked_repo>


================================================
FILE: LICENSE
================================================

The MIT License (MIT)

Copyright (c) 2019 Arkweid

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: Makefile
================================================
COMMIT_HASH = $(shell git rev-parse HEAD)

.PHONY: build
build:
	go build -ldflags "-s -w -X github.com/evilmartians/lefthook/v2/internal/version.commit=$(COMMIT_HASH)" -o lefthook

.PHONY: build-with-coverage
build-with-coverage:
	go build -cover -ldflags "-s -w -X github.com/evilmartians/lefthook/v2/internal/version.commit=$(COMMIT_HASH)" -o lefthook

.PHONY: jsonschema
jsonschema:
	go generate gen/jsonschema.go > schema.json
	go generate gen/jsonschema.go > internal/config/jsonschema.json

install: build
ifeq ($(shell go env GOOS),windows)
	copy lefthook $(shell go env GOPATH)\bin\lefthook.exe
else
	cp lefthook $$(go env GOPATH)/bin
endif

.PHONY: test
test:
	go test -cpu 24 -race -count=1 -timeout=30s ./...

.PHONY: test-integration
test-integration: install
	go test -cpu 24 -race -count=1 -timeout=30s -tags=integration integration_test.go

.PHONY: bench
bench:
	go test -cpu 24 -race -run=Bench -bench=. ./...

.PHONY: lint
lint: bin/golangci-lint
	bin/golangci-lint run --fix

bin/golangci-lint:
	curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b bin/ v$$(cat .tool-versions | grep golangci-lint | cut -d' ' -f2)

.ONESHELL:
version:
	@read -p "New version: " version
	sed -i "s/const version = .*/const version = \"$$version\"/" internal/version/version.go
	sed -i "s/VERSION = .*/VERSION = \"$$version\";/" packaging/scripts/lib/Constants.rakumod
	sed -i "s/lefthook-plugin.git\", exact: \".*\"/lefthook-plugin.git\", exact: \"$$version\"/" docs/installation/swift.md
	sed -i "s/go install github.com\/evilmartians\/lefthook\/v2.*/go install github.com\/evilmartians\/lefthook\/v2@v$$version/" docs/installation/go.md
	sed -i "s/go install github.com\/evilmartians\/lefthook\/v2.*/go install github.com\/evilmartians\/lefthook\/v2@v$$version/" README.md
	sed -i "s/go get -tool github.com\/evilmartians\/lefthook\/v2.*/go get -tool github.com\/evilmartians\/lefthook\/v2@v$$version/" README.md
	raku packaging/scripts/set-version.raku
	git add internal/version/version.go packaging/* docs/ README.md


================================================
FILE: README.md
================================================
![Build Status](https://github.com/evilmartians/lefthook/actions/workflows/test.yml/badge.svg?branch=master)
[![codecov](https://codecov.io/gh/evilmartians/lefthook/graph/badge.svg?token=d93ya8MfmB)](https://codecov.io/gh/evilmartians/lefthook)

# Lefthook

<img align="right" width="147" height="100" title="Lefthook logo"
     src="./logo_sign.svg">

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](https://evilmartians.com/chronicles/lefthook-knock-your-teams-code-back-into-shape?utm_source=lefthook)

<a href="https://evilmartians.com/?utm_source=lefthook">
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="100%" height="54"></a>

## Install

With **Go** (>= 1.26):

```bash
go install github.com/evilmartians/lefthook/v2@v2.1.4
```

* or as a go tool

```bash
go get -tool github.com/evilmartians/lefthook/v2@v2.1.4
```

With **NPM**:

```bash
npm install lefthook --save-dev
```

For **Ruby**:

```bash
gem install lefthook
```

For **Python**:

```bash
pipx install lefthook
```

**[Installation guide][installation]** with more ways to install lefthook: [apt][install-apt], [brew][install-brew], [winget][install-winget], and others.

## Usage

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

#### TL;DR

```bash
# 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

- [**Configuration**][configuration] for `lefthook.yml` config options.
- [**Usage**][usage] for **lefthook** CLI options, and features.
- [**Discussions**][discussion] for questions, ideas, suggestions.
<!-- - [**Wiki**](https://github.com/evilmartians/lefthook/wiki) for guides, examples, and benchmarks. -->

## Why Lefthook

* ### **Parallel execution**
Gives you more speed. [docs][config-parallel]

```yml
pre-push:
  parallel: true
```

* ### **Flexible list of files**
If you want your own list. [Custom][config-files] and [prebuilt][config-run] examples.

```yml
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](https://github.com/gobwas/glob#example).

```yml
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

```yml
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][config-scripts]

```yml
commit-msg:
  jobs:
    - script: "template_checker"
      runner: bash
```

* ### **Tags**
If you want to control a group of commands. [docs][config-tags]

```yml
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][config-run]

```yml
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][usage-local-config]

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

* ### **Direct control**

If you want to run hooks group directly.

```bash
$ lefthook run pre-commit
```

* ### **Your own tasks**

If you want to run specific group of commands directly.

```yml
fixer:
  jobs:
    - run: bundle exec rubocop --force-exclusion --safe-auto-correct -- {staged_files}
    - run: yarn eslint --fix {staged_files}
```
```bash
$ lefthook run fixer
```

* ### **Control output**

You can control what lefthook prints with [output][config-output] option.

```yml
output:
  - execution
  - failure
```

----

### Guides

* [Install with Node.js][install-node]
* [Install with Ruby][install-ruby]
* [Install with Homebrew][install-brew]
* [Install with Winget][install-winget]
* [Install for Debian-based Linux][install-apt]
* [Install for RPM-based Linux][install-rpm]
* [Install for Arch Linux][install-arch]
* [Install for Alpine Linux][install-alpine]
* [Usage][usage]
* [Configuration][configuration]
<!-- * [Troubleshooting](https://github.com/evilmartians/lefthook/wiki/Troubleshooting) -->

<!-- ### Migrate from -->
<!-- * [Husky](https://github.com/evilmartians/lefthook/wiki/Migration-from-husky) -->
<!-- * [Husky and lint-staged](https://github.com/evilmartians/lefthook/wiki/Migration-from-husky-with-lint-staged) -->
<!-- * [Overcommit](https://github.com/evilmartians/lefthook/wiki/Migration-from-overcommit) -->

### Examples

Check [examples][examples]

<!-- ### Benchmarks -->
<!-- * [vs Overcommit](https://github.com/evilmartians/lefthook/wiki/Benchmark-lefthook-vs-overcommit) -->
<!-- * [vs pre-commit](https://github.com/evilmartians/lefthook/wiki/Benchmark-lefthook-vs-pre-commit) -->

<!-- ### Comparison list -->
<!-- * [vs Overcommit, Husky, pre-commit](https://github.com/evilmartians/lefthook/wiki/Comparison-with-other-solutions) -->

### Articles
* [5 cool (and surprising) ways to configure Lefthook for automation joy](https://evilmartians.com/chronicles/5-cool-and-surprising-ways-to-configure-lefthook-for-automation-joy?utm_source=lefthook)
* [Lefthook: Knock your team’s code back into shape](https://evilmartians.com/chronicles/lefthook-knock-your-teams-code-back-into-shape?utm_source=lefthook)
* [Lefthook + Crystalball](https://evilmartians.com/chronicles/lefthook-crystalball-and-git-magic?utm_source=lefthook)
* [Keeping OSS documentation in check with docsify, Lefthook, and friends](https://evilmartians.com/chronicles/keeping-oss-documentation-in-check-with-docsify-lefthook-and-friends?utm_source=lefthook)
* [Automatically linting docker containers](https://dev.to/nitzano/linting-docker-containers-2lo6?utm_source=lefthook)
* [Smooth PostgreSQL upgrades in DockerDev environments with Lefthook](https://dev.to/palkan_tula/smooth-postgresql-upgrades-in-dockerdev-environments-with-lefthook-203k?utm_source=lefthook)
* [Lefthook for React/React Native apps](https://blog.logrocket.com/deep-dive-into-lefthook-react-native?utm_source=lefthook)


[documentation]: https://lefthook.dev/
[configuration]: https://lefthook.dev/configuration/index
[examples]: https://lefthook.dev/examples/lefthook-local
[installation]: https://lefthook.dev/install/
[usage]: https://lefthook.dev/usage/
[discussion]: https://github.com/evilmartians/lefthook/discussions
[install-apt]: https://lefthook.dev/installation/deb
[install-ruby]: https://lefthook.dev/installation/ruby
[install-node]: https://lefthook.dev/installation/node
[install-brew]: https://lefthook.dev/installation/homebrew
[install-winget]: https://lefthook.dev/installation/winget
[install-rpm]: https://lefthook.dev/installation/rpm
[install-arch]: https://lefthook.dev/installation/arch
[install-alpine]: https://lefthook.dev/installation/alpine
[config-parallel]: https://lefthook.dev/configuration/parallel
[config-files]: https://lefthook.dev/configuration/files
[config-glob]: https://lefthook.dev/configuration/glob
[config-run]: https://lefthook.dev/configuration/run
[config-scripts]: https://lefthook.dev/configuration/Scripts
[config-tags]: https://lefthook.dev/configuration/tags
[config-output]: https://lefthook.dev/configuration/output
[usage-local-config]: https://lefthook.dev/examples/lefthook-local


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Latest major version of Lefthook is being supported with security updates.

| Version | Supported          |
| ------- | ------------------ |
| 1.x     | :white_check_mark: |
| 0.x     | :x:                |

## Reporting a Vulnerability

If you have found a security issue in Lefthook, please **do not** create a new issue in the GitHub repository. Instead, please send an email to [lefthook@evilmartians.com](mailto:lefthook@evilmartians.com?subject=Lefthook%3A%20security%20issue) describing what the problem is and how to reproduce it. We will get in touch with you!

Please note that Lefthook, as a CLI tool, executes arbitrary commands and scripts from its configuration file by design. This is intended behavior. Feel free to join the discussion on [issue #229](https://github.com/evilmartians/lefthook/issues/229).


================================================
FILE: assets/css/lefthook.css
================================================
:root {
  --link-color: #ff1e1e;
  --font-family-mono: "Martian Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
}

.main-content-wrapper {
  min-height: 100vh;
}

body[data-theme="dark"] {
  --link-color: #ff1e1e;
  --font-family-mono: "Martian Mono", SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
}


================================================
FILE: book.toml
================================================
[book]
authors = ["Evil Martians"]
language = "en"
multilingual = false
src = "docs/mdbook"
title = "Lefthook Documentation"

[output.html]
no-section-label = true
git-repository-url = "https://github.com/evilmartians/lefthook"

[output.html.fold]
enable = true


================================================
FILE: cliff.toml
================================================
# https://git-cliff.org/docs/configuration

[changelog]
header = "# Change log\n\n"
body = """
{% if version %}\
    ## {{ version | trim_start_matches(pat="v") }} ({{ timestamp | date(format="%Y-%m-%d") }})
{% else %}\
    ## (unreleased)
{% endif %}
{% for commit in commits %}\
    - {{ commit.group }}: {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message }} by {% if commit.remote.username %}[@{{commit.remote.username}}](https://github.com/{{commit.remote.username}}) {% else %}{{ commit.author.name }}{% endif %}
{% endfor %}\n
"""
trim = true

[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
  { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/evilmartians/lefthook/pull/${2}))"}, # replace issue numbers
]
# regex for parsing and grouping commits
commit_parsers = [
  { message = "^feat", group = "feat" },
  { message = "^fix", group = "fix" },
  { message = "^docs", group = "docs" },
  { message = "^perf", group = "perf" },
  { message = "^refactor", group = "refactor" },
  { message = "^ci", group = "ci" },
  { message = "^test", group = "test" },
  { message = "^chore\\(release\\): prepare for", skip = true },
  { message = "^chore", group = "chore" },
  { body = ".*security", group = "security" },
]
# protect breaking changes from being skipped due to matching a skipping commit_parser
protect_breaking_commits = false
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]*"
# regex for ignoring tags
ignore_tags = ""
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "newest"
# limit the number of commits included in the changelog.
# limit_commits = 42


================================================
FILE: cmd/add-usage.txt
================================================
lefthook add pre-commit

This command will try to build the following structure in repository:
├───.git
│   └───hooks
│       └───pre-commit // this executable will be added. Existing file with
│                      // same name will be renamed to pre-commit.old
(lefthook adds these dirs if you run the command with the -d option)
│
├───.lefthook          // directory for project level hooks
│   └───pre-commit     // directory with hook executables
└───.lefthook-local    // directory for personal hooks; add it in .gitignore
    └───pre-commit


================================================
FILE: cmd/add.go
================================================
package cmd

import (
	"context"
	_ "embed"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

//go:embed add-usage.txt
var addUsageText string

func add() *cli.Command {
	var args command.AddArgs
	var verbose bool

	return &cli.Command{
		Name:      "add",
		Usage:     "add scripts directory and install the hook",
		UsageText: addUsageText,
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "force",
				Aliases:     []string{"f"},
				Destination: &args.Force,
			},
			&cli.BoolFlag{
				Name:        "create-dirs",
				Aliases:     []string{"dirs"},
				Usage:       "create directories for scripts",
				Destination: &args.CreateDirs,
			},
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(verbose, "auto")
			if err != nil {
				return err
			}

			args.Hook = cmd.Args().Get(0)
			return l.Add(ctx, args)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
			command.ShellCompleteHookNames()
		},
	}
}


================================================
FILE: cmd/check_install.go
================================================
package cmd

import (
	"context"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

func checkInstall() *cli.Command {
	var verbose bool
	return &cli.Command{
		Name:  "check-install",
		Usage: "check if hooks are installed",
		UsageText: `lefthook check-install – Check if lefthook is installed. Exit codes:
0 – hooks are installed
1 – hooks are not installed or stale`,
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(verbose, "auto")
			if err != nil {
				return err
			}

			return l.CheckInstall(ctx)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: cmd/commands.go
================================================
//go:build !no_self_update && !jsonschema

package cmd

import "github.com/urfave/cli/v3"

var commands = []*cli.Command{
	run(),
	install(),
	uninstall(),
	checkInstall(),
	dump(),
	add(),
	validate(),
	version(),
	selfUpdate(),
}


================================================
FILE: cmd/commands_without_self_update.go
================================================
//go:build no_self_update && !jsonschema

package cmd

import "github.com/urfave/cli/v3"

var commands = []*cli.Command{
	run(),
	install(),
	uninstall(),
	checkInstall(),
	dump(),
	add(),
	validate(),
	version(),
	// selfUpdate(),
}


================================================
FILE: cmd/dump.go
================================================
package cmd

import (
	"context"
	"errors"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

var errInvalidFormat = errors.New("invalid 'format' value, supported: 'toml', 'yaml', 'json'")

func dump() *cli.Command {
	args := command.DumpArgs{
		Format: "yaml",
	}

	return &cli.Command{
		Name:  "dump",
		Usage: "print config merged from all extensions",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:        "format",
				Usage:       "'yaml', 'toml', or 'json' (default: 'yaml')",
				Aliases:     []string{"f"},
				Destination: &args.Format,
				Validator: func(format string) error {
					switch format {
					case "":
					case "yaml":
					case "toml":
					case "json":
					default:
						return errInvalidFormat
					}
					return nil
				},
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(false, "no")
			if err != nil {
				return err
			}

			return l.Dump(ctx, args)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: cmd/install.go
================================================
package cmd

import (
	"context"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

func install() *cli.Command {
	var args command.InstallArgs
	var verbose bool

	return &cli.Command{
		Name:      "install",
		Usage:     "install Git hook from the config or create a blank lefthook.yml",
		UsageText: "lefthook install [hook-names...] [options]",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "force",
				Usage:       "overwrite .old files and proceed even if core.hooksPath is set",
				Aliases:     []string{"f"},
				Destination: &args.Force,
			},
			&cli.BoolFlag{
				Name:        "reset-hooks-path",
				Usage:       "automatically unset core.hooksPath configuration",
				Aliases:     []string{"r"},
				Destination: &args.ResetHooksPath,
			},
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(verbose, "auto")
			if err != nil {
				return err
			}

			return l.Install(ctx, args, cmd.Args().Slice())
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
			command.ShellCompleteHookNames()
		},
	}
}


================================================
FILE: cmd/lefthook.go
================================================
package cmd

import (
	"github.com/urfave/cli/v3"

	ver "github.com/evilmartians/lefthook/v2/internal/version"
)

func Lefthook() *cli.Command {
	return &cli.Command{
		Name:     "lefthook",
		Usage:    "Git hooks manager",
		Version:  ver.Version(true),
		Commands: commands,
		Description: `... of supported ENV variables:

LEFTHOOK          set to '0' or 'false' to disable lefthook execution
LEFTHOOK_CONFIG   override main config path
LEFTHOOK_OUTPUT   control printed sections (see config option 'output')
LEFTHOOK_VERBOSE  enable debug logs`,
		EnableShellCompletion: true,
		Suggest:               true,
	}
}


================================================
FILE: cmd/run.go
================================================
package cmd

import (
	"context"
	"errors"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

func run() *cli.Command {
	var args command.RunArgs
	var colors string
	failOnChanges := &cli.BoolWithInverseFlag{
		Name:  "fail-on-changes",
		Usage: "exit with 1 if some of the files were changed",
	}
	failOnChangesDiff := &cli.BoolWithInverseFlag{
		Name:  "fail-on-changes-diff",
		Usage: "output a diff when failing on changes",
	}

	return &cli.Command{
		Name:      "run",
		Usage:     "execute a group of hooks",
		UsageText: "lefthook run <hook-name> [args...] [options]",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Usage:       "enable debug logs",
				Destination: &args.Verbose,
			},
			&cli.StringFlag{
				Name:        "colors",
				Usage:       "on, off, or auto (default: auto)",
				Destination: &colors,
				Value:       "auto",
			},
			&cli.StringSliceFlag{
				Name:        "job",
				Usage:       "run only jobs with names",
				Destination: &args.RunOnlyJobs,
			},
			&cli.StringSliceFlag{
				Name:        "tag",
				Usage:       "run only jobs with tag names",
				Destination: &args.RunOnlyTags,
			},
			&cli.StringSliceFlag{
				Name:        "command",
				Usage:       "run only commands",
				Destination: &args.RunOnlyCommands,
			},
			&cli.StringSliceFlag{
				Name:        "exclude",
				Usage:       "exclude files from all templates",
				Destination: &args.Exclude,
			},
			&cli.StringSliceFlag{
				Name:        "file",
				Usage:       "overwrite file templates with files",
				Destination: &args.Files,
			},
			&cli.BoolFlag{
				Name:        "force",
				Aliases:     []string{"f"},
				Usage:       "do not skip if no files changed",
				Destination: &args.Force,
			},
			&cli.BoolFlag{
				Name:        "all-files",
				Usage:       "replace files templates with {all_files}",
				Destination: &args.AllFiles,
			},
			&cli.BoolFlag{
				Name:        "no-auto-install",
				Usage:       "do not implicitly install hooks",
				Destination: &args.NoAutoInstall,
			},
			&cli.BoolFlag{
				Name:        "no-stage-fixed",
				Usage:       "ignore 'stage_fixed: true' setting",
				Destination: &args.NoStageFixed,
			},
			&cli.BoolFlag{
				Name:        "no-tty",
				Usage:       "act as if no TTY is connected",
				Destination: &args.NoTTY,
			},
			&cli.BoolFlag{
				Name:        "skip-lfs",
				Usage:       "do not run LFS hooks",
				Destination: &args.SkipLFS,
			},
			failOnChanges,
			&cli.BoolFlag{
				Name:        "files-from-stdin",
				Usage:       "parse filelist from STDIN",
				Destination: &args.FilesFromStdin,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(args.Verbose, colors)
			if err != nil {
				return err
			}

			if failOnChanges.IsSet() {
				value := cmd.Bool("fail-on-changes")
				args.FailOnChanges = &value
			}
			if failOnChangesDiff.IsSet() {
				value := cmd.Bool("fail-on-changes-diff")
				args.FailOnChangesDiff = &value
			}

			if cmd.Args().Len() < 1 {
				return errors.New("hook name missing")
			}

			args.Hook = cmd.Args().Get(0)
			args.GitArgs = cmd.Args().Slice()[1:]
			return l.Run(ctx, args)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
			command.ShellCompleteHookNames()
		},
	}
}


================================================
FILE: cmd/self_update.go
================================================
package cmd

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
	"github.com/evilmartians/lefthook/v2/internal/log"
	"github.com/evilmartians/lefthook/v2/internal/updater"
)

func selfUpdate() *cli.Command {
	var yes, force, verbose bool

	return &cli.Command{
		Name:  "self-update",
		Usage: "update lefthook executable",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "yes",
				Aliases:     []string{"y"},
				Usage:       "do not prompt y/n",
				Destination: &yes,
			},
			&cli.BoolFlag{
				Name:        "force",
				Aliases:     []string{"f"},
				Usage:       "force reinstall",
				Destination: &force,
			},
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			if os.Getenv(command.EnvVerbose) == "1" || os.Getenv(command.EnvVerbose) == "true" {
				verbose = true
			}
			if verbose {
				log.SetLevel(log.DebugLevel)
				log.Debug("Verbose mode enabled")
			}

			exePath, err := os.Executable()
			if err != nil {
				return fmt.Errorf("failed to determine the binary path: %w", err)
			}

			ctxCancel, stop := signal.NotifyContext(ctx, os.Interrupt)
			defer stop()

			return updater.New().SelfUpdate(ctxCancel, updater.Options{
				Yes:     yes,
				Force:   force,
				ExePath: exePath,
			})
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: cmd/uninstall.go
================================================
package cmd

import (
	"context"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

func uninstall() *cli.Command {
	var args command.UninstallArgs
	var verbose bool

	return &cli.Command{
		Name:  "uninstall",
		Usage: "delete installed hooks",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
			&cli.BoolFlag{
				Name:        "force",
				Aliases:     []string{"f"},
				Usage:       "remove all Git hooks",
				Destination: &args.Force,
			},
			&cli.BoolFlag{
				Name:        "remove-configs",
				Usage:       "remove lefthook configs",
				Destination: &args.RemoveConfig,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(verbose, "auto")
			if err != nil {
				return err
			}

			return l.Uninstall(ctx, args)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: cmd/validate.go
================================================
package cmd

import (
	"context"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
)

func validate() *cli.Command {
	var args command.ValidateArgs
	var verbose bool

	return &cli.Command{
		Name:  "validate",
		Usage: "validate lefthook config",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			l, err := command.NewLefthook(verbose, "auto")
			if err != nil {
				return nil
			}

			return l.Validate(ctx, args)
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: cmd/version.go
================================================
package cmd

import (
	"context"

	"github.com/urfave/cli/v3"

	"github.com/evilmartians/lefthook/v2/internal/command"
	"github.com/evilmartians/lefthook/v2/internal/log"
	ver "github.com/evilmartians/lefthook/v2/internal/version"
)

func version() *cli.Command {
	var verbose bool

	return &cli.Command{
		Name:  "version",
		Usage: "print version",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:        "verbose",
				Aliases:     []string{"v"},
				Destination: &verbose,
			},
			&cli.BoolFlag{
				Name:        "full",
				Aliases:     []string{"f"},
				Destination: &verbose,
			},
		},
		Action: func(_ctx context.Context, cmd *cli.Command) error {
			log.Println(ver.Version(verbose))
			return nil
		},
		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			command.ShellCompleteFlags(cmd)
		},
	}
}


================================================
FILE: codecov.yml
================================================
comment: false


================================================
FILE: docmd.config.js
================================================
module.exports = {
  siteTitle: "Lefthook",
  siteUrl: "https://lefthook.dev",
  logo: {
    light: "/assets/lefthook.png",
    dark: "/assets/lefthook.png",
    alt: "Logo",
    href: "/"
  },
  favicon: "/assets/favicon.svg",
  srcDir: "docs",
  outputDir: "site",
  layout: {
    spa: true,
    header: {
      enabled: true
    },
    sidebar: {
      collapsible: true,
      defaultCollapsed: false
    },
    optionsMenu: {
      position: "header",
      components: {
        search: true,
        themeSwitch: true,
        sponsor: null
      }
    },
    footer: {
      style: "minimal",
      content: "© 2026 Lefthook. Evil Martians."
    }
  },
  theme: {
    name: "default",
    defaultMode: "system",
    codeHighlight: true,
    customCss: [
      "assets/css/lefthook.css"
    ]
  },
  minify: true,
  autoTitleFromH1: true,
  copyCode: true,
  pageNavigation: false,
  editLink: {
    enabled: false,
    baseUrl: "https://github.com/evilmartians/lefthook/edit/main/docs",
    text: "Edit this page"
  },
  plugins: {
    seo: {
      defaultDescription: "Lefthook documentation.",
      openGraph: {
        defaultImage: "assets/lefthook.png"
      }
    },
    analytics: {},
    sitemap: {
      defaultChangefreq: "weekly",
      defaultPriority: 0.8
    },
    search: {},
    mermaid: {},
    llms: {}
  },
  navigation: [
    {
      title: "Installation",
      icon: "rocket",
      path: "/install",
      collapsible: true,
      children: [
        {
          title: "Ruby gem",
          path: "/installation/ruby"
        },
        {
          title: "NPM",
          path: "/installation/node"
        },
        {
          title: "Go",
          path: "/installation/go"
        },
        {
          title: "Python",
          path: "/installation/python"
        },
        {
          title: "Swift",
          path: "/installation/swift"
        },
        {
          title: "Homebrew",
          path: "/installation/homebrew"
        },
        {
          title: "Winget",
          path: "/installation/winget"
        },
        {
          title: "Scoop",
          path: "/installation/scoop"
        },
        {
          title: "Debian-based distro",
          path: "/installation/deb"
        },
        {
          title: "RPM-based distro",
          path: "/installation/rpm"
        },
        {
          title: "Alpine",
          path: "/installation/alpine"
        },
        {
          title: "Arch Linux",
          path: "/installation/arch"
        },
        {
          title: "Snap",
          path: "/installation/snap"
        },
        {
          title: "Devbox",
          path: "/installation/devbox"
        },
        {
          title: "Mise",
          path: "/installation/mise"
        },
        {
          title: "Manual",
          path: "/installation/manual"
        }
      ]
    },
    {
      title: "Configuration",
      path: "/configuration",
      collapsible: true,
      icon: "settings",
      children: [
        {
          title: "assert_lefthook_installed",
          path: "/configuration/assert_lefthook_installed"
        },
        {
          title: "colors",
          path: "/configuration/colors"
        },
        {
          title: "extends",
          path: "/configuration/extends"
        },
        {
          title: "install_non_git_hooks",
          path: "/configuration/install_non_git_hooks"
        },
        {
          title: "lefthook",
          path: "/configuration/lefthook"
        },
        {
          title: "min_version",
          path: "/configuration/min_version"
        },
        {
          title: "no_auto_install",
          path: "/configuration/no_auto_install"
        },
        {
          title: "no_tty",
          path: "/configuration/no_tty"
        },
        {
          title: "output",
          path: "/configuration/output"
        },
        {
          title: "rc",
          path: "/configuration/rc"
        },
        {
          title: "remotes",
          path: "/configuration/remotes",
          children: [
            {
              title: "git_url",
              path: "/configuration/git_url"
            },
            {
              title: "ref",
              path: "/configuration/ref"
            },
            {
              title: "refetch",
              path: "/configuration/refetch"
            },
            {
              title: "refetch_frequency",
              path: "/configuration/refetch_frequency"
            },
            {
              title: "configs",
              path: "/configuration/configs"
            }
          ]
        },
        {
          title: "source_dir",
          path: "/configuration/source_dir"
        },
        {
          title: "source_dir_local",
          path: "/configuration/source_dir_local"
        },
        {
          title: "skip_lfs",
          path: "/configuration/skip_lfs"
        },
        {
          title: "glob_matcher",
          path: "/configuration/glob_matcher"
        },
        {
          title: "templates",
          path: "/configuration/templates"
        },
        {
          title: "Hook",
          path: "/configuration/Hook",
          children: [
            {
              title: "parallel",
              path: "/configuration/parallel"
            },
            {
              title: "piped",
              path: "/configuration/piped"
            },
            {
              title: "follow",
              path: "/configuration/follow"
            },
            {
              title: "files",
              path: "/configuration/files-global"
            },
            {
              title: "fail_on_changes",
              path: "/configuration/fail_on_changes"
            },
            {
              title: "fail_on_changes_diff",
              path: "/configuration/fail_on_changes_diff"
            },
            {
              title: "exclude_tags",
              path: "/configuration/exclude_tags"
            },
            {
              title: "exclude",
              path: "/configuration/exclude"
            },
            {
              title: "only",
              path: "/configuration/only"
            },
            {
              title: "skip",
              path: "/configuration/skip"
            },
            {
              title: "setup",
              path: "/configuration/setup"
            },
            {
              title: "jobs",
              path: "/configuration/jobs",
              children: [
                {
                  title: "name",
                  path: "/configuration/name"
                },
                {
                  title: "run",
                  path: "/configuration/run"
                },
                {
                  title: "script",
                  path: "/configuration/script"
                },
                {
                  title: "runner",
                  path: "/configuration/runner"
                },
                {
                  title: "args",
                  path: "/configuration/args"
                },
                {
                  title: "group",
                  collapsible: true,
                  path: "/configuration/group",
                  children: [
                    {
                      title: "parallel",
                      path: "/configuration/parallel"
                    },
                    {
                      title: "piped",
                      path: "/configuration/piped"
                    },
                    {
                      title: "jobs",
                      path: "/configuration/jobs"
                    }
                  ]
                },
                {
                  title: "skip",
                  path: "/configuration/skip"
                },
                {
                  title: "only",
                  path: "/configuration/only"
                },
                {
                  title: "tags",
                  path: "/configuration/tags"
                },
                {
                  title: "glob",
                  path: "/configuration/glob"
                },
                {
                  title: "files",
                  path: "/configuration/files"
                },
                {
                  title: "file_types",
                  path: "/configuration/file_types"
                },
                {
                  title: "env",
                  path: "/configuration/env"
                },
                {
                  title: "root",
                  path: "/configuration/root"
                },
                {
                  title: "exclude",
                  path: "/configuration/exclude"
                },
                {
                  title: "fail_text",
                  path: "/configuration/fail_text"
                },
                {
                  title: "stage_fixed",
                  path: "/configuration/stage_fixed"
                },
                {
                  title: "interactive",
                  path: "/configuration/interactive"
                },
                {
                  title: "use_stdin",
                  path: "/configuration/use_stdin"
                }
              ]
            },
            {
              title: "commands",
              path: "/configuration/Commands",
              children: [
                {
                  title: "run",
                  path: "/configuration/run"
                },
                {
                  title: "skip",
                  path: "/configuration/skip"
                },
                {
                  title: "only",
                  path: "/configuration/only"
                },
                {
                  title: "tags",
                  path: "/configuration/tags"
                },
                {
                  title: "glob",
                  path: "/configuration/glob"
                },
                {
                  title: "files",
                  path: "/configuration/files"
                },
                {
                  title: "file_types",
                  path: "/configuration/file_types"
                },
                {
                  title: "env",
                  path: "/configuration/env"
                },
                {
                  title: "root",
                  path: "/configuration/root"
                },
                {
                  title: "exclude",
                  path: "/configuration/exclude"
                },
                {
                  title: "fail_text",
                  path: "/configuration/fail_text"
                },
                {
                  title: "stage_fixed",
                  path: "/configuration/stage_fixed"
                },
                {
                  title: "interactive",
                  path: "/configuration/interactive"
                },
                {
                  title: "use_stdin",
                  path: "/configuration/use_stdin"
                },
                {
                  title: "priority",
                  path: "/configuration/priority"
                }
              ]
            },
            {
              title: "scripts",
              path: "/configuration/Scripts",
              children: [
                {
                  title: "runner",
                  path: "/configuration/runner"
                },
                {
                  title: "args",
                  path: "/configuration/args"
                },
                {
                  title: "skip",
                  path: "/configuration/skip"
                },
                {
                  title: "only",
                  path: "/configuration/only"
                },
                {
                  title: "tags",
                  path: "/configuration/tags"
                },
                {
                  title: "env",
                  path: "/configuration/env"
                },
                {
                  title: "fail_text",
                  path: "/configuration/fail_text"
                },
                {
                  title: "stage_fixed",
                  path: "/configuration/stage_fixed"
                },
                {
                  title: "interactive",
                  path: "/configuration/interactive"
                },
                {
                  title: "use_stdin",
                  path: "/configuration/use_stdin"
                },
                {
                  title: "priority",
                  path: "/configuration/priority"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      title: "CLI",
      collapsible: true,
      icon: "terminal",
      children: [
        {
          title: "lefthook install",
          icon: "chevron-right",
          path: "/usage/commands/install"
        },
        {
          title: "lefthook uninstall",
          icon: "chevron-right",
          path: "/usage/commands/uninstall"
        },
        {
          title: "lefthook run",
          icon: "chevron-right",
          path: "/usage/commands/run"
        },
        {
          title: "lefthook add",
          icon: "chevron-right",
          path: "/usage/commands/add"
        },
        {
          title: "lefthook validate",
          icon: "chevron-right",
          path: "/usage/commands/validate"
        },
        {
          title: "lefthook dump",
          icon: "chevron-right",
          path: "/usage/commands/dump"
        },
        {
          title: "lefthook check-install",
          icon: "chevron-right",
          path: "/usage/commands/check-install"
        },
        {
          title: "lefthook self-update",
          icon: "chevron-right",
          path: "/usage/commands/self-update"
        },
        {
          title: "ENV variables",
          collapsible: true,
          icon: "dollar-sign",
          children: [
            {
              title: "LEFTHOOK",
              path: "/usage/envs/LEFTHOOK"
            },
            {
              title: "LEFTHOOK_VERBOSE",
              path: "/usage/envs/LEFTHOOK_VERBOSE"
            },
            {
              title: "LEFTHOOK_OUTPUT",
              path: "/usage/envs/LEFTHOOK_OUTPUT"
            },
            {
              title: "LEFTHOOK_CONFIG",
              path: "/usage/envs/LEFTHOOK_CONFIG"
            },
            {
              title: "LEFTHOOK_EXCLUDE",
              path: "/usage/envs/LEFTHOOK_EXCLUDE"
            },
            {
              title: "CLICOLOR_FORCE",
              path: "/usage/envs/CLICOLOR_FORCE"
            },
            {
              title: "NO_COLOR",
              path: "/usage/envs/NO_COLOR"
            },
            {
              title: "CI",
              path: "/usage/envs/CI"
            }
          ]
        }
      ]
    },
    {
      title: "Examples",
      collapsible: true,
      icon: "file-code",
      children: [
        {
          title: "Using local only config",
          path: "/examples/lefthook-local"
        },
        {
          title: "Wrap commands locally",
          path: "/examples/wrap-commands"
        },
        {
          title: "Auto add linter fixes to commit",
          path: "/examples/stage_fixed"
        },
        {
          title: "Filter files",
          path: "/examples/filters"
        },
        {
          title: "Skip or run on condition",
          path: "/examples/skip"
        },
        {
          title: "Remote configs",
          path: "/examples/remotes"
        },
        {
          title: "With commitlint",
          path: "/examples/commitlint"
        }
      ]
    },
    {
      title: "Contributors",
      path: "/misc/contributors",
      icon: "users-round"
    },
    {
      title: "GitHub",
      path: "https://github.com/evilmartians/lefthook",
      icon: "github",
      external: true
    }
  ]
};


================================================
FILE: docs/configuration/Commands.md
================================================
---
title: "commands"
---

# `commands`

Commands to be executed for the hook. Each command has a name and associated run [options](#command).

#### Example

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      ... # command options
```

### Command options

- [`run`](./run.md)
- [`skip`](./skip.md)
- [`only`](./only.md)
- [`tags`](./tags.md)
- [`glob`](./glob.md)
- [`files`](./files.md)
- [`file_types`](./file_types.md)
- [`env`](./env.md)
- [`root`](./root.md)
- [`exclude`](./exclude.md)
- [`fail_text`](./fail_text.md)
- [`stage_fixed`](./stage_fixed.md)
- [`interactive`](./interactive.md)
- [`use_stdin`](./use_stdin.md)
- [`priority`](./priority.md)


================================================
FILE: docs/configuration/Hook.md
================================================
---
title: "Hook"
---

# Git hook

Contains settings for the git hook (commands, scripts, skip rules, etc.). You can specify any Git hook or your own custom, e.g. `test`


```yml
# lefthook.yml

# Git hook
pre-commit:
  jobs:
    - run: yarn lint {staged_files} --fix
      stage_fixed: true

# Custom hook
check-docs:
  jobs:
    - run: yarn check-docs
    - run: typos
```


================================================
FILE: docs/configuration/README.md
================================================
## Config file name

Lefthook supports the following file names for the main config:

| Format | File name |
|-------|-----------|
| YAML  | `lefthook.yml` |
| YAML  | `.lefthook.yml` |
| YAML  | `.config/lefthook.yml` |
|       |              |
| YAML  | `lefthook.yaml` |
| YAML  | `.lefthook.yaml` |
| YAML  | `.config/lefthook.yaml` |
|       |              |
| TOML  | `lefthook.toml` |
| TOML  | `.lefthook.toml` |
| TOML  | `.config/lefthook.toml` |
|       |              |
| JSON  | `lefthook.json` |
| JSON  | `.lefthook.json` |
| JSON  | `.config/lefthook.json` |
|       |              |
| JSONC | `lefthook.jsonc` |
| JSONC | `.lefthook.jsonc` |
| JSONC | `.config/lefthook.jsonc` |

If there are more than 1 file in the project, only one will be used, and you'll never know which one. So, please, use one format in a project.

Filenames without the leading dot will also be looked up from the [`.config` subdirectory](https://github.com/pi0/config-dir).

Lefthook also merges an extra config with the name `lefthook-local`. All supported formats can be applied to this `-local` config. If you name your main config with the leading dot, like `.lefthook.json`, the `-local` config also must be named with the leading dot: `.lefthook-local.json`.

The `-local` config can be used without a main config file. This is useful when you want to use lefthook locally without imposing it on your teammates – just create a `lefthook-local.yml` file and add it to your global `.gitignore`.

## Options

- [`assert_lefthook_installed`](./assert_lefthook_installed.md)
- [`colors`](./colors.md)
- [`extends`](./extends.md)
- [`lefthook`](./lefthook.md)
- [`min_version`](./min_version.md)
- [`no_tty`](./no_tty.md)
- [`output`](./output.md)
- [`rc`](./rc.md)
- [`remotes`](./remotes.md)
  - [`git_url`](./git_url.md)
  - [`ref`](./ref.md)
  - [`refetch`](./refetch.md)
  - [`refetch_frequency`](./refetch_frequency.md)
  - [`configs`](./configs.md)
- [`source_dir`](./source_dir.md)
- [`source_dir_local`](./source_dir_local.md)
- [`skip_lfs`](./skip_lfs.md)
- [`templates`](./templates.md)
- [{Git hook name}](./Hook.md) (e.g. `pre-commit`)
  - [`files` (global)](./files-global.md)
  - [`parallel`](./parallel.md)
  - [`piped`](./piped.md)
  - [`follow`](./follow.md)
  - [`fail_on_changes`](./fail_on_changes.md)
  - [`fail_on_changes_diff`](./fail_on_changes_diff.md)
  - [`exclude_tags`](./exclude_tags.md)
  - [`exclude`](./exclude.md)
  - [`skip`](./skip.md)
  - [`only`](./only.md)
  - [`jobs`](./jobs.md)
    - [`name`](./name.md)
    - [`run`](./run.md)
    - [`script`](./script.md)
    - [`runner`](./runner.md)
    - [`args`](./args.md)
    - [`group`](./group.md)
      - [`parallel`](./parallel.md)
      - [`piped`](./piped.md)
      - [`jobs`](./jobs.md)
    - [`skip`](./skip.md)
    - [`only`](./only.md)
    - [`tags`](./tags.md)
    - [`glob`](./glob.md)
    - [`files`](./files.md)
    - [`file_types`](./file_types.md)
    - [`env`](./env.md)
    - [`root`](./root.md)
    - [`exclude`](./exclude.md)
    - [`fail_text`](./fail_text.md)
    - [`stage_fixed`](./stage_fixed.md)
    - [`interactive`](./interactive.md)
    - [`use_stdin`](./use_stdin.md)
  - [`commands`](./Commands.md)
    - [`run`](./run.md)
    - [`skip`](./skip.md)
    - [`only`](./only.md)
    - [`tags`](./tags.md)
    - [`glob`](./glob.md)
    - [`files`](./files.md)
    - [`file_types`](./file_types.md)
    - [`env`](./env.md)
    - [`root`](./root.md)
    - [`exclude`](./exclude.md)
    - [`fail_text`](./fail_text.md)
    - [`stage_fixed`](./stage_fixed.md)
    - [`interactive`](./interactive.md)
    - [`use_stdin`](./use_stdin.md)
    - [`priority`](./priority.md)
  - [`scripts`](./Scripts.md)
    - [`runner`](./runner.md)
    - [`args`](./args.md)
    - [`skip`](./skip.md)
    - [`only`](./only.md)
    - [`tags`](./tags.md)
    - [`env`](./env.md)
    - [`fail_text`](./fail_text.md)
    - [`stage_fixed`](./stage_fixed.md)
    - [`interactive`](./interactive.md)
    - [`use_stdin`](./use_stdin.md)
    - [`priority`](./priority.md)


================================================
FILE: docs/configuration/Scripts.md
================================================
---
title: "Scripts"
---

# Scripts

Scripts are stored under `<source_dir>/<hook-name>/` folder. These scripts are your own executables which are being run in the project root.

To add a script for a `pre-commit` hook:

1. Run `lefthook add -d pre-commit`
1. Edit `.lefthook/pre-commit/my-script.sh`
1. Add an entry to `lefthook.yml`
   ```yml
   # lefthook.yml

   pre-commit:
     scripts:
       "my-script.sh":
         runner: bash
   ```

### Example

Let's create a bash script to check commit templates `.lefthook/commit-msg/template_checker`:

```bash
INPUT_FILE=$1
START_LINE=`head -n1 $INPUT_FILE`
PATTERN="^(TICKET)-[[:digit:]]+: "
if ! [[ "$START_LINE" =~ $PATTERN ]]; then
  echo "Bad commit message, see example: TICKET-123: some text"
  exit 1
fi
```

Now we can ask lefthook to run our bash script by adding this code to
`lefthook.yml` file:

```yml
# lefthook.yml

commit-msg:
  scripts:
    "template_checker":
      runner: bash
```

When you try to commit `git commit -m "bad commit text"` script `template_checker` will be executed. Since commit text doesn't match the described pattern the commit process will be interrupted.


================================================
FILE: docs/configuration/args.md
================================================
---
title: "args"
---

# `args`

::: callout tip New feature
Added in lefthook `2.0.5`
:::

Sometimes you want to pass arguments to the scripts or be able to overwrite arguments to the commands in `lefthook-local.yml`. For this you can use `args` option which will simply be appended to the command. You can use the same templates as in [`run`](./run.md).

Arguments passed by Git will be omitted if you specify `args` in the config. Providing no `args` or providing `args: "{0}"` works the same way.

See [`run`](./run.md) for supported templates.

#### Example

```yml
# lefthook.yml

pre-commit:
  jobs:
    - script: check-python-files.sh
      runner: bash
      args: "{staged_files}"
      glob: "*.py"

    - run: yarn lint
      args: "{staged_files}"
      glob:
        - "*.ts"
        - "*.js"
```


================================================
FILE: docs/configuration/assert_lefthook_installed.md
================================================
---
title: "assert_lefthook_installed"
---

# `assert_lefthook_installed`

**Default: `false`**

When set to `true`, fail (with exit status 1) if `lefthook` executable can't be found in $PATH, under node_modules/, as a Ruby gem, or other supported method. This makes sure git hook won't omit `lefthook` rules if `lefthook` ever was installed.


================================================
FILE: docs/configuration/colors.md
================================================
---
title: "colors"
---

# `colors`

**Default: `auto`**

Whether enable or disable colorful output of Lefthook. This option can be overwritten with `--colors` option. You can also provide your own color codes.

#### Example

Disable colors.

```yml
# lefthook.yml

colors: false
```

Custom color codes. Can be hex or ANSI codes.

```yml
# lefthook.yml

colors:
  cyan: 14
  gray: 244
  green: '#32CD32'
  red: '#FF1493'
  yellow: '#F0E68C'
```

Control via ENV variable.

- Set `NO_COLOR=true` to disable colored output in lefthook and all subcommands that lefthook calls.
- Set `CLICOLOR_FORCE=true` to force colored output in lefthook and all subcommands.


================================================
FILE: docs/configuration/configs.md
================================================
---
title: "configs"
---

# `configs`

**Default:** `[lefthook.yml]`

An optional array of config paths from remote's root.

#### Example

```yml
# lefthook.yml

remotes:
  - git_url: git@github.com:evilmartians/lefthook
    ref: v1.0.0
    configs:
      - examples/ruby-linter.yml
      - examples/test.yml
```

Example with multiple remotes merging multiple configurations.

```yml
# lefthook.yml

remotes:
  - git_url: git@github.com:org/lefthook-configs
    ref: v1.0.0
    configs:
      - examples/ruby-linter.yml
      - examples/test.yml
  - git_url: https://github.com/org2/lefthook-configs
    configs:
      - lefthooks/pre_commit.yml
      - lefthooks/post_merge.yml
  - git_url: https://github.com/org3/lefthook-configs
    ref: feature/new
    configs:
      - configs/pre-push.yml

```


================================================
FILE: docs/configuration/env.md
================================================
---
title: "env"
---

# `env`

You can specify some ENV variables for the command or script.

#### Example

```yml
# lefthook.yml

pre-commit:
  commands:
    test:
      env:
        RAILS_ENV: test
      run: bundle exec rspec
```

#### Extending PATH

If your hook is run by GUI program, and you use some PATH tweaks in your ~/.<shell>rc, you might see an error saying *executable not found*. In that case You can extend the **$PATH** variable with `lefthook-local.yml` configuration the following way.

```yml
# lefthook.yml

pre-commit:
  commands:
    test:
      run: yarn test
```

```yml
# lefthook-local.yml

pre-commit:
  commands:
    test:
      env:
        PATH: $PATH:/home/me/path/to/yarn
```

**Notes**

This option is useful when using lefthook on different OSes or shells where ENV variables are set in different ways.


================================================
FILE: docs/configuration/exclude.md
================================================
---
title: "exclude"
---

# `exclude`

This option allows to setup a list of globs for files to be excluded in files template.

::: callout info Note
The glob patterns used in `exclude` are affected by the [`glob_matcher`](./glob_matcher.md) setting. See the glob_matcher documentation for details on how `**` patterns behave.
:::

#### Example

Run Rubocop on staged files with `.rb` extension except for `application.rb`, `routes.rb`, `rails_helper.rb`, and all Ruby files in `config/initializers/`.

```yml
# lefthook.yml

pre-commit:
  jobs:
    - name: lint
      glob: "*.rb"
      exclude:
        - config/routes.rb
        - config/application.rb
        - config/initializers/*.rb
        - spec/rails_helper.rb
      run: bundle exec rubocop --force-exclusion -- {staged_files}
```

If you've specified `exclude` but don't have a files template in [`run`](./run.md) option, lefthook will check `{staged_files}` for `pre-commit` hook and `{push_files}` for `pre-push` hook and apply filtering. If no files left, the command will be skipped.

```yml
# lefthook.yml

pre-commit:
  exclude:
    - "*/application.rb"
  jobs:
    - name: lint
      run: bundle exec rubocop # will skip if only application.rb was staged
```


================================================
FILE: docs/configuration/exclude_tags.md
================================================
---
title: "exclude_tags"
---

# `exclude_tags`

[Tags](./tags.md) or command names that you want to exclude. This option can be overwritten with `LEFTHOOK_EXCLUDE` env variable.

#### Example

```yml
# lefthook.yml

pre-commit:
  exclude_tags: frontend
  commands:
    lint:
      tags: frontend
      ...
    test:
      tags: frontend
      ...
    check-syntax:
      tags: documentation
```

```bash
lefthook run pre-commit # will only run check-syntax command
```

**Notes**

This option is good to specify in `lefthook-local.yml` when you want to skip some execution locally.

```yml
# lefthook.yml

pre-push:
  commands:
    packages-audit:
      tags:
        - frontend
        - security
      run: yarn audit
    gems-audit:
      tags:
        - backend
        - security
      run: bundle audit
```

You can skip commands by tags:

```yml
# lefthook-local.yml

pre-push:
  exclude_tags:
    - frontend
```


================================================
FILE: docs/configuration/extends.md
================================================
---
title: "extends"
---

# `extends`

You can extend your config with another one YAML file. Its content will be merged. Extends for `lefthook.yml`, `lefthook-local.yml`, and [`remotes`](./remotes.md) configs are handled separately, so you can have different extends in these files.

You can use asterisk to make a glob.

#### Example

```yml
# lefthook.yml

extends:
  - /home/user/work/lefthook-extend.yml
  - /home/user/work/lefthook-extend-2.yml
  - lefthook-extends/file.yml
  - ../extend.yml
  - projects/*/specific-lefthook-config.yml
```

> The extends will be merged to the main configuration in your file. Here is the order of settings applied:
>
> - `lefthook.yml` – main config file
> - `extends` – configs specified in [extends](./extends.md) option
> - `remotes` – configs specified in [remotes](./remotes.md) option
> - `lefthook-local.yml` – local config file
>
> So, `extends` override settings from `lefthook.yml`, `remotes` override `extends`, and `lefthook-local.yml` can override everything.


================================================
FILE: docs/configuration/fail_on_changes.md
================================================
---
title: "fail_on_changes"
---

# `fail_on_changes`

The behaviour of lefthook when files (tracked by git) are modified can set by modifying the `fail_on_changes` configuration parameter. The possible values are:

- `never`: never exit with a non-zero status if files were modified (default).
- `always`: always exit with a non-zero status if files were modified.
- `ci`: exit with a non-zero status only when the `CI` environment variable is set. This can be useful when combined with `stage_fixed` to ensure a frictionless devX locally, and a robust CI.
- `non-ci`: exit with a non-zero status only when the `CI` environment variable is _not_ set. This can be useful in setups where the CI pipeline commits changes automatically, such as [autofix.ci](https://autofix.ci).

See also [`fail_on_changes_diff`](./fail_on_changes_diff.md).

```yml
# lefthook.yml
pre-commit:
  parallel: true
  fail_on_changes: "always"
  commands:
    lint:
      run: yarn lint
    test:
      run: yarn test
```


================================================
FILE: docs/configuration/fail_on_changes_diff.md
================================================
---
title: "fail_on_changes_diff"
---

# `fail_on_changes_diff`

When Lefthook exits with a non-zero status as a result of [`fail_on_changes`](./fail_on_changes.md) triggering,
it can optionally output a diff of the detected changes.

The default behavior is to output the diff when run in a CI pipeline.
The `fail_on_changes_diff` boolean configuration parameter can be used to override this.

```yml
# lefthook.yml
pre-commit:
  parallel: true
  fail_on_changes: "always"
  fail_on_changes_diff: true
  commands:
    lint:
      run: yarn lint
    test:
      run: yarn test
```


================================================
FILE: docs/configuration/fail_text.md
================================================
---
title: "fail_text"
---

# `fail_text`

You can specify a text to show when the command or script fails.

#### Example

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      run: yarn lint
      fail_text: Add node executable to $PATH
```

```bash
$ git commit -m 'fix: Some bug'

Lefthook v1.1.3
RUNNING HOOK: pre-commit

  EXECUTE > lint

SUMMARY: (done in 0.01 seconds)
🥊  lint: Add node executable to $PATH env
```


================================================
FILE: docs/configuration/file_types.md
================================================
---
title: "file_types"
---

# `file_types`

Filter files in a [`run`](./run.md) templates by their type. Special file types and MIME types are supported[^1]:

|File type| Explanation|
|---------|-----------|
|`text`   | Any file that contains text. Symlinks are not followed. |
|`binary` | Any file that contains non-text bytes. Symlinks are not followed. |
|`executable` | Any file that has executable bits set. Symlinks are not followed. |
|`not executable` | Any file without executable bits in file mode. Symlinks included. |
|`symlink` | A symlink file. |
|`not symlink` | Any non-symlink file. |
|`text/html` | An HTML file. |
|`text/xml`  | An XML file. |
|`text/javascript` | A Javascript file. |
|`text/x-php` | A PHP file. |
|`text/x-lua` | A Lua file. |
|`text/x-perl` | A Perl file. |
|`text/x-python` | A Python file. |
|`text/x-shellscript` | Shell script file. |
|`text/x-sh` | Also shell script file. |
|`application/json` | JSON file. |

> **Important**
> The following types are applied using AND logic:
> - text
> - binary
> - executable
> - not executable
> - symlink
> - not symlink
>
> The mime types are applied using OR logic. So, you can have both `text/x-lua` and `text/x-sh`, but you can't specify both `symlink` and `not symlink`.

**Examples**

Apply some different linters on text and binary files.

```yml
# lefthook.yml

pre-commit:
  commands:
    lint-code:
      run: yarn lint {staged_files}
      file_types: text
    check-hex-codes:
      run: yarn check-hex {staged_files}
      file_types: binary
```

Skip symlinks.

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      run: yarn lint --fix {staged_files}
      file_types:
        - not symlink
```

Lint executable scripts.

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      run: yarn lint --fix {staged_files}
      file_types:
        - executable
        - text
```

Check typos in scripts.

```yml
# lefthook.yml

pre-commit:
  jobs:
    - run: typos -w -- {staged_files}
      file_types:
        - text/x-perl
        - text/x-python
        - text/x-php
        - text/x-lua
        - text/x-sh
```

[^1]: All supported MIME types can be found here: [supported_mimes.md](https://github.com/gabriel-vasile/mimetype/blob/v1.4.11/supported_mimes.md)


================================================
FILE: docs/configuration/files-global.md
================================================
---
title: "files (hook-level)"
---

# `files`

A custom command executed by the `sh` shell that returns the files or directories to be referenced in `{files}` template. See [`run`](#run) and [`files`](#files).

If the result of this command is empty, the execution of commands will be skipped.

#### Example

```yml
# lefthook.yml

pre-commit:
  files: git diff --name-only master # custom list of files
  commands:
    ...
```


================================================
FILE: docs/configuration/files.md
================================================
---
title: "files (job-level)"
---

# `files`

A custom command executed by the `sh` shell that returns the files or directories to be referenced in `{files}` template for [`run`](./run.md) setting.

If the result of this command is empty, the execution of commands will be skipped.

This option overwrites the [hook-level `files`](./files-global.md) option.

#### Example

Provide a git command to list files.

```yml
# lefthook.yml

pre-push:
  commands:
    stylelint:
      tags:
        - frontend
        - style
      files: git diff --name-only master
      glob: "*.js"
      run: yarn stylelint {files}
```

Call a custom script for listing files.

```yml
# lefthook.yml

pre-push:
  commands:
    rubocop:
      tags: backend
      glob: "**/*.rb"
      files: node ./lefthook-scripts/ls-files.js # you can call your own scripts
      run: bundle exec rubocop --force-exclusion --parallel -- {files}
```


================================================
FILE: docs/configuration/follow.md
================================================
---
title: "follow"
---

# `follow`

**Default: `false`**

Follow the STDOUT of the running commands and scripts.

#### Example

```yml
# lefthook.yml

pre-push:
  follow: true
  commands:
    backend-tests:
      run: bundle exec rspec
    frontend-tests:
      run: yarn test
```

::: callout info Note
If used with [`parallel`](#parallel) the output can be a mess, so please avoid setting both options to `true`
:::


================================================
FILE: docs/configuration/git_url.md
================================================
---
title: "git_url"
---

# `git_url`

A URL to Git repository. It will be accessed with privileges of the machine lefthook runs on.

#### Example

```yml
# lefthook.yml

remotes:
  - git_url: git@github.com:evilmartians/lefthook
```

Or

```yml
# lefthook.yml

remotes:
  - git_url: https://github.com/evilmartians/lefthook
```


================================================
FILE: docs/configuration/glob.md
================================================
---
title: "glob"
---

# `glob`

You can set a glob to filter files for your command. This is only used if you use a file template in [`run`](./run.md) option or provide your custom [`files`](./files.md) command.

#### Example

```yml
# lefthook.yml

pre-commit:
  jobs:
    - name: lint
      run: yarn eslint {staged_files}
      glob: "*.{js,ts,jsx,tsx}"
```

::: callout info Note
From lefthook version `1.10.10` you can also provide a list of globs:

```yml
# lefthook.yml

pre-commit:
  jobs:
    - run: yarn lint {staged_files}
      glob:
        - "*.ts"
        - "*.js"
```
:::

For patterns that you can use see [this](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm) reference. We use [glob](https://github.com/gobwas/glob) library.

**When using `root:`**

Globs are still calculated from the actual root of the git repo, `root` is ignored.

**Behaviour of `**`**

Note that the behaviour of `**` is different from typical glob implementations, like `ls` or tools like `lint-staged` in that a double-asterisk matches 1+ directories deep, not zero or more directories.
If you want to match *both* files at the top level and nested, then rather than:

```yaml
glob: "src/**/*.js"
```

You'll need:

```yaml
glob: "src/*.js"
```

Alternatively, you can opt-in to standard glob behavior by setting [`glob_matcher: doublestar`](./glob_matcher.md) at the top level of your configuration. With this setting, `**` will match 0 or more directories, making it consistent with most other glob implementations.

**Using `glob` without a files template in`run`**

If you've specified `glob` but don't have a files template in [`run`](./run.md) option, lefthook will check `{staged_files}` for `pre-commit` hook and `{push_files}` for `pre-push` hook and apply filtering. If no files left, the command will be skipped.

```yml
# lefthook.yml

pre-commit:
  jobs:
    - name: lint
      run: npm run lint # skipped if no .js files staged
      glob: "*.js"
```


================================================
FILE: docs/configuration/glob_matcher.md
================================================
---
title: "glob_matcher"
---

# `glob_matcher`

You can configure which glob matching engine lefthook uses to filter files. By default, lefthook uses `gobwas/glob`, but you can opt-in to use `doublestar` for standard glob behavior.

**Values:**
- `gobwas` (default): The current glob implementation
- `doublestar`: Standard glob behavior where `**` matches 0 or more directories

**Example:**

```yml
# lefthook.yml

glob_matcher: doublestar

pre-commit:
  jobs:
    - name: lint
      run: yarn eslint {staged_files}
      glob: "**/*.{js,ts}"
```

### Key Differences

The main difference between the two matchers is how they handle `**`:

#### Default behavior (`gobwas`)

The `**` pattern matches **1 or more** directories:
- `**/*.js` matches `folder/file.js`, `a/b/c/file.js`
- `**/*.js` does **NOT** match `file.js` at the root level

#### Standard behavior (`doublestar`)

The `**` pattern matches **0 or more** directories:
- `**/*.js` matches `file.js`, `folder/file.js`, `a/b/c/file.js`
- This is consistent with most glob implementations

### When to Use

**Use `glob_matcher: doublestar` when:**
- You want standard glob behavior consistent with other tools
- You need `**` to match files at any level including the root
- You're migrating from other tools that use standard glob patterns

**Keep the default (`gobwas`) when:**
- You want to maintain existing behavior
- You specifically need `**` to require at least one directory level
- You have existing patterns that depend on the current behavior

### Example Comparison

```yml
# With default (gobwas)
glob_matcher: gobwas  # or omit this line

pre-commit:
  jobs:
    - run: eslint -- {staged_files}
      glob: "**/*.js"
      # Matches: src/app.js, lib/util.js
      # Does NOT match: app.js

    - run: eslint -- {staged_files}
      glob: "*.js"
      # Matches: app.js
      # Does NOT match: src/app.js
```

```yml
# With doublestar
glob_matcher: doublestar

pre-commit:
  jobs:
    - run: eslint -- {staged_files}
      glob: "**/*.js"
      # Matches: app.js, src/app.js, lib/util.js
```

### Notes

- The `glob_matcher` setting is global and applies to all `glob` and `exclude` patterns in your configuration
- This setting does not affect `root` or other path-related options
- The setting is fully backward compatible - existing configurations continue to work without modification


================================================
FILE: docs/configuration/group.md
================================================
---
title: "group"
---

# `group`

You can define a group of jobs and configure how they should execute using the following options:

- [`parallel`](./parallel.md): Executes all jobs in the group simultaneously.
- [`piped`](./piped.md): Executes jobs sequentially, passing output between them.
- [`jobs`](./jobs.md): Specifies the jobs within the group.

### Example

```yml
# lefthook.yml

pre-commit:
  jobs:
    - group:
        parallel: true
        jobs:
          - run: echo 1
          - run: echo 2
          - run: echo 3
```

If you specify `env`, `root`, `glob`, or `exclude` on a group, they will be inherited to the underlying jobs.

```yml
# lefthook.yml

pre-commit:
  jobs:
    - env:
        E1: hello
      glob:
        - "*.md"
      exclude:
        - "README.md"
      root: "subdir/"
      group:
        parallel: true
        jobs:
          - run: echo $E1
          - run: echo $E1
            env:
              E1: bonjour
```

::: callout info Note
To make a group mergeable with settings defined in local config or extends you have to specify the name of the job group belongs to:
```yml
pre-commit:
  jobs:
    - name: a name of a group
      group:
        jobs:
          - name: lint
            run: yarn lint
          - name: test
            run: yarn test
```
:::


================================================
FILE: docs/configuration/install_non_git_hooks.md
================================================
---
title: "install_non_git_hooks"
---

# `install_non_git_hooks`

> Since lefthook 2.0.17

Install non-Git hooks into `.git/hooks`. May be useful for using with tools like https://git-flow.sh/.


================================================
FILE: docs/configuration/interactive.md
================================================
---
title: "interactive"
---

# `interactive`

**Default: `false`**

::: callout info Note
If you want to pass stdin to your command or script but don't need to get the input from CLI, use [`use_stdin`](./use_stdin.md) option instead.
:::


Whether to use interactive mode. This applies the certain behavior:
- All `interactive` commands/scripts are executed after non-interactive. Exception: [`piped`](./piped.md) option is set to `true`.
- When executing, lefthook tries to open /dev/tty (Linux/Unix only) and use it as stdin.
- When [`no_tty`](./no_tty.md) option is set, `interactive` is ignored.


================================================
FILE: docs/configuration/jobs.md
================================================
---
title: "jobs"
---

# `jobs`

::: callout tip New feature
Added in lefthook `1.10.0`
:::

Jobs provide a flexible way to define tasks, supporting both commands and scripts. Jobs can be grouped for advanced flow control.

### Basic example

Define jobs in your `lefthook.yml` file under a specific hook like `pre-commit`:

```yml
# lefthook.yml

pre-commit:
  jobs:
    - run: yarn lint
    - run: yarn test
```

### Differences from Commands and Scripts

**Optional Job Names**

- Named jobs are merged across [`extends`](./extends.md) and local config.
- Unnamed jobs are appended in the order of their definition.

**Job Groups**

- Groups can include other jobs.
- Flow within groups can be parallel or piped. Options `glob`, `root`, and `exclude` apply to all jobs in the group, including nested ones.

### Example

::: callout info Note
Currently, only `root`, `glob`, and `exclude` options are applied to group jobs. Other options must be set for each job individually. Submit a [feature request](https://github.com/evilmartians/lefthook/issues/new?assignees=&labels=feature+request&projects=&template=feature_request.md) if this limits your workflow.
:::

A configuration demonstrating a piped group running in parallel with other jobs:

```yml
# lefthook.yml

pre-commit:
  parallel: true
  jobs:
    - name: migrate
      root: backend/
      glob: "db/migrations/*"
      group:
        piped: true
        jobs:
          - run: bundle install
          - run: rails db:migrate
    - run: yarn lint --fix {staged_files}
      root: frontend/
      stage_fixed: true
    - run: bundle exec rubocop
      root: backend/
    - run: golangci-lint
      root: proxy/
    - script: verify.sh
      runner: bash
```

This configuration runs migrate jobs in a piped flow while other jobs execute in parallel.


================================================
FILE: docs/configuration/lefthook.md
================================================
---
title: "lefthook"
---

# `lefthook`

**Default:** `null`

::: callout tip New feature
Added in lefthook `1.10.5`
:::

Provide a full path to lefthook executable or a command to run lefthook. Bourne shell (`sh`) syntax is supported.

> **Important:** This option does not merge from `remotes` or `extends` for security reasons. But it gets merged from lefthook local config if specified.

There are three reasons you may want to specify `lefthook`:

1. You want to force using specific lefthook version from your dependencies (e.g. npm package)
1. You use PnP loader for your JS/TS project, and your `package.json` with lefthook dependency locates in a subfolder
1. You want to make sure you use concrete lefthook executable path and want to defined it in `lefthook-local.yml`

### Examples

#### Specify lefthook executable

```yml
# lefthook.yml

lefthook: /usr/bin/lefthook

pre-commit:
  jobs:
    - run: yarn lint
```

#### Specify a command to run lefthook

```yml
# lefthook.yml

lefthook: |
  cd project-with-lefthook
  pnpm lefthook

pre-commit:
  jobs:
    - run: yarn lint
      root: project-with-lefthook
```

#### Force using a version from Rubygems

```yml
# lefthook.yml

lefthook: bundle exec lefthook

pre-commit:
  jobs:
    - run: bundle exec rubocop -- {staged_files}
```

#### Enable debug logs

```yml
# lefthook-local.yml

lefthook: LEFTHOOK_VERBOSE=1 lefthook
```


================================================
FILE: docs/configuration/min_version.md
================================================
---
title: "min_version"
---

# `min_version`

If you want to specify a minimum version for lefthook binary (e.g. if you need some features older versions don't have) you can set this option.

#### Example

```yml
# lefthook.yml

min_version: 1.1.3
```


================================================
FILE: docs/configuration/name.md
================================================
---
title: "name"
---

# `name`

Name of a job. Will be printed in summary. If specified, the jobs can be merged with a jobs of the same name in a [local config](../examples/lefthook-local.md) or [extends](./extends.md).

### Example

```yml
# lefthook.yml

pre-commit:
  jobs:
    - name: lint and fix
      run: yarn run eslint --fix {staged_files}
```


================================================
FILE: docs/configuration/no_auto_install.md
================================================
---
title: "no_auto_install"
---

# `no_auto_install`

**Default: `false`**

Disable automatic installation and synchronization of git hooks when running lefthook. By default, lefthook automatically installs and updates hooks when you run `lefthook run` if the configuration has changed. Setting this to `true` disables that behavior.

This can also be controlled with the `--no-auto-install` option for the `lefthook run` command.

#### Example

```yml
# lefthook.yml

no_auto_install: true

pre-commit:
  commands:
    lint:
      run: npm run lint
```


================================================
FILE: docs/configuration/no_tty.md
================================================
---
title: "no_tty"
---

# `no_tty`

**Default: `false`**

Whether hide spinner and other interactive things. This can be also controlled with `--no-tty` option for `lefthook run` command.

#### Example

```yml
# lefthook.yml

no_tty: true
```


================================================
FILE: docs/configuration/only.md
================================================
---
title: "only"
---

# `only`

You can force a command, script, or the whole hook to execute only in certain conditions. This option acts like the opposite of [`skip`](./skip.md). It accepts the same values but skips execution only if the condition is not satisfied.

::: callout info Note
`skip` option takes precedence over `only` option, so if you have conflicting conditions the execution will be skipped.
:::

#### Example

Execute a hook only for `dev/*` branches.

```yml
# lefthook.yml

pre-commit:
  only:
    - ref: dev/*
  commands:
    lint:
      run: yarn lint
    test:
      run: yarn test
```

When rebasing execute quick linter but skip usual linter and tests.

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      skip: rebase
      run: yarn lint
    test:
      skip: rebase
      run: yarn test
    lint-on-rebase:
      only: rebase
      run: yarn lint-quickly
```


================================================
FILE: docs/configuration/output.md
================================================
---
title: "output"
---

# `output`

You can manage verbosity using the `output` config. You can specify what to print in your output by setting these values, which you need to have

Possible values are `meta,summary,success,failure,execution,execution_out,execution_info,skips`.
By default, all output values are enabled

You can also disable all output with setting `output: false`. In this case only errors will be printed.

#### Example

```yml
# lefthook.yml

output:
  - meta           # Print lefthook version
  - summary        # Print summary block (successful and failed steps)
  - empty_summary  # Print summary heading when there are no steps to run
  - success        # Print successful steps
  - failure        # Print failed steps printing
  - execution      # Print any execution logs
  - execution_out  # Print execution output
  - execution_info # Print `EXECUTE > ...` logging
  - skips          # Print "skip" (i.e. no files matched)
```

You can also *extend* this list with an environment variable `LEFTHOOK_OUTPUT`:

```bash
LEFTHOOK_OUTPUT="meta,success,summary" lefthook run pre-commit
```


================================================
FILE: docs/configuration/parallel.md
================================================
---
title: "parallel"
---

# `parallel`

**Default: `false`**

::: callout info Note
Lefthook runs commands and scripts **sequentially** by default
:::

Run commands and scripts concurrently.


================================================
FILE: docs/configuration/piped.md
================================================
---
title: "piped"
---

# `piped`

**Default: `false`**

::: callout info Note
Lefthook will return an error if both `piped: true` and `parallel: true` are set
:::

Stop running commands and scripts if one of them fail.

#### Example

```yml
# lefthook.yml

database:
  piped: true # Stop if one of the steps fail
  commands:
    1_create:
      run: rake db:create
    2_migrate:
      run: rake db:migrate
    3_seed:
      run: rake db:seed
```


================================================
FILE: docs/configuration/priority.md
================================================
---
title: "priority"
---

# `priority`

**Default: `0`**

::: callout info Note
This option makes sense only when `parallel: false` or `piped: true` is set.

Value `0` is considered an `+Infinity`, so commands or scripts with `priority: 0` or without this setting will be run at the very end.
:::

Set priority from 1 to +Infinity. This option can be used to configure the order of the sequential steps.

#### Example

```yml
# lefthook.yml

post-checkout:
  piped: true
  commands:
    db-create:
      priority: 1
      run: rails db:create
    db-migrate:
      priority: 2
      run: rails db:migrate
    db-seed:
      priority: 3
      run: rails db:seed

  scripts:
    "check-spelling.sh":
      runner: bash
      priority: 1
    "check-grammar.rb":
      runner: ruby
      priority: 2
```


================================================
FILE: docs/configuration/rc.md
================================================
---
title: "rc"
---

# `rc`

Provide an [**rc**](https://www.baeldung.com/linux/rc-files) file, which is actually a simple `sh` script. Currently it can be used to set ENV variables that are not accessible from non-shell programs.

#### Example

Use cases:

- You have a GUI program that runs git hooks (e.g., VSCode)
- You reference executables that are accessible only from a tweaked $PATH environment variable (e.g., when using rbenv or nvm, fnm)
- Or even if your GUI program cannot locate the `lefthook` executable :scream:
- Or if you want to use ENV variables that control the executables behavior in `lefthook.yml`

```bash
# An npm executable which is managed by nvm
$ which npm
/home/user/.nvm/versions/node/v15.14.0/bin/npm
```

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      run: npm run eslint {staged_files}
```

Provide a tweak to access `npm` executable the same way you do it in your ~/<shell>rc.

```yml
# lefthook-local.yml

# You can choose whatever name you want.
# You can share it between projects where you use lefthook.
# Make sure the path is absolute.
rc: ~/.lefthookrc
```

Or

```yml
# lefthook-local.yml

# If the path contains spaces, you need to quote it.
rc: '"${XDG_CONFIG_HOME:-$HOME/.config}/lefthookrc"'
```

In the rc file, export any new environment variables or modify existing ones.

```bash
# ~/.lefthookrc

# An nvm way
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# An fnm way
export FNM_DIR="$HOME/.fnm"
[ -s "$FNM_DIR/fnm.sh" ] && \. "$FNM_DIR/fnm.sh"

# Or maybe just
PATH=$PATH:$HOME/.nvm/versions/node/v15.14.0/bin
```

```bash
# Make sure you updated git hooks. This is important.
$ lefthook install -f
```

Now any program that runs your hooks will have a tweaked PATH environment variable and will be able to get `nvm` :wink:


================================================
FILE: docs/configuration/ref.md
================================================
---
title: "ref"
---

# `ref`

An optional *branch* or *tag* name.

::: callout info Note
If you initially had `ref` option, ran `lefthook install`, and then removed it, lefthook won't decide which branch/tag to use as a ref. So, if you added it once, please, use it always to avoid issues in local setups.
:::

See also [`refetch_frequency`](./refetch_frequency.md).

#### Example

```yml
# lefthook.yml

remotes:
  - git_url: git@github.com:evilmartians/lefthook
    ref: v1.0.0
```


================================================
FILE: docs/configuration/refetch.md
================================================
---
title: "refetch"
---

# `refetch`

**Default:** `false`

Force remote config refetching on every run. Lefthook will be refetching the specified remote every time it is called.

See [`refetch_frequency`](./refetch_frequency.md) for more flexible refetching options and additional considerations.

#### Example

```yml
# lefthook.yml

remotes:
  - git_url: https://github.com/evilmartians/lefthook
    refetch: true
```


================================================
FILE: docs/configuration/refetch_frequency.md
================================================
---
title: "refetch_frequency"
---

# `refetch_frequency`

**Default:** Not set

Specifies how frequently Lefthook should refetch the remote configuration. This can be set to `always`, `never` or a time duration like `24h`, `30m`, etc.

- When set to `always`, Lefthook will always refetch the remote configuration on each run.
- When set to a duration (e.g., `24h`), Lefthook will check the last fetch time and refetch the configuration only if the specified amount of time has passed.
- When set to `never` or not set, Lefthook will not fetch from remote.

It is recommended to configure remotes that point to mutable references
(including ones without a `ref`) to be refetched with some frequency appropriate for the project.

Failure to fetch does not cause an error, but just a warning message.
If a successfully fetched previous configuration exists, it will be used.
Otherwise, the remote will be ignored.

#### Example

```yml
# lefthook.yml

remotes:
  - git_url: https://github.com/evilmartians/lefthook
    refetch_frequency: 24h # Refetches once every 24 hours
```

> WARNING
> If `refetch` is set to `true`, it overrides any setting in `refetch_frequency`.


================================================
FILE: docs/configuration/remotes.md
================================================
---
title: "remotes"
---

# `remotes`

You can provide multiple remote configs if you want to share yours lefthook configurations across many projects. Lefthook will automatically download and merge configurations into your local `lefthook.yml`.

You can use [`extends`](./extends.md) but the paths must be relative to the remote repository root.

If you provide [`scripts`](./scripts.md) in a remote config file, the [scripts](./source_dir.md) folder must also be in the **root of the repository**.

**Note**

The configuration from `remotes` will be merged to the local config using the following priority:

1. Local main config (`lefthook.yml`)
1. Remote configs (`remotes`)
1. Local overrides (`lefthook-local.yml`)

This priority may be changed in the future. For simplicity, try to keep jobs in remote settings independent from any other steps.


================================================
FILE: docs/configuration/root.md
================================================
---
title: "root"
---

# `root`

You can change the CWD for the command you execute using `root` option.

This is useful when you execute some `npm` or `yarn` command but the `package.json` is in another directory.

For `pre-push` and `pre-commit` hooks and for the custom `files` command `root` option is used to filter file paths. If all files are filtered the command will be skipped.

#### Example

Format and stage files from a `client/` folder.

```bash
# Folders structure

$ tree .
.
├── client/
│   ├── package.json
│   ├── node_modules/
|   ├── ...
├── server/
|   ...
```

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      root: "client/"
      glob: "*.{js,ts}"
      run: yarn eslint --fix {staged_files} && git add {staged_files}
```

**When using `root:`**

Globs are still calculated from the actual root of the git repo, `root` is ignored.


================================================
FILE: docs/configuration/run.md
================================================
---
title: "run"
---

# `run`

This is a mandatory option for a command, which specifies the actual command to be run using the `sh` shell.

You can use files templates that will be substituted with the appropriate files on execution:

- `{files}` - custom [`files`](./files.md) command result.
- `{staged_files}` - staged files which you try to commit.
- `{push_files}` - files that are committed but not pushed.
- `{all_files}` - all files tracked by git.
- `{cmd}` - shorthand for the command from `lefthook.yml`.
- `{0}` - shorthand for the single space-joint string of git hook arguments.
- `{1}` - shorthand for the 1-st git hook argument (and so on for `{2}`, `{3}`, etc.)
- `{lefthook_job_name}` - current job/command/script name

::: callout info Note
Command line length has a limit on every system. If your list of files is quite long, lefthook splits your files list to fit in the limit and runs few commands sequentially.
:::

#### Example

Run `yarn lint` on `pre-commit` hook.

```yml
# lefthook.yml

pre-commit:
  commands:
    lint:
      run: yarn lint
```

#### `{files}` template

Run `go vet` only on files listed with `git ls-files -m` command with `.go` extension.

```yml
# lefthook.yml

pre-commit:
  commands:
    govet:
      files: git ls-files -m
      glob: "*.go"
      run: go vet -- {files}
```

#### `{staged_files}`

Run `yarn eslint` only on staged files with `.js`, `.ts`, `.jsx`, and `.tsx` extensions.

```yml
# lefthook.yml

pre-commit:
  commands:
    
Download .txt
gitextract_h_yok89w/

├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── codeql.yml
│       ├── gh-pages.yml
│       ├── lint.yml
│       ├── release.yml
│       └── test.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── .lefthook.yml
├── .tool-versions
├── .typos.toml
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── assets/
│   └── css/
│       └── lefthook.css
├── book.toml
├── cliff.toml
├── cmd/
│   ├── add-usage.txt
│   ├── add.go
│   ├── check_install.go
│   ├── commands.go
│   ├── commands_without_self_update.go
│   ├── dump.go
│   ├── install.go
│   ├── lefthook.go
│   ├── run.go
│   ├── self_update.go
│   ├── uninstall.go
│   ├── validate.go
│   └── version.go
├── codecov.yml
├── docmd.config.js
├── docs/
│   ├── configuration/
│   │   ├── Commands.md
│   │   ├── Hook.md
│   │   ├── README.md
│   │   ├── Scripts.md
│   │   ├── args.md
│   │   ├── assert_lefthook_installed.md
│   │   ├── colors.md
│   │   ├── configs.md
│   │   ├── env.md
│   │   ├── exclude.md
│   │   ├── exclude_tags.md
│   │   ├── extends.md
│   │   ├── fail_on_changes.md
│   │   ├── fail_on_changes_diff.md
│   │   ├── fail_text.md
│   │   ├── file_types.md
│   │   ├── files-global.md
│   │   ├── files.md
│   │   ├── follow.md
│   │   ├── git_url.md
│   │   ├── glob.md
│   │   ├── glob_matcher.md
│   │   ├── group.md
│   │   ├── install_non_git_hooks.md
│   │   ├── interactive.md
│   │   ├── jobs.md
│   │   ├── lefthook.md
│   │   ├── min_version.md
│   │   ├── name.md
│   │   ├── no_auto_install.md
│   │   ├── no_tty.md
│   │   ├── only.md
│   │   ├── output.md
│   │   ├── parallel.md
│   │   ├── piped.md
│   │   ├── priority.md
│   │   ├── rc.md
│   │   ├── ref.md
│   │   ├── refetch.md
│   │   ├── refetch_frequency.md
│   │   ├── remotes.md
│   │   ├── root.md
│   │   ├── run.md
│   │   ├── runner.md
│   │   ├── script.md
│   │   ├── setup.md
│   │   ├── skip.md
│   │   ├── skip_lfs.md
│   │   ├── source_dir.md
│   │   ├── source_dir_local.md
│   │   ├── stage_fixed.md
│   │   ├── tags.md
│   │   ├── templates.md
│   │   └── use_stdin.md
│   ├── configuration.md
│   ├── examples/
│   │   ├── commitlint.md
│   │   ├── filters.md
│   │   ├── lefthook-local.md
│   │   ├── remotes.md
│   │   ├── skip.md
│   │   ├── stage_fixed.md
│   │   └── wrap-commands.md
│   ├── index.md
│   ├── install.md
│   ├── installation/
│   │   ├── alpine.md
│   │   ├── arch.md
│   │   ├── deb.md
│   │   ├── devbox.md
│   │   ├── go.md
│   │   ├── homebrew.md
│   │   ├── manual.md
│   │   ├── mise.md
│   │   ├── node.md
│   │   ├── python.md
│   │   ├── rpm.md
│   │   ├── ruby.md
│   │   ├── scoop.md
│   │   ├── snap.md
│   │   ├── swift.md
│   │   └── winget.md
│   ├── misc/
│   │   └── contributors.md
│   ├── usage/
│   │   ├── commands/
│   │   │   ├── add.md
│   │   │   ├── check-install.md
│   │   │   ├── dump.md
│   │   │   ├── install.md
│   │   │   ├── run.md
│   │   │   ├── self-update.md
│   │   │   ├── uninstall.md
│   │   │   ├── validate.md
│   │   │   └── version.md
│   │   ├── envs/
│   │   │   ├── CI.md
│   │   │   ├── CLICOLOR_FORCE.md
│   │   │   ├── LEFTHOOK.md
│   │   │   ├── LEFTHOOK_BIN.md
│   │   │   ├── LEFTHOOK_CONFIG.md
│   │   │   ├── LEFTHOOK_EXCLUDE.md
│   │   │   ├── LEFTHOOK_OUTPUT.md
│   │   │   ├── LEFTHOOK_VERBOSE.md
│   │   │   └── NO_COLOR.md
│   │   └── features/
│   │       ├── git-args.md
│   │       ├── git-lfs.md
│   │       ├── interactive.md
│   │       ├── local.md
│   │       └── pass-stdin.md
│   └── usage.md
├── examples/
│   ├── commitlint/
│   │   ├── README.md
│   │   ├── commitlint.config.js
│   │   └── lefthook.yml
│   ├── complete/
│   │   └── lefthook.yml
│   ├── remote/
│   │   └── ping.yml
│   ├── verbose/
│   │   └── lefthook.yml
│   └── with_scripts/
│       └── lefthook.yml
├── gen/
│   └── jsonschema.go
├── go.mod
├── go.sum
├── integration_test.go
├── internal/
│   ├── command/
│   │   ├── add.go
│   │   ├── add_test.go
│   │   ├── check_install.go
│   │   ├── dump.go
│   │   ├── install.go
│   │   ├── install_test.go
│   │   ├── lefthook.go
│   │   ├── run.go
│   │   ├── run_test.go
│   │   ├── uninstall.go
│   │   ├── uninstall_test.go
│   │   └── validate.go
│   ├── config/
│   │   ├── available_hooks.go
│   │   ├── command.go
│   │   ├── command_executor.go
│   │   ├── command_test.go
│   │   ├── config.go
│   │   ├── files.go
│   │   ├── hook.go
│   │   ├── job.go
│   │   ├── jsonc_parser.go
│   │   ├── jsonschema.go
│   │   ├── jsonschema.json
│   │   ├── load.go
│   │   ├── load_test.go
│   │   ├── remote.go
│   │   ├── script.go
│   │   ├── script_test.go
│   │   ├── skip_checker.go
│   │   └── skip_checker_test.go
│   ├── git/
│   │   ├── command_executor.go
│   │   ├── command_executor_test.go
│   │   ├── lfs.go
│   │   ├── remote.go
│   │   ├── repository.go
│   │   ├── repository_test.go
│   │   └── state.go
│   ├── log/
│   │   ├── builder.go
│   │   ├── execution.go
│   │   ├── log.go
│   │   ├── log_test.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── setup.go
│   │   └── skip.go
│   ├── run/
│   │   ├── controller/
│   │   │   ├── command/
│   │   │   │   ├── build.go
│   │   │   │   ├── build_command.go
│   │   │   │   ├── build_script.go
│   │   │   │   ├── replacer/
│   │   │   │   │   ├── replacer.go
│   │   │   │   │   └── replacer_test.go
│   │   │   │   └── skip_error.go
│   │   │   ├── controller.go
│   │   │   ├── controller_test.go
│   │   │   ├── exec/
│   │   │   │   ├── exec_unix.go
│   │   │   │   ├── exec_windows.go
│   │   │   │   └── executor.go
│   │   │   ├── filter/
│   │   │   │   ├── detect_text.go
│   │   │   │   ├── detect_text_test.go
│   │   │   │   ├── filter.go
│   │   │   │   └── filter_test.go
│   │   │   ├── guard.go
│   │   │   ├── guard_test.go
│   │   │   ├── job.go
│   │   │   ├── lfs.go
│   │   │   ├── run.go
│   │   │   ├── scope.go
│   │   │   ├── scope_test.go
│   │   │   ├── setup.go
│   │   │   └── utils/
│   │   │       ├── cached_reader.go
│   │   │       ├── cached_reader_test.go
│   │   │       ├── firstNonBlank.go
│   │   │       └── intersect.go
│   │   ├── result/
│   │   │   ├── result.go
│   │   │   └── result_test.go
│   │   └── run.go
│   ├── system/
│   │   ├── command.go
│   │   ├── limits.go
│   │   ├── null_reader.go
│   │   ├── null_reader_test.go
│   │   ├── sh_unix.go
│   │   └── sh_windows.go
│   ├── templates/
│   │   ├── config.tmpl
│   │   ├── hook.tmpl
│   │   └── templates.go
│   ├── updater/
│   │   ├── updater.go
│   │   └── updater_test.go
│   └── version/
│       ├── version.go
│       └── version_test.go
├── main.go
├── packaging/
│   ├── .gitignore
│   ├── registries/
│   │   ├── aur/
│   │   │   ├── lefthook/
│   │   │   │   └── PKGBUILD
│   │   │   └── lefthook-bin/
│   │   │       └── PKGBUILD
│   │   ├── npm/
│   │   │   ├── lefthook/
│   │   │   │   ├── bin/
│   │   │   │   │   └── index.js
│   │   │   │   ├── get-exe.js
│   │   │   │   ├── package.json
│   │   │   │   └── postinstall.js
│   │   │   ├── lefthook-darwin-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-darwin-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-freebsd-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-freebsd-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-linux-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-linux-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-openbsd-arm64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-openbsd-x64/
│   │   │   │   └── package.json
│   │   │   ├── lefthook-windows-arm64/
│   │   │   │   └── package.json
│   │   │   └── lefthook-windows-x64/
│   │   │       └── package.json
│   │   ├── npm-bundled/
│   │   │   ├── bin/
│   │   │   │   └── index.js
│   │   │   ├── get-exe.js
│   │   │   ├── package.json
│   │   │   └── postinstall.js
│   │   ├── npm-installer/
│   │   │   ├── bin/
│   │   │   │   └── index.js
│   │   │   ├── install.js
│   │   │   └── package.json
│   │   ├── pypi/
│   │   │   ├── LICENSE
│   │   │   ├── README.md
│   │   │   ├── hatch_build.py
│   │   │   ├── lefthook/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── __main__.py
│   │   │   │   ├── bin/
│   │   │   │   │   └── .keep
│   │   │   │   └── main.py
│   │   │   └── pyproject.toml
│   │   └── rubygems/
│   │       ├── Gemfile
│   │       ├── README.md
│   │       ├── Rakefile
│   │       ├── bin/
│   │       │   └── lefthook
│   │       ├── lefthook.gemspec
│   │       ├── lib/
│   │       │   └── lefthook.rb
│   │       └── libexec/
│   │           └── .keep
│   └── scripts/
│       ├── META6.json
│       ├── clean.raku
│       ├── lib/
│       │   ├── Constants.rakumod
│       │   ├── Packager.rakumod
│       │   ├── Registries/
│       │   │   ├── AUR/
│       │   │   │   └── Publishing.rakumod
│       │   │   ├── AUR-Bin.rakumod
│       │   │   ├── AUR.rakumod
│       │   │   ├── NPM.rakumod
│       │   │   ├── PyPI.rakumod
│       │   │   └── RubyGems.rakumod
│       │   ├── Registry.rakumod
│       │   ├── System.rakumod
│       │   └── SystemAPI.rakumod
│       ├── prepare.raku
│       ├── publish.raku
│       ├── set-version.raku
│       └── t/
│           ├── 01-system.rakutest
│           ├── 02-npm.rakutest
│           ├── 03-rubygems.rakutest
│           ├── 04-pypi.rakutest
│           └── lib/
│               ├── FakeSystem.rakumod
│               └── TestRegistry.rakumod
├── schema.json
├── tea.yaml
└── tests/
    ├── helpers/
    │   ├── cmdtest/
    │   │   ├── cmdtest.go
    │   │   ├── dumb.go
    │   │   ├── ordered.go
    │   │   ├── ordered_test.go
    │   │   ├── tracking.go
    │   │   └── tracking_test.go
    │   ├── configtest/
    │   │   ├── config.go
    │   │   └── config_test.go
    │   └── gittest/
    │       ├── gittest.go
    │       └── gittest_test.go
    └── integration/
        ├── add.txt
        ├── check_install.txt
        ├── cli_run_only.txt
        ├── dump.txt
        ├── env_overwrite_issue_1137.txt
        ├── exclude.txt
        ├── exclude_arg.txt
        ├── fail_on_changes.txt
        ├── fail_on_changes_issue_1125.txt
        ├── fail_on_changes_recover_previous_change.txt
        ├── fail_text.txt
        ├── files_override.txt
        ├── files_skip_if_empty.txt
        ├── filter_by_file_type.txt
        ├── filter_by_mime_type.txt
        ├── group_envs.txt
        ├── hide_unstaged.txt
        ├── install.txt
        ├── install_specific.txt
        ├── job_fail_text.txt
        ├── job_filter_by_file_type.txt
        ├── job_merging.txt
        ├── job_stage_fixed.txt
        ├── lefthook_job_name_issue_1345.txt
        ├── lefthook_option.txt
        ├── many_extends_levels.txt
        ├── min_version.txt
        ├── pre-commit_issue_919.txt
        ├── remotes.txt
        ├── run_deleted_only.txt
        ├── run_interrupt.txt
        ├── run_json.txt
        ├── run_jsonc.txt
        ├── run_non_existing.txt
        ├── run_script.txt
        ├── run_script_with_args.txt
        ├── run_toml.txt
        ├── run_yml.txt
        ├── setup_instructions.txt
        ├── sh_syntax_in_files.txt
        ├── skip_group_issue_1083.txt
        ├── skip_merge_commit.txt
        ├── skip_run.txt
        ├── stage_fixed.txt
        ├── stage_fixed_505.txt
        ├── templates.txt
        ├── timeout.txt
        ├── timeout_success.txt
        ├── uninstall.txt
        ├── validate.txt
        ├── validate_fail.txt
        └── version.txt
Download .txt
SYMBOL INDEX (612 symbols across 113 files)

FILE: cmd/add.go
  function add (line 15) | func add() *cli.Command {

FILE: cmd/check_install.go
  function checkInstall (line 11) | func checkInstall() *cli.Command {

FILE: cmd/dump.go
  function dump (line 14) | func dump() *cli.Command {

FILE: cmd/install.go
  function install (line 11) | func install() *cli.Command {

FILE: cmd/lefthook.go
  function Lefthook (line 9) | func Lefthook() *cli.Command {

FILE: cmd/run.go
  function run (line 12) | func run() *cli.Command {

FILE: cmd/self_update.go
  function selfUpdate (line 16) | func selfUpdate() *cli.Command {

FILE: cmd/uninstall.go
  function uninstall (line 11) | func uninstall() *cli.Command {

FILE: cmd/validate.go
  function validate (line 11) | func validate() *cli.Command {

FILE: cmd/version.go
  function version (line 13) | func version() *cli.Command {

FILE: gen/jsonschema.go
  function main (line 16) | func main() {

FILE: integration_test.go
  function TestLefthook (line 14) | func TestLefthook(t *testing.T) {

FILE: internal/command/add.go
  constant defaultDirMode (line 12) | defaultDirMode = 0o755
  type AddArgs (line 14) | type AddArgs struct
  method Add (line 21) | func (l *Lefthook) Add(_ctx context.Context, args AddArgs) error {
  method getSourceDirs (line 57) | func (l *Lefthook) getSourceDirs() (global, local string) {

FILE: internal/command/add_test.go
  function TestLefthookAdd (line 13) | func TestLefthookAdd(t *testing.T) {

FILE: internal/command/check_install.go
  type installationStatus (line 8) | type installationStatus
  constant installed (line 11) | installed installationStatus = iota
  constant notInstalled (line 12) | notInstalled
  method CheckInstall (line 15) | func (l *Lefthook) CheckInstall(_ctx context.Context) error {
  method checkInstall (line 31) | func (l *Lefthook) checkInstall() (installationStatus, error) {

FILE: internal/command/dump.go
  type DumpArgs (line 11) | type DumpArgs struct
  method Dump (line 15) | func (l *Lefthook) Dump(_ctx context.Context, args DumpArgs) error {

FILE: internal/command/install.go
  constant configFileMode (line 26) | configFileMode   = 0o666
  constant checksumFileMode (line 27) | checksumFileMode = 0o644
  constant hooksDirMode (line 28) | hooksDirMode     = 0o755
  constant timestampBase (line 29) | timestampBase    = 10
  constant timestampBitsize (line 30) | timestampBitsize = 64
  type InstallArgs (line 38) | type InstallArgs struct
  method Install (line 43) | func (l *Lefthook) Install(ctx context.Context, args InstallArgs, hooks ...
  method readOrCreateConfig (line 76) | func (l *Lefthook) readOrCreateConfig() (*config.Config, error) {
  method configExists (line 89) | func (l *Lefthook) configExists(path string) bool {
  method findMainConfig (line 94) | func (l *Lefthook) findMainConfig(path string) (string, error) {
  method createConfig (line 118) | func (l *Lefthook) createConfig(path string) error {
  method syncHooks (line 131) | func (l *Lefthook) syncHooks(cfg *config.Config, fetchRemotes bool) (*co...
  method shouldRefetch (line 202) | func (l *Lefthook) shouldRefetch(remote *config.Remote) bool {
  method findAvailableRemoteRef (line 236) | func (l *Lefthook) findAvailableRemoteRef(url string) (string, error) {
  method createHooksIfNeeded (line 257) | func (l *Lefthook) createHooksIfNeeded(cfg *config.Config, hooks []strin...
  function collectAllJobRoots (line 351) | func collectAllJobRoots(roots map[string]struct{}, jobs []*config.Job) {
  method checkHooksSynchronized (line 368) | func (l *Lefthook) checkHooksSynchronized(cfg *config.Config) (bool, []s...
  method configLastUpdateTimestamp (line 429) | func (l *Lefthook) configLastUpdateTimestamp() (int64, error) {
  method addChecksumFile (line 443) | func (l *Lefthook) addChecksumFile(checksum string, hooks []string) error {
  method checksumFilePath (line 454) | func (l *Lefthook) checksumFilePath() string {
  method ensureHooksDirExists (line 458) | func (l *Lefthook) ensureHooksDirExists() error {
  method getHooksPathConfig (line 471) | func (l *Lefthook) getHooksPathConfig() (local, global string) {
  method ensureHooksPathUnset (line 481) | func (l *Lefthook) ensureHooksPathUnset(force, resetHooksPath bool) error {
  function formatHooksPathError (line 518) | func formatHooksPathError(local, global string) error {
  method unsetHooksPathConfig (line 550) | func (l *Lefthook) unsetHooksPathConfig(local, global string) error {

FILE: internal/command/install_test.go
  function TestLefthookInstall (line 17) | func TestLefthookInstall(t *testing.T) {
  function Test_syncHooks (line 382) | func Test_syncHooks(t *testing.T) {
  function TestShouldRefetch (line 620) | func TestShouldRefetch(t *testing.T) {
  function TestLefthookInstallWithCoreHooksPath (line 722) | func TestLefthookInstallWithCoreHooksPath(t *testing.T) {

FILE: internal/command/lefthook.go
  constant EnvVerbose (line 26) | EnvVerbose             = "LEFTHOOK_VERBOSE"
  constant envNoColor (line 27) | envNoColor             = "NO_COLOR"
  constant envClicolorForce (line 28) | envClicolorForce       = "CLICOLOR_FORCE"
  constant envClicolor (line 29) | envClicolor            = "CLICOLOR"
  constant hookFileMode (line 30) | hookFileMode           = 0o755
  constant oldHookPostfix (line 31) | oldHookPostfix         = ".old"
  constant hookContentFingerprint (line 32) | hookContentFingerprint = "LEFTHOOK"
  type Lefthook (line 35) | type Lefthook struct
    method LoadConfig (line 78) | func (l *Lefthook) LoadConfig() (*config.Config, error) {
    method reloadConfig (line 87) | func (l *Lefthook) reloadConfig(cfg *config.Config) (*config.Config, e...
    method isLefthookFile (line 109) | func (l *Lefthook) isLefthookFile(path string) bool {
    method cleanHook (line 135) | func (l *Lefthook) cleanHook(hook string, force bool) error {
    method addHook (line 173) | func (l *Lefthook) addHook(hook string, args templates.Args) error {
  function NewLefthook (line 42) | func NewLefthook(verbose bool, colors string) (*Lefthook, error) {
  function isEnvEnabled (line 180) | func isEnvEnabled(name string) bool {
  function ShellCompleteHookNames (line 189) | func ShellCompleteHookNames() {
  function ShellCompleteFlags (line 205) | func ShellCompleteFlags(cmd *cli.Command) {

FILE: internal/command/run.go
  constant envEnabled (line 22) | envEnabled = "LEFTHOOK"
  constant envOutput (line 23) | envOutput  = "LEFTHOOK_OUTPUT"
  type RunArgs (line 28) | type RunArgs struct
  method Run (line 48) | func (l *Lefthook) Run(ctx context.Context, args RunArgs) error {
  function resolveHook (line 153) | func resolveHook(cfg *config.Config, hookName string) (*config.Hook, err...
  function getFiles (line 170) | func getFiles(repo *git.Repository, args RunArgs) ([]string, error) {
  function getSourceDirs (line 188) | func getSourceDirs(repo *git.Repository, cfg *config.Config) []string {
  function shouldFailOnChanges (line 214) | func shouldFailOnChanges(fromArg *bool, fromHook string) (bool, error) {
  function shouldFailOnChangesDiff (line 235) | func shouldFailOnChangesDiff(fromArg *bool, fromHook *bool) bool {
  function runHook (line 247) | func runHook(ctx context.Context, hook *config.Hook, repo *git.Repositor...
  function printSummary (line 276) | func printSummary(
  function logResults (line 309) | func logResults(indent int, results []result.Result) {
  function parseFilesFromString (line 340) | func parseFilesFromString(paths string) []string {
  function checkVersion (line 353) | func checkVersion(minVersion string) error {

FILE: internal/command/run_test.go
  function TestRun (line 15) | func TestRun(t *testing.T) {

FILE: internal/command/uninstall.go
  type UninstallArgs (line 15) | type UninstallArgs struct
  method Uninstall (line 19) | func (l *Lefthook) Uninstall(_ctx context.Context, args UninstallArgs) e...
  method deleteHooks (line 47) | func (l *Lefthook) deleteHooks(force bool) error {
  method removeFile (line 83) | func (l *Lefthook) removeFile(glob string) {

FILE: internal/command/uninstall_test.go
  function TestLefthookUninstall (line 14) | func TestLefthookUninstall(t *testing.T) {

FILE: internal/command/validate.go
  type ValidateArgs (line 14) | type ValidateArgs struct
  method Validate (line 18) | func (l *Lefthook) Validate(_ctx context.Context, args ValidateArgs) err...
  function logValidationErrors (line 50) | func logValidationErrors(indent int, details jsonschema.List) {
  function logDetail (line 66) | func logDetail(indent int, details jsonschema.List) {

FILE: internal/config/available_hooks.go
  constant ChecksumFileName (line 4) | ChecksumFileName = "lefthook.checksum"
  constant GhostHookName (line 7) | GhostHookName = "prepare-commit-msg"
  function HookUsesStagedFiles (line 42) | func HookUsesStagedFiles(hook string) bool {
  function HookUsesPushFiles (line 46) | func HookUsesPushFiles(hook string) bool {
  function KnownHook (line 50) | func KnownHook(hook string) bool {

FILE: internal/config/command.go
  type Command (line 13) | type Command struct
  function CommandsToJobs (line 36) | func CommandsToJobs(commands map[string]*Command) []*Job {

FILE: internal/config/command_executor.go
  type commandExecutor (line 12) | type commandExecutor struct
    method execute (line 17) | func (c *commandExecutor) execute(commandLine string) bool {

FILE: internal/config/command_test.go
  function TestCommandsToJobs (line 10) | func TestCommandsToJobs(t *testing.T) {
  function TestCommandsToJobsWithTimeout (line 44) | func TestCommandsToJobsWithTimeout(t *testing.T) {

FILE: internal/config/config.go
  type DumpFormat (line 17) | type DumpFormat
  constant YAMLFormat (line 20) | YAMLFormat DumpFormat = iota
  constant TOMLFormat (line 21) | TOMLFormat
  constant JSONFormat (line 22) | JSONFormat
  constant JSONCompactFormat (line 23) | JSONCompactFormat
  constant yamlIndent (line 25) | yamlIndent = 2
  type Config (line 28) | type Config struct
    method Md5 (line 64) | func (c *Config) Md5() (checksum string, err error) {
    method Dump (line 82) | func (c *Config) Dump(format DumpFormat, out io.Writer) error {
  type dumper (line 116) | type dumper interface
  type yamlDumper (line 120) | type yamlDumper struct
    method Dump (line 122) | func (yamlDumper) Dump(input map[string]any, out io.Writer) error {
  type tomlDumper (line 130) | type tomlDumper struct
    method Dump (line 132) | func (tomlDumper) Dump(input map[string]any, out io.Writer) error {
  type jsonDumper (line 142) | type jsonDumper struct
    method Dump (line 146) | func (j jsonDumper) Dump(input map[string]any, out io.Writer) error {

FILE: internal/config/files.go
  constant SubFiles (line 6) | SubFiles       string = "{files}"
  constant SubAllFiles (line 7) | SubAllFiles    string = "{all_files}"
  constant SubStagedFiles (line 8) | SubStagedFiles string = "{staged_files}"
  constant SubPushFiles (line 9) | SubPushFiles   string = "{push_files}"
  function IsRunFilesCompatible (line 12) | func IsRunFilesCompatible(run string) bool {

FILE: internal/config/hook.go
  constant CMD (line 3) | CMD = "{cmd}"
  type Hook (line 5) | type Hook struct
  type SetupInstruction (line 25) | type SetupInstruction struct

FILE: internal/config/job.go
  type Job (line 5) | type Job struct
    method PrintableName (line 40) | func (job *Job) PrintableName(id string) string {
  type Group (line 33) | type Group struct

FILE: internal/config/jsonc_parser.go
  type JSONC (line 9) | type JSONC struct
    method Unmarshal (line 15) | func (p *JSONC) Unmarshal(b []byte) (map[string]any, error) {
    method Marshal (line 24) | func (p *JSONC) Marshal(o map[string]any) ([]byte, error) {
  function jsoncParser (line 11) | func jsoncParser() *JSONC {

FILE: internal/config/load.go
  constant DefaultConfigName (line 26) | DefaultConfigName     = "lefthook.yml"
  constant DefaultSourceDir (line 27) | DefaultSourceDir      = ".lefthook"
  constant DefaultSourceDirLocal (line 28) | DefaultSourceDirLocal = ".lefthook-local"
  type ConfigNotFoundError (line 54) | type ConfigNotFoundError struct
    method Error (line 58) | func (err ConfigNotFoundError) Error() string {
  function loadConfig (line 63) | func loadConfig(k *koanf.Koanf, filesystem afero.Fs, path string) error {
  function loadFirst (line 74) | func loadFirst(k *koanf.Koanf, filesystem afero.Fs, root string, names [...
  function loadFirstMain (line 90) | func loadFirstMain(k *koanf.Koanf, filesystem afero.Fs, root string) err...
  function loadMain (line 113) | func loadMain(filesystem afero.Fs, root string) (*koanf.Koanf, error) {
  function LoadSecondary (line 139) | func LoadSecondary(main *koanf.Koanf, filesystem afero.Fs, repo *git.Rep...
  function LoadKoanf (line 188) | func LoadKoanf(filesystem afero.Fs, repo *git.Repository) (*koanf.Koanf,...
  function Load (line 205) | func Load(filesystem afero.Fs, repo *git.Repository) (*Config, error) {
  function Unmarshal (line 214) | func Unmarshal(main *koanf.Koanf, secondary *koanf.Koanf) (*Config, erro...
  function loadRemotes (line 230) | func loadRemotes(k *koanf.Koanf, filesystem afero.Fs, repo *git.Reposito...
  function extend (line 276) | func extend(k *koanf.Koanf, filesystem afero.Fs, root string, extends []...
  function extendRecursive (line 282) | func extendRecursive(k *koanf.Koanf, filesystem afero.Fs, root string, e...
  function unmarshalConfigs (line 321) | func unmarshalConfigs(main, secondary *koanf.Koanf, c *Config) error {
  function addHook (line 363) | func addHook(name string, main, secondary *koanf.Koanf, c *Config) error {
  type iofs (line 466) | type iofs struct
    method Open (line 474) | func (iofs iofs) Open(name string) (fs.File, error) {
  function newIOFS (line 470) | func newIOFS(filesystem afero.Fs) iofs {
  type koanfProvider (line 483) | type koanfProvider struct
    method Read (line 487) | func (k koanfProvider) Read() (map[string]any, error) {
    method ReadBytes (line 491) | func (k koanfProvider) ReadBytes() ([]byte, error) {
  function mergeHooks (line 499) | func mergeHooks(src, dest map[string]any) error {
  function mergeJobsSlice (line 602) | func mergeJobsSlice(src, dest []any) []any {

FILE: internal/config/load_test.go
  function TestLoad (line 16) | func TestLoad(t *testing.T) {

FILE: internal/config/remote.go
  type Remote (line 3) | type Remote struct
    method Configured (line 15) | func (r *Remote) Configured() bool {

FILE: internal/config/script.go
  type Script (line 12) | type Script struct
  function ScriptsToJobs (line 29) | func ScriptsToJobs(scripts map[string]*Script) []*Job {
  function parseNum (line 87) | func parseNum(str string) int {

FILE: internal/config/script_test.go
  function TestScriptsToJobs (line 10) | func TestScriptsToJobs(t *testing.T) {
  function TestScriptsToJobsWithTimeout (line 44) | func TestScriptsToJobsWithTimeout(t *testing.T) {

FILE: internal/config/skip_checker.go
  type skipChecker (line 11) | type skipChecker struct
    method Check (line 20) | func (sc *skipChecker) Check(state func() git.State, skip any, only an...
    method matches (line 38) | func (sc *skipChecker) matches(state func() git.State, value any) bool {
    method matchesSlices (line 50) | func (sc *skipChecker) matchesSlices(gitState func() git.State, slice ...
    method matchesRef (line 71) | func (sc *skipChecker) matchesRef(state func() git.State, typedState m...
    method matchesCommands (line 87) | func (sc *skipChecker) matchesCommands(typedState map[string]any) bool {
  function NewSkipChecker (line 15) | func NewSkipChecker(cmd system.Command) *skipChecker {

FILE: internal/config/skip_checker_test.go
  type mockCmd (line 12) | type mockCmd struct
    method WithoutEnvs (line 14) | func (mc mockCmd) WithoutEnvs(...string) system.Command {
    method Run (line 18) | func (mc mockCmd) Run(cmd []string, _root string, _in io.Reader, _out ...
  function TestSkipChecker_Check (line 26) | func TestSkipChecker_Check(t *testing.T) {

FILE: internal/git/command_executor.go
  type CommandExecutor (line 15) | type CommandExecutor struct
    method WithoutEnvs (line 41) | func (c CommandExecutor) WithoutEnvs(envs ...string) CommandExecutor {
    method OnlyDebugLogs (line 46) | func (c CommandExecutor) OnlyDebugLogs() CommandExecutor {
    method WithoutTrim (line 51) | func (c CommandExecutor) WithoutTrim() CommandExecutor {
    method Cmd (line 57) | func (c CommandExecutor) Cmd(cmd []string) (string, error) {
    method BatchedCmd (line 71) | func (c CommandExecutor) BatchedCmd(cmd []string, args []string) (stri...
    method CmdLines (line 88) | func (c CommandExecutor) CmdLines(cmd []string) ([]string, error) {
    method CmdLinesWithinFolder (line 98) | func (c CommandExecutor) CmdLinesWithinFolder(cmd []string, folder str...
    method execute (line 112) | func (c CommandExecutor) execute(cmd []string, root string) (string, e...
  function NewExecutor (line 33) | func NewExecutor(cmd system.Command) *CommandExecutor {
  function batchByLength (line 145) | func batchByLength(s []string, length int) [][]string {

FILE: internal/git/command_executor_test.go
  type mockCmd (line 13) | type mockCmd struct
    method WithoutEnvs (line 15) | func (m mockCmd) WithoutEnvs(...string) system.Command { return mockCm...
    method Run (line 16) | func (m mockCmd) Run(cmd []string, root string, in io.Reader, out io.W...
  function TestBatchedCmd (line 25) | func TestBatchedCmd(t *testing.T) {

FILE: internal/git/lfs.go
  constant LFSRequiredFile (line 8) | LFSRequiredFile = ".lfs-required"
  constant LFSConfigFile (line 9) | LFSConfigFile   = ".lfsconfig"
  function IsLFSAvailable (line 20) | func IsLFSAvailable() bool {
  function IsLFSHook (line 27) | func IsLFSHook(hookName string) bool {

FILE: internal/git/remote.go
  constant remotesFolder (line 13) | remotesFolder     = "lefthook-remotes"
  constant remotesFolderMode (line 14) | remotesFolderMode = 0o755
  method RemoteFolder (line 19) | func (r *Repository) RemoteFolder(url string, ref string) string {
  method RemotesFolder (line 27) | func (r *Repository) RemotesFolder() string {
  method SyncRemote (line 34) | func (r *Repository) SyncRemote(url, ref string, force bool) error {
  method updateRemote (line 65) | func (r *Repository) updateRemote(path, ref string) error {
  method cloneRemote (line 96) | func (r *Repository) cloneRemote(dest, directoryName, url, ref string) e...
  function RemoteDirectoryName (line 132) | func RemoteDirectoryName(url, ref string) string {

FILE: internal/git/repository.go
  constant minGitVersion (line 22) | minGitVersion     = "2.31.0"
  constant stashMessage (line 23) | stashMessage      = "lefthook auto backup"
  constant unstagedPatchName (line 24) | unstagedPatchName = "lefthook-unstaged.patch"
  constant infoDirMode (line 25) | infoDirMode       = 0o775
  constant emptyTreeSHA (line 28) | emptyTreeSHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
  type Repository (line 57) | type Repository struct
    method Precompute (line 124) | func (r *Repository) Precompute() func() {
    method Setup (line 146) | func (r *Repository) Setup() {
    method StagedFiles (line 167) | func (r *Repository) StagedFiles() ([]string, error) {
    method StagedFilesWithDeleted (line 172) | func (r *Repository) StagedFilesWithDeleted() ([]string, error) {
    method AllFiles (line 177) | func (r *Repository) AllFiles() ([]string, error) {
    method PushFiles (line 182) | func (r *Repository) PushFiles() ([]string, error) {
    method PartiallyStagedFiles (line 222) | func (r *Repository) PartiallyStagedFiles() ([]string, error) {
    method SaveUnstaged (line 239) | func (r *Repository) SaveUnstaged(files []string) error {
    method RevertUnstagedChanges (line 260) | func (r *Repository) RevertUnstagedChanges(files []string) error {
    method RestoreUnstaged (line 266) | func (r *Repository) RestoreUnstaged() error {
    method StashUnstaged (line 307) | func (r *Repository) StashUnstaged() error {
    method DropUnstagedStash (line 329) | func (r *Repository) DropUnstagedStash() error {
    method AddFiles (line 363) | func (r *Repository) AddFiles(files []string) error {
    method Changeset (line 375) | func (r *Repository) Changeset() (map[string]string, error) {
    method PrintDiff (line 414) | func (r *Repository) PrintDiff(files []string) {
    method statusShort (line 432) | func (r *Repository) statusShort() ([]string, error) {
    method parseStatusShort (line 438) | func (r *Repository) parseStatusShort(lines []string, cb func(path str...
    method FindAllFiles (line 461) | func (r *Repository) FindAllFiles(command []string, folder string) ([]...
    method FindExistingFiles (line 471) | func (r *Repository) FindExistingFiles(command []string, folder string...
    method extractFiles (line 480) | func (r *Repository) extractFiles(lines []string, checkExistence bool)...
    method isFile (line 511) | func (r *Repository) isFile(path string) (bool, error) {
    method readOriginHead (line 526) | func (r *Repository) readOriginHead() string {
  function NewRepository (line 74) | func NewRepository(fs afero.Fs, git *CommandExecutor) (*Repository, erro...

FILE: internal/git/repository_test.go
  function TestPartiallyStagedFiles (line 11) | func TestPartiallyStagedFiles(t *testing.T) {
  function TestChangeset (line 57) | func TestChangeset(t *testing.T) {

FILE: internal/git/state.go
  type State (line 13) | type State struct
  constant Nil (line 18) | Nil         string = ""
  constant Merge (line 19) | Merge       string = "merge"
  constant MergeCommit (line 20) | MergeCommit string = "merge-commit"
  constant Rebase (line 21) | Rebase      string = "rebase"
  method State (line 29) | func (r *Repository) State() State {
  method state (line 33) | func (r *Repository) state() State {
  method branch (line 67) | func (r *Repository) branch() string {
  method inMergeState (line 99) | func (r *Repository) inMergeState() bool {
  method inRebaseState (line 106) | func (r *Repository) inRebaseState() bool {
  method inMergeCommitState (line 116) | func (r *Repository) inMergeCommitState() bool {

FILE: internal/log/builder.go
  type builder (line 8) | type builder interface
  type dummyBuilder (line 14) | type dummyBuilder struct
    method Add (line 80) | func (d dummyBuilder) Add(_ string, _ any) builder { return d }
    method Log (line 81) | func (dummyBuilder) Log()                          {}
    method String (line 82) | func (dummyBuilder) String() string                { return "" }
  type logBuilder (line 16) | type logBuilder struct
    method Add (line 34) | func (b *logBuilder) Add(prefix string, data any) builder {
    method Log (line 63) | func (b *logBuilder) Log() {
    method String (line 76) | func (b *logBuilder) String() string {
  function Builder (line 22) | func Builder(level Level, prefix string) builder {

FILE: internal/log/execution.go
  constant execLogPadding (line 10) | execLogPadding = 2
  function Execution (line 12) | func Execution(name string, err error, out io.Reader) {

FILE: internal/log/log.go
  type Level (line 36) | type Level
  constant ErrorLevel (line 39) | ErrorLevel Level = iota
  constant WarnLevel (line 40) | WarnLevel
  constant InfoLevel (line 41) | InfoLevel
  constant DebugLevel (line 42) | DebugLevel
  constant spinnerCharSet (line 44) | spinnerCharSet     = 14
  constant spinnerRefreshRate (line 45) | spinnerRefreshRate = 100 * time.Millisecond
  constant spinnerText (line 46) | spinnerText        = " waiting"
  constant ColorAuto (line 48) | ColorAuto = iota
  constant ColorOn (line 49) | ColorOn
  constant ColorOff (line 50) | ColorOff
  type StyleLogger (line 53) | type StyleLogger struct
    method WithLeftBorder (line 103) | func (s StyleLogger) WithLeftBorder(border lipgloss.Border, color lipg...
    method WithPadding (line 109) | func (s StyleLogger) WithPadding(m int) StyleLogger {
    method Info (line 115) | func (s StyleLogger) Info(str string) {
  type Logger (line 57) | type Logger struct
    method SetLevel (line 384) | func (l *Logger) SetLevel(level Level) {
    method SetOutput (line 390) | func (l *Logger) SetOutput(out io.Writer) {
    method Info (line 396) | func (l *Logger) Info(args ...any) {
    method Debug (line 400) | func (l *Logger) Debug(args ...string) {
    method Error (line 410) | func (l *Logger) Error(args ...string) {
    method Warn (line 420) | func (l *Logger) Warn(args ...string) {
    method Infof (line 430) | func (l *Logger) Infof(format string, args ...any) {
    method Debugf (line 434) | func (l *Logger) Debugf(format string, args ...any) {
    method Errorf (line 438) | func (l *Logger) Errorf(format string, args ...any) {
    method Warnf (line 442) | func (l *Logger) Warnf(format string, args ...any) {
    method Log (line 446) | func (l *Logger) Log(level Level, args ...any) {
    method SetName (line 460) | func (l *Logger) SetName(name string) {
    method UnsetName (line 473) | func (l *Logger) UnsetName(name string) {
    method Logf (line 497) | func (l *Logger) Logf(level Level, format string, args ...any) {
    method Println (line 503) | func (l *Logger) Println(args ...any) {
    method Printf (line 515) | func (l *Logger) Printf(format string, args ...any) {
    method IsLevelEnabled (line 527) | func (l *Logger) IsLevelEnabled(level Level) bool {
    method formatSpinnerSuffix (line 532) | func (l *Logger) formatSpinnerSuffix(names []string) string {
  function New (line 67) | func New() *Logger {
  function Colors (line 81) | func Colors() int {
  function Colorized (line 85) | func Colorized() bool {
  function StartSpinner (line 89) | func StartSpinner() {
  function StopSpinner (line 93) | func StopSpinner() {
  function Styled (line 97) | func Styled() StyleLogger {
  function Debug (line 124) | func Debug(args ...any) {
  function Debugf (line 129) | func Debugf(format string, args ...any) {
  function Info (line 133) | func Info(args ...any) {
  function InfoPad (line 137) | func InfoPad(s string) {
  function Infof (line 147) | func Infof(format string, args ...any) {
  function Error (line 151) | func Error(args ...any) {
  function Errorf (line 156) | func Errorf(format string, args ...any) {
  function Warn (line 160) | func Warn(args ...any) {
  function Warnf (line 165) | func Warnf(format string, args ...any) {
  function Println (line 169) | func Println(args ...any) {
  function Printf (line 173) | func Printf(format string, args ...any) {
  function SetLevel (line 177) | func SetLevel(level Level) {
  function SetColors (line 181) | func SetColors(colors any) {
  function setColor (line 234) | func setColor(colorCode any, adaptiveColor *lipgloss.TerminalColor) {
  function Cyan (line 257) | func Cyan(s string) string {
  function Green (line 261) | func Green(s string) string {
  function Red (line 265) | func Red(s string) string {
  function Yellow (line 269) | func Yellow(s string) string {
  function Gray (line 273) | func Gray(s string) string {
  function Bold (line 277) | func Bold(s string) string {
  function LogMeta (line 285) | func LogMeta(hookName string) {
  function Success (line 297) | func Success(indent int, name string, duration time.Duration) {
  function Failure (line 310) | func Failure(indent int, name, failText string, duration time.Duration) {
  function box (line 328) | func box(left, right string) {
  function Separate (line 346) | func Separate(s string) {
  function color (line 362) | func color(clr lipgloss.TerminalColor) lipgloss.Style {
  function SetOutput (line 366) | func SetOutput(out io.Writer) {
  function ParseLevel (line 370) | func ParseLevel(lvl string) (Level, error) {
  function SetName (line 452) | func SetName(name string) {
  function UnsetName (line 456) | func UnsetName(name string) {
  function terminalWidth (line 564) | func terminalWidth() int {
  function formatWithPartialNames (line 580) | func formatWithPartialNames(names []string, availableWidth int) string {
  function pluralize (line 637) | func pluralize(count int) string {

FILE: internal/log/log_test.go
  constant testConcurrentGoroutines (line 17) | testConcurrentGoroutines   = 10
  constant testOperationsPerGoroutine (line 18) | testOperationsPerGoroutine = 50
  function TestLogger_SetName (line 21) | func TestLogger_SetName(t *testing.T) {
  function TestLogger_UnsetName (line 91) | func TestLogger_UnsetName(t *testing.T) {
  function TestLogger_SetName_UnsetName_Integration (line 173) | func TestLogger_SetName_UnsetName_Integration(t *testing.T) {
  function TestLogger_LongHookNames (line 212) | func TestLogger_LongHookNames(t *testing.T) {
  function TestLogger_ConcurrentAccess (line 243) | func TestLogger_ConcurrentAccess(t *testing.T) {
  function TestLogger_SpinnerActiveHandling (line 277) | func TestLogger_SpinnerActiveHandling(t *testing.T) {
  function TestGlobalSetNameUnsetName (line 302) | func TestGlobalSetNameUnsetName(t *testing.T) {
  function createTestLogger (line 332) | func createTestLogger() *Logger {
  function TestLogger_FormatSpinnerSuffix (line 347) | func TestLogger_FormatSpinnerSuffix(t *testing.T) {
  function TestLogger_FormatWithPartialNames (line 428) | func TestLogger_FormatWithPartialNames(t *testing.T) {
  function TestPluralize (line 469) | func TestPluralize(t *testing.T) {
  function TestLogger_TerminalWidthIntegration (line 488) | func TestLogger_TerminalWidthIntegration(t *testing.T) {
  function createTestLoggerWithWidth (line 524) | func createTestLoggerWithWidth(width int) *Logger {

FILE: internal/log/settings.go
  constant meta (line 8) | meta = 1 << iota
  constant success (line 9) | success
  constant failure (line 10) | failure
  constant summary (line 11) | summary
  constant skips (line 12) | skips
  constant execution (line 13) | execution
  constant executionOutput (line 14) | executionOutput
  constant executionInfo (line 15) | executionInfo
  constant emptySummary (line 16) | emptySummary
  constant setup (line 17) | setup
  constant disableAll (line 20) | disableAll = 0
  type LogSettings (line 22) | type LogSettings struct
    method Apply (line 40) | func (s *LogSettings) Apply(enableTags string, enable any) {
    method enable (line 76) | func (s *LogSettings) enable(setting string) {
    method enableAll (line 101) | func (s *LogSettings) enableAll() {
    method disableAll (line 105) | func (s *LogSettings) disableAll() {
    method isEnable (line 110) | func (s LogSettings) isEnable(option int16) bool {
    method LogSuccess (line 114) | func (s LogSettings) LogSuccess() bool {
    method LogFailure (line 118) | func (s LogSettings) LogFailure() bool {
    method LogSummary (line 122) | func (s LogSettings) LogSummary() bool {
    method LogMeta (line 126) | func (s LogSettings) LogMeta() bool {
    method LogExecution (line 130) | func (s LogSettings) LogExecution() bool {
    method LogExecutionOutput (line 134) | func (s LogSettings) LogExecutionOutput() bool {
    method LogExecutionInfo (line 138) | func (s LogSettings) LogExecutionInfo() bool {
    method LogSkips (line 142) | func (s LogSettings) LogSkips() bool {
    method LogEmptySummary (line 146) | func (s LogSettings) LogEmptySummary() bool {
    method LogSetup (line 150) | func (s LogSettings) LogSetup() bool {
  function InitSettings (line 28) | func InitSettings() {
  function NewSettings (line 32) | func NewSettings() LogSettings {
  function ApplySettings (line 36) | func ApplySettings(enableTags string, enable any) {

FILE: internal/log/settings_test.go
  function TestSetting (line 8) | func TestSetting(t *testing.T) {

FILE: internal/log/setup.go
  function LogSetup (line 10) | func LogSetup(r io.Reader) {

FILE: internal/log/skip.go
  constant skipLogPadding (line 7) | skipLogPadding = 2
  function Skip (line 9) | func Skip(name, reason string) {

FILE: internal/run/controller/command/build.go
  type JobParams (line 8) | type JobParams struct
    method validateCommand (line 55) | func (p *JobParams) validateCommand() error {
    method validateScript (line 63) | func (p *JobParams) validateScript() error {
  type BuilderOptions (line 24) | type BuilderOptions struct
  type Builder (line 34) | type Builder struct
    method BuildCommands (line 47) | func (b *Builder) BuildCommands(params *JobParams) ([]string, []string...
  function NewBuilder (line 39) | func NewBuilder(repo *git.Repository, opts BuilderOptions) *Builder {

FILE: internal/run/controller/command/build_command.go
  method buildCommand (line 14) | func (b *Builder) buildCommand(params *JobParams) ([]string, []string, e...
  method buildReplacer (line 87) | func (b *Builder) buildReplacer(params *JobParams) replacer.Replacer {
  method buildFilter (line 103) | func (b *Builder) buildFilter(params *JobParams) *filter.Filter {

FILE: internal/run/controller/command/build_script.go
  constant executableFileMode (line 17) | executableFileMode os.FileMode = 0o751
  constant executableMask (line 18) | executableMask     os.FileMode = 0o111
  type scriptNotExistsError (line 21) | type scriptNotExistsError struct
    method Error (line 25) | func (s scriptNotExistsError) Error() string {
  method buildScript (line 29) | func (b *Builder) buildScript(params *JobParams) ([]string, []string, er...

FILE: internal/run/controller/command/replacer/replacer.go
  type entry (line 20) | type entry struct
  type Replacer (line 25) | type Replacer struct
    method AddTemplates (line 62) | func (r Replacer) AddTemplates(templates map[string]string) Replacer {
    method AddGitArgs (line 74) | func (r Replacer) AddGitArgs(args []string) Replacer {
    method Discover (line 102) | func (r Replacer) Discover(source string, filter *filter.Filter) error {
    method HasEmpty (line 131) | func (r Replacer) HasEmpty() bool {
    method Cached (line 141) | func (r Replacer) Cached(key string) bool {
    method Empty (line 147) | func (r Replacer) Empty(key string) bool {
    method Files (line 156) | func (r Replacer) Files(template string, filter *filter.Filter) ([]str...
    method ReplaceAndSplit (line 175) | func (r Replacer) ReplaceAndSplit(command string, maxlen int) ([]strin...
  function New (line 31) | func New(
  function NewMocked (line 87) | func NewMocked(files []string) Replacer {
  function escapeFiles (line 228) | func escapeFiles(files []string) []string {
  function getNChars (line 243) | func getNChars(s []string, n int) ([]string, []string) {
  function replaceQuoted (line 265) | func replaceQuoted(source, substitution string, files []string) string {

FILE: internal/run/controller/command/replacer/replacer_test.go
  function Test_getNChars (line 13) | func Test_getNChars(t *testing.T) {
  function Test_ReplaceAndSplit (line 71) | func Test_ReplaceAndSplit(t *testing.T) {
  function Test_ReplaceAndSplit_CustomTemplates (line 210) | func Test_ReplaceAndSplit_CustomTemplates(t *testing.T) {
  function Test_replaceQuoted (line 253) | func Test_replaceQuoted(t *testing.T) {

FILE: internal/run/controller/command/skip_error.go
  type SkipError (line 4) | type SkipError struct
    method Error (line 8) | func (r SkipError) Error() string {

FILE: internal/run/controller/controller.go
  type Controller (line 21) | type Controller struct
    method RunHook (line 61) | func (c *Controller) RunHook(ctx context.Context, opts Options, hook *...
    method concurrently (line 97) | func (c *Controller) concurrently(ctx context.Context, scope *scope, j...
    method sequentially (line 122) | func (c *Controller) sequentially(ctx context.Context, scope *scope, j...
  type Options (line 28) | type Options struct
  function NewController (line 45) | func NewController(repo *git.Repository) *Controller {

FILE: internal/run/controller/controller_test.go
  type executor (line 25) | type executor struct
    method Execute (line 36) | func (e executor) Execute(_ctx context.Context, opts exec.Options, _in...
  function succeeded (line 28) | func succeeded(name string) result.Result {
  function failed (line 32) | func failed(name, failText string) result.Result {
  function TestRunAll (line 78) | func TestRunAll(t *testing.T) {

FILE: internal/run/controller/exec/exec_unix.go
  type CommandExecutor (line 19) | type CommandExecutor struct
    method Execute (line 29) | func (e CommandExecutor) Execute(ctx context.Context, opts Options, in...
    method execute (line 79) | func (e CommandExecutor) execute(ctx context.Context, cmdstr string, a...
  type executeArgs (line 21) | type executeArgs struct

FILE: internal/run/controller/exec/exec_windows.go
  type CommandExecutor (line 19) | type CommandExecutor struct
    method Execute (line 27) | func (e CommandExecutor) Execute(ctx context.Context, opts Options, in...
    method execute (line 69) | func (e CommandExecutor) execute(ctx context.Context, cmdstr string, a...
  type executeArgs (line 20) | type executeArgs struct

FILE: internal/run/controller/exec/executor.go
  type Options (line 9) | type Options struct
  type Executor (line 18) | type Executor interface

FILE: internal/run/controller/filter/detect_text.go
  function hasBOM (line 18) | func hasBOM(content []byte) bool {
  function detectText (line 31) | func detectText(bytes []byte) bool {

FILE: internal/run/controller/filter/detect_text_test.go
  function TestDetectText (line 8) | func TestDetectText(t *testing.T) {

FILE: internal/run/controller/filter/filter.go
  type fileTypeFilter (line 17) | type fileTypeFilter struct
  constant typeExecutable (line 23) | typeExecutable int = 1 << iota
  constant typeNotExecutable (line 24) | typeNotExecutable
  constant typeSymlink (line 25) | typeSymlink
  constant typeNotSymlink (line 26) | typeNotSymlink
  constant typeText (line 27) | typeText
  constant typeBinary (line 28) | typeBinary
  constant detectTypes (line 30) | detectTypes    = typeText | typeBinary
  constant detectBufSize (line 31) | detectBufSize  = 1024
  constant executableMask (line 32) | executableMask = 0o111
  type Params (line 35) | type Params struct
  type Filter (line 43) | type Filter struct
    method Apply (line 53) | func (f *Filter) Apply(files []string) []string {
  function New (line 49) | func New(fs afero.Fs, params Params) *Filter {
  function byGlob (line 72) | func byGlob(vs []string, matchers []string, globMatcher string) []string {
  function matchFiles (line 95) | func matchFiles(vs []string, matcher string, globMatcher string) []string {
  function matchFilesDoublestar (line 108) | func matchFilesDoublestar(vs []string, lowerMatcher string) []string {
  function matchFilesGobwas (line 119) | func matchFilesGobwas(vs []string, lowerMatcher string) []string {
  function byExclude (line 130) | func byExclude(vs []string, exclude []string, globMatcher string) []stri...
  function byExcludeDoublestar (line 141) | func byExcludeDoublestar(vs []string, exclude []string) []string {
  function byExcludeGobwas (line 151) | func byExcludeGobwas(vs []string, exclude []string) []string {
  function matchesAnyDoublestar (line 166) | func matchesAnyDoublestar(path string, patterns []string) bool {
  function matchesAnyGobwas (line 176) | func matchesAnyGobwas(path string, globs []glob.Glob) bool {
  function byRoot (line 185) | func byRoot(vs []string, matcher string) []string {
  function byType (line 199) | func byType(fs afero.Fs, vs []string, types []string) []string {
  function parseFileTypeFilter (line 279) | func parseFileTypeFilter(types []string) fileTypeFilter {
  function checkIsText (line 306) | func checkIsText(fs afero.Fs, filepath string) bool {

FILE: internal/run/controller/filter/filter_test.go
  function slicesEqual (line 8) | func slicesEqual(a, b []string) bool {
  function TestByGlob (line 28) | func TestByGlob(t *testing.T) {
  function TestByGlobDoublestar (line 74) | func TestByGlobDoublestar(t *testing.T) {
  function TestByExclude (line 120) | func TestByExclude(t *testing.T) {
  function TestByExcludeDoublestar (line 148) | func TestByExcludeDoublestar(t *testing.T) {
  function TestByRoot (line 188) | func TestByRoot(t *testing.T) {

FILE: internal/run/controller/guard.go
  type FailOnChangesError (line 12) | type FailOnChangesError struct
    method Error (line 16) | func (e *FailOnChangesError) Error() string {
  type guard (line 20) | type guard struct
    method wrap (line 37) | func (g *guard) wrap(fn func()) error {
    method withHiddenUnstagedChanges (line 51) | func (g *guard) withHiddenUnstagedChanges(fn func() error) error {
    method withFailOnChanges (line 110) | func (g *guard) withFailOnChanges(fn func()) error {
    method printDiff (line 135) | func (g *guard) printDiff(changesetBefore, changesetAfter map[string]s...
    method getChangedFiles (line 145) | func (g *guard) getChangedFiles(changesetBefore, changesetAfter map[st...
  function newGuard (line 28) | func newGuard(repo *git.Repository, stashUnstagedChanges bool, failOnCha...

FILE: internal/run/controller/guard_test.go
  function Test_guard_wrap (line 14) | func Test_guard_wrap(t *testing.T) {

FILE: internal/run/controller/job.go
  constant invalidJobError (line 23) | invalidJobError = "either `run`,`script`, or `group` must be provided fo...
  constant emptyGroupError (line 24) | emptyGroupError = "group must have `jobs`"
  method runJob (line 27) | func (c *Controller) runJob(ctx context.Context, scope *scope, id string...
  method runSingleJob (line 83) | func (c *Controller) runSingleJob(ctx context.Context, scope *scope, id ...
  method addStagedFiles (line 186) | func (c *Controller) addStagedFiles(files []string) {
  method skipReason (line 192) | func (c *Controller) skipReason(scope *scope, job *config.Job, name stri...

FILE: internal/run/controller/lfs.go
  method runLFSHook (line 16) | func (c *Controller) runLFSHook(ctx context.Context, hookName string, ar...

FILE: internal/run/controller/run.go
  method run (line 14) | func (c *Controller) run(ctx context.Context, name string, follow bool, ...

FILE: internal/run/controller/scope.go
  type scope (line 11) | type scope struct
    method extend (line 51) | func (s *scope) extend(job *config.Job) *scope {
  function newScope (line 27) | func newScope(hook *config.Hook, opts Options) *scope {

FILE: internal/run/controller/scope_test.go
  function Test_newScope (line 13) | func Test_newScope(t *testing.T) {
  function TestScope_extend (line 65) | func TestScope_extend(t *testing.T) {

FILE: internal/run/controller/setup.go
  method setup (line 14) | func (c *Controller) setup(

FILE: internal/run/controller/utils/cached_reader.go
  type CachedReader (line 12) | type CachedReader struct
    method Read (line 27) | func (r *CachedReader) Read(p []byte) (int, error) {
  function NewCachedReader (line 19) | func NewCachedReader(in io.Reader) *CachedReader {

FILE: internal/run/controller/utils/cached_reader_test.go
  function TestCachedReader (line 9) | func TestCachedReader(t *testing.T) {

FILE: internal/run/controller/utils/firstNonBlank.go
  function FirstNonBlank (line 4) | func FirstNonBlank(args ...string) string {

FILE: internal/run/controller/utils/intersect.go
  function Intersect (line 4) | func Intersect[K comparable](a, b []K) bool {

FILE: internal/run/result/result.go
  type status (line 5) | type status
  constant success (line 8) | success status = iota
  constant failure (line 9) | failure
  constant skip (line 10) | skip
  type Result (line 14) | type Result struct
    method Success (line 22) | func (r Result) Success() bool {
    method Failure (line 26) | func (r Result) Failure() bool {
    method Text (line 30) | func (r Result) Text() string {
  function Skip (line 34) | func Skip(name string) Result {
  function Success (line 38) | func Success(name string, duration time.Duration) Result {
  function Failure (line 42) | func Failure(name, text string, duration time.Duration) Result {
  function Group (line 46) | func Group(name string, results []Result) Result {

FILE: internal/run/result/result_test.go
  function TestGroup (line 11) | func TestGroup(t *testing.T) {

FILE: internal/run/run.go
  function Run (line 21) | func Run(

FILE: internal/system/command.go
  type osCmd (line 12) | type osCmd struct
    method WithoutEnvs (line 27) | func (c osCmd) WithoutEnvs(envs ...string) Command {
    method Run (line 32) | func (c osCmd) Run(command []string, root string, in io.Reader, out io...
    method RunWithContext (line 38) | func (c osCmd) RunWithContext(
  type Command (line 18) | type Command interface
  type CommandWithContext (line 23) | type CommandWithContext interface

FILE: internal/system/limits.go
  constant maxCommandLengthDarwin (line 9) | maxCommandLengthDarwin  = 260000
  constant maxCommandLengthWindows (line 10) | maxCommandLengthWindows = 7000
  constant maxCommandLengthLinux (line 11) | maxCommandLengthLinux   = 130000
  function MaxCmdLen (line 14) | func MaxCmdLen() int {

FILE: internal/system/null_reader.go
  type nullReader (line 6) | type nullReader struct
    method Read (line 11) | func (nullReader) Read(b []byte) (int, error) {

FILE: internal/system/null_reader_test.go
  function TestNullReader (line 9) | func TestNullReader(t *testing.T) {

FILE: internal/system/sh_unix.go
  function Sh (line 6) | func Sh() (string, error) {

FILE: internal/system/sh_windows.go
  constant sh (line 14) | sh            = "sh"
  constant defaultShPath (line 15) | defaultShPath = `C:\Program Files\Git\bin\sh.exe`
  function Sh (line 42) | func Sh() (string, error) {

FILE: internal/templates/templates.go
  constant checksumFormat (line 13) | checksumFormat = "%s %d %s\n"
  type Args (line 18) | type Args struct
  type hookTmplData (line 25) | type hookTmplData struct
  function Hook (line 35) | func Hook(hookName string, args Args) []byte {
  function Config (line 58) | func Config() []byte {
  function Checksum (line 67) | func Checksum(checksum string, timestamp int64, hooks []string) []byte {
  function getExtension (line 71) | func getExtension() string {

FILE: internal/updater/updater.go
  constant timeout (line 29) | timeout                       = 120 * time.Second
  constant latestReleaseURL (line 30) | latestReleaseURL              = "https://api.github.com/repos/evilmartia...
  constant checksumsFilename (line 31) | checksumsFilename             = "lefthook_checksums.txt"
  constant checksumFields (line 32) | checksumFields                = 2
  constant modExecutable (line 33) | modExecutable     os.FileMode = 0o755
  type release (line 56) | type release struct
  type asset (line 61) | type asset struct
  type Options (line 66) | type Options struct
  type Updater (line 72) | type Updater struct
    method SelfUpdate (line 84) | func (u *Updater) SelfUpdate(ctx context.Context, opts Options) error {
    method fetchLatestRelease (line 204) | func (u *Updater) fetchLatestRelease(ctx context.Context) (*release, e...
    method download (line 235) | func (u *Updater) download(ctx context.Context, name, fileURL, checksu...
  function New (line 77) | func New() *Updater {

FILE: internal/updater/updater_test.go
  function TestUpdater_SelfUpdate (line 18) | func TestUpdater_SelfUpdate(t *testing.T) {

FILE: internal/version/version.go
  constant version (line 9) | version = "2.1.4"
  function Version (line 19) | func Version(verbose bool) string {
  function Check (line 27) | func Check(wanted, given string) error {

FILE: internal/version/version_test.go
  function TestCheck (line 10) | func TestCheck(t *testing.T) {
  function TestVersion (line 62) | func TestVersion(t *testing.T) {

FILE: main.go
  function main (line 11) | func main() {

FILE: packaging/registries/npm-bundled/get-exe.js
  function getExePath (line 3) | function getExePath() {

FILE: packaging/registries/npm-installer/install.js
  function install (line 8) | async function install() {
  function getDownloadURL (line 29) | function getDownloadURL() {
  function downloadBinary (line 57) | async function downloadBinary(url, dest) {

FILE: packaging/registries/npm/lefthook/get-exe.js
  function getExePath (line 3) | function getExePath() {

FILE: packaging/registries/npm/lefthook/postinstall.js
  function install (line 4) | function install() {

FILE: packaging/registries/pypi/hatch_build.py
  function normalize_platform (line 40) | def normalize_platform(value: str) -> str:
  function normalize_arch (line 46) | def normalize_arch(value: str) -> str:
  function get_platform_info (line 52) | def get_platform_info():
  class CustomBuildHook (line 69) | class CustomBuildHook(BuildHookInterface):
    method __init__ (line 72) | def __init__(self, *args, **kwargs) -> None:
    method initialize (line 80) | def initialize(self, version, build_data):
    method finalize (line 101) | def finalize(self, version, build_data, artifact_path) -> None:
    method _prune_binaries (line 105) | def _prune_binaries(self):
    method _restore_binaries (line 139) | def _restore_binaries(self):

FILE: packaging/registries/pypi/lefthook/main.py
  function main (line 12) | def main():

FILE: tests/helpers/cmdtest/cmdtest.go
  function NewOrdered (line 9) | func NewOrdered(t testing.TB, outs []Out) *OrderedCmd {
  function NewTracking (line 14) | func NewTracking(cb func(string, string, io.Writer) error) *TrackingCmd {
  function NewDumb (line 22) | func NewDumb() *DumbCmd {

FILE: tests/helpers/cmdtest/dumb.go
  type DumbCmd (line 9) | type DumbCmd struct
    method WithoutEnvs (line 12) | func (c *DumbCmd) WithoutEnvs(_ ...string) system.Command {
    method Run (line 17) | func (c *DumbCmd) Run(_ []string, _ string, _ io.Reader, _ io.Writer, ...

FILE: tests/helpers/cmdtest/ordered.go
  type Out (line 11) | type Out struct
  type OrderedCmd (line 17) | type OrderedCmd struct
    method WithoutEnvs (line 24) | func (c *OrderedCmd) WithoutEnvs(envs ...string) system.Command {
    method Run (line 29) | func (c *OrderedCmd) Run(command []string, root string, in io.Reader, ...

FILE: tests/helpers/cmdtest/ordered_test.go
  function TestOrderedCmd (line 12) | func TestOrderedCmd(t *testing.T) {

FILE: tests/helpers/cmdtest/tracking.go
  type TrackingCmd (line 11) | type TrackingCmd struct
    method WithoutEnvs (line 17) | func (c *TrackingCmd) WithoutEnvs(envs ...string) system.Command {
    method Run (line 22) | func (c *TrackingCmd) Run(command []string, root string, in io.Reader,...
    method RunWithContext (line 33) | func (c *TrackingCmd) RunWithContext(_ context.Context, command []stri...
    method Reset (line 37) | func (c *TrackingCmd) Reset() {

FILE: tests/helpers/cmdtest/tracking_test.go
  function TestTrackingCmd (line 12) | func TestTrackingCmd(t *testing.T) {

FILE: tests/helpers/configtest/config.go
  function ParseHook (line 13) | func ParseHook(str string) *config.Hook {
  function ParseJob (line 23) | func ParseJob(str string) *config.Job {
  function stripPadding (line 32) | func stripPadding(str string) []byte {

FILE: tests/helpers/configtest/config_test.go
  function TestParseHook (line 12) | func TestParseHook(t *testing.T) {
  function TestParseJob (line 60) | func TestParseJob(t *testing.T) {

FILE: tests/helpers/gittest/gittest.go
  type RepositoryBuilder (line 12) | type RepositoryBuilder struct
    method Root (line 22) | func (b *RepositoryBuilder) Root(root string) *RepositoryBuilder {
    method Cmd (line 27) | func (b *RepositoryBuilder) Cmd(cmd system.Command) *RepositoryBuilder {
    method Fs (line 32) | func (b *RepositoryBuilder) Fs(fs afero.Fs) *RepositoryBuilder {
    method Build (line 37) | func (b *RepositoryBuilder) Build() *git.Repository {
  function NewRepositoryBuilder (line 18) | func NewRepositoryBuilder() *RepositoryBuilder {
  function GitPath (line 48) | func GitPath(root string) string {

FILE: tests/helpers/gittest/gittest_test.go
  function TestBuilder (line 14) | func TestBuilder(t *testing.T) {
  function TestGitPath (line 28) | func TestGitPath(t *testing.T) {
Condensed preview — 372 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (807K chars).
[
  {
    "path": ".github/CODEOWNERS",
    "chars": 10,
    "preview": "* @mrexox\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 495,
    "preview": "---\nname: 🐞 Report a bug\nabout: Found something broken? Let us know! If it's not yet reproducible, please `Ask a questio"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 522,
    "preview": "---\nblank_issues_enabled: false\n\ncontact_links:\n  - name: 💡 Discuss an idea\n    url: https://github.com/evilmartians/lef"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 270,
    "preview": "---\nname: ⭐ Feature request\nabout: Want something to be implemented in `lefthook`? Create a feature request! If you are "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 143,
    "preview": "Closes # (issue)\n\n### Context\n\n<!-- Brief description of what problem PR is solving -->\n\n### Changes\n\n<!-- Summary for c"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 272,
    "preview": "---\nversion: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    target-branch: \"dependencies\"\n    schedule"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "chars": 2341,
    "preview": "name: \"CodeQL\"\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    # The branches below must be a subset of the "
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 584,
    "preview": "name: Publish Docs\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\njobs:\n  gh-pages:\n    runs-on: ubuntu-late"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 418,
    "preview": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n\nname: Lint\njobs:\n  golangci:\n    name: golangci-lint\n    runs-"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 7291,
    "preview": "name: release\n\non:\n  push:\n    tags:\n      - \"*\"\n\npermissions:\n  attestations: write\n  contents: write\n  id-token: write"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 3461,
    "preview": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n\nname: Test\njobs:\n  unit:\n    strategy:\n      matrix:\n        o"
  },
  {
    "path": ".gitignore",
    "chars": 80,
    "preview": "/lefthook\n/lefthook-local.yml\n/bin/\n/dist/\n/book/\n/site/\n/vscode/\n/.idea/\n\ntmp/\n"
  },
  {
    "path": ".golangci.yml",
    "chars": 1573,
    "preview": "version: \"2\"\n\nlinters:\n  default: none\n  enable:\n    - asasalint\n    - asciicheck\n    - bidichk\n    - bodyclose\n    - co"
  },
  {
    "path": ".goreleaser.yml",
    "chars": 4216,
    "preview": "version: 2\nproject_name: lefthook\nbefore:\n  hooks:\n    - go generate ./...\nbuilds:\n  # Builds the binaries without `left"
  },
  {
    "path": ".lefthook.yml",
    "chars": 1286,
    "preview": "assert_lefthook_installed: true\nskip_lfs: true\n\noutput:\n  - meta\n  - summary\n  - jobs\n\npre-commit:\n  parallel: true\n  se"
  },
  {
    "path": ".tool-versions",
    "chars": 21,
    "preview": "golangci-lint 2.10.1\n"
  },
  {
    "path": ".typos.toml",
    "chars": 77,
    "preview": "[default.extend-identifiers]\n\"PnP\" = \"PnP\"\n[default.extend-words]\nslq = \"slq\""
  },
  {
    "path": "AGENTS.md",
    "chars": 1714,
    "preview": "# AGENTS.md\n\nLefthook is a CLI-first Git hooks manager. Contributions must be predictable,\nbackwards-compatible, and dep"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 78184,
    "preview": "# Change log\n\n## 2.1.4 (2026-03-12)\n\n- pkg: fix scripts ([#1348](https://github.com/evilmartians/lefthook/pull/1348)) by"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 255,
    "preview": "# Contributing\n\nFirst off, thanks for taking the time to contribute! Feel free to make Pull Request with your changes.\n\n"
  },
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "\nThe MIT License (MIT)\n\nCopyright (c) 2019 Arkweid\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "Makefile",
    "chars": 2070,
    "preview": "COMMIT_HASH = $(shell git rev-parse HEAD)\n\n.PHONY: build\nbuild:\n\tgo build -ldflags \"-s -w -X github.com/evilmartians/lef"
  },
  {
    "path": "README.md",
    "chars": 8458,
    "preview": "![Build Status](https://github.com/evilmartians/lefthook/actions/workflows/test.yml/badge.svg?branch=master)\n[![codecov]"
  },
  {
    "path": "SECURITY.md",
    "chars": 865,
    "preview": "# Security Policy\n\n## Supported Versions\n\nLatest major version of Lefthook is being supported with security updates.\n\n| "
  },
  {
    "path": "assets/css/lefthook.css",
    "chars": 357,
    "preview": ":root {\n  --link-color: #ff1e1e;\n  --font-family-mono: \"Martian Mono\", SFMono-Regular, Consolas, \"Liberation Mono\", Menl"
  },
  {
    "path": "book.toml",
    "chars": 262,
    "preview": "[book]\nauthors = [\"Evil Martians\"]\nlanguage = \"en\"\nmultilingual = false\nsrc = \"docs/mdbook\"\ntitle = \"Lefthook Documentat"
  },
  {
    "path": "cliff.toml",
    "chars": 2062,
    "preview": "# https://git-cliff.org/docs/configuration\n\n[changelog]\nheader = \"# Change log\\n\\n\"\nbody = \"\"\"\n{% if version %}\\\n    ## "
  },
  {
    "path": "cmd/add-usage.txt",
    "chars": 549,
    "preview": "lefthook add pre-commit\n\nThis command will try to build the following structure in repository:\n├───.git\n│   └───hooks\n│ "
  },
  {
    "path": "cmd/add.go",
    "chars": 1169,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t_ \"embed\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal"
  },
  {
    "path": "cmd/check_install.go",
    "chars": 842,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/command\"\n)"
  },
  {
    "path": "cmd/commands.go",
    "chars": 232,
    "preview": "//go:build !no_self_update && !jsonschema\n\npackage cmd\n\nimport \"github.com/urfave/cli/v3\"\n\nvar commands = []*cli.Command"
  },
  {
    "path": "cmd/commands_without_self_update.go",
    "chars": 234,
    "preview": "//go:build no_self_update && !jsonschema\n\npackage cmd\n\nimport \"github.com/urfave/cli/v3\"\n\nvar commands = []*cli.Command{"
  },
  {
    "path": "cmd/dump.go",
    "chars": 1096,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/"
  },
  {
    "path": "cmd/install.go",
    "chars": 1271,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/command\"\n)"
  },
  {
    "path": "cmd/lefthook.go",
    "chars": 617,
    "preview": "package cmd\n\nimport (\n\t\"github.com/urfave/cli/v3\"\n\n\tver \"github.com/evilmartians/lefthook/v2/internal/version\"\n)\n\nfunc L"
  },
  {
    "path": "cmd/run.go",
    "chars": 3400,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/"
  },
  {
    "path": "cmd/self_update.go",
    "chars": 1544,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefth"
  },
  {
    "path": "cmd/uninstall.go",
    "chars": 1012,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/command\"\n)"
  },
  {
    "path": "cmd/validate.go",
    "chars": 717,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/command\"\n)"
  },
  {
    "path": "cmd/version.go",
    "chars": 823,
    "preview": "package cmd\n\nimport (\n\t\"context\"\n\n\t\"github.com/urfave/cli/v3\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/command\"\n\t"
  },
  {
    "path": "codecov.yml",
    "chars": 15,
    "preview": "comment: false\n"
  },
  {
    "path": "docmd.config.js",
    "chars": 16115,
    "preview": "module.exports = {\n  siteTitle: \"Lefthook\",\n  siteUrl: \"https://lefthook.dev\",\n  logo: {\n    light: \"/assets/lefthook.pn"
  },
  {
    "path": "docs/configuration/Commands.md",
    "chars": 671,
    "preview": "---\ntitle: \"commands\"\n---\n\n# `commands`\n\nCommands to be executed for the hook. Each command has a name and associated ru"
  },
  {
    "path": "docs/configuration/Hook.md",
    "chars": 375,
    "preview": "---\ntitle: \"Hook\"\n---\n\n# Git hook\n\nContains settings for the git hook (commands, scripts, skip rules, etc.). You can spe"
  },
  {
    "path": "docs/configuration/README.md",
    "chars": 4045,
    "preview": "## Config file name\n\nLefthook supports the following file names for the main config:\n\n| Format | File name |\n|-------|--"
  },
  {
    "path": "docs/configuration/Scripts.md",
    "chars": 1150,
    "preview": "---\ntitle: \"Scripts\"\n---\n\n# Scripts\n\nScripts are stored under `<source_dir>/<hook-name>/` folder. These scripts are your"
  },
  {
    "path": "docs/configuration/args.md",
    "chars": 811,
    "preview": "---\ntitle: \"args\"\n---\n\n# `args`\n\n::: callout tip New feature\nAdded in lefthook `2.0.5`\n:::\n\nSometimes you want to pass a"
  },
  {
    "path": "docs/configuration/assert_lefthook_installed.md",
    "chars": 343,
    "preview": "---\ntitle: \"assert_lefthook_installed\"\n---\n\n# `assert_lefthook_installed`\n\n**Default: `false`**\n\nWhen set to `true`, fai"
  },
  {
    "path": "docs/configuration/colors.md",
    "chars": 660,
    "preview": "---\ntitle: \"colors\"\n---\n\n# `colors`\n\n**Default: `auto`**\n\nWhether enable or disable colorful output of Lefthook. This op"
  },
  {
    "path": "docs/configuration/configs.md",
    "chars": 802,
    "preview": "---\ntitle: \"configs\"\n---\n\n# `configs`\n\n**Default:** `[lefthook.yml]`\n\nAn optional array of config paths from remote's ro"
  },
  {
    "path": "docs/configuration/env.md",
    "chars": 839,
    "preview": "---\ntitle: \"env\"\n---\n\n# `env`\n\nYou can specify some ENV variables for the command or script.\n\n#### Example\n\n```yml\n# lef"
  },
  {
    "path": "docs/configuration/exclude.md",
    "chars": 1229,
    "preview": "---\ntitle: \"exclude\"\n---\n\n# `exclude`\n\nThis option allows to setup a list of globs for files to be excluded in files tem"
  },
  {
    "path": "docs/configuration/exclude_tags.md",
    "chars": 921,
    "preview": "---\ntitle: \"exclude_tags\"\n---\n\n# `exclude_tags`\n\n[Tags](./tags.md) or command names that you want to exclude. This optio"
  },
  {
    "path": "docs/configuration/extends.md",
    "chars": 1014,
    "preview": "---\ntitle: \"extends\"\n---\n\n# `extends`\n\nYou can extend your config with another one YAML file. Its content will be merged"
  },
  {
    "path": "docs/configuration/fail_on_changes.md",
    "chars": 997,
    "preview": "---\ntitle: \"fail_on_changes\"\n---\n\n# `fail_on_changes`\n\nThe behaviour of lefthook when files (tracked by git) are modifie"
  },
  {
    "path": "docs/configuration/fail_on_changes_diff.md",
    "chars": 581,
    "preview": "---\ntitle: \"fail_on_changes_diff\"\n---\n\n# `fail_on_changes_diff`\n\nWhen Lefthook exits with a non-zero status as a result "
  },
  {
    "path": "docs/configuration/fail_text.md",
    "chars": 431,
    "preview": "---\ntitle: \"fail_text\"\n---\n\n# `fail_text`\n\nYou can specify a text to show when the command or script fails.\n\n#### Exampl"
  },
  {
    "path": "docs/configuration/file_types.md",
    "chars": 2272,
    "preview": "---\ntitle: \"file_types\"\n---\n\n# `file_types`\n\nFilter files in a [`run`](./run.md) templates by their type. Special file t"
  },
  {
    "path": "docs/configuration/files-global.md",
    "chars": 429,
    "preview": "---\ntitle: \"files (hook-level)\"\n---\n\n# `files`\n\nA custom command executed by the `sh` shell that returns the files or di"
  },
  {
    "path": "docs/configuration/files.md",
    "chars": 915,
    "preview": "---\ntitle: \"files (job-level)\"\n---\n\n# `files`\n\nA custom command executed by the `sh` shell that returns the files or dir"
  },
  {
    "path": "docs/configuration/follow.md",
    "chars": 419,
    "preview": "---\ntitle: \"follow\"\n---\n\n# `follow`\n\n**Default: `false`**\n\nFollow the STDOUT of the running commands and scripts.\n\n#### "
  },
  {
    "path": "docs/configuration/git_url.md",
    "chars": 329,
    "preview": "---\ntitle: \"git_url\"\n---\n\n# `git_url`\n\nA URL to Git repository. It will be accessed with privileges of the machine lefth"
  },
  {
    "path": "docs/configuration/glob.md",
    "chars": 1973,
    "preview": "---\ntitle: \"glob\"\n---\n\n# `glob`\n\nYou can set a glob to filter files for your command. This is only used if you use a fil"
  },
  {
    "path": "docs/configuration/glob_matcher.md",
    "chars": 2365,
    "preview": "---\ntitle: \"glob_matcher\"\n---\n\n# `glob_matcher`\n\nYou can configure which glob matching engine lefthook uses to filter fi"
  },
  {
    "path": "docs/configuration/group.md",
    "chars": 1306,
    "preview": "---\ntitle: \"group\"\n---\n\n# `group`\n\nYou can define a group of jobs and configure how they should execute using the follow"
  },
  {
    "path": "docs/configuration/install_non_git_hooks.md",
    "chars": 195,
    "preview": "---\ntitle: \"install_non_git_hooks\"\n---\n\n# `install_non_git_hooks`\n\n> Since lefthook 2.0.17\n\nInstall non-Git hooks into `"
  },
  {
    "path": "docs/configuration/interactive.md",
    "chars": 601,
    "preview": "---\ntitle: \"interactive\"\n---\n\n# `interactive`\n\n**Default: `false`**\n\n::: callout info Note\nIf you want to pass stdin to "
  },
  {
    "path": "docs/configuration/jobs.md",
    "chars": 1815,
    "preview": "---\ntitle: \"jobs\"\n---\n\n# `jobs`\n\n::: callout tip New feature\nAdded in lefthook `1.10.0`\n:::\n\nJobs provide a flexible way"
  },
  {
    "path": "docs/configuration/lefthook.md",
    "chars": 1392,
    "preview": "---\ntitle: \"lefthook\"\n---\n\n# `lefthook`\n\n**Default:** `null`\n\n::: callout tip New feature\nAdded in lefthook `1.10.5`\n:::"
  },
  {
    "path": "docs/configuration/min_version.md",
    "chars": 253,
    "preview": "---\ntitle: \"min_version\"\n---\n\n# `min_version`\n\nIf you want to specify a minimum version for lefthook binary (e.g. if you"
  },
  {
    "path": "docs/configuration/name.md",
    "chars": 355,
    "preview": "---\ntitle: \"name\"\n---\n\n# `name`\n\nName of a job. Will be printed in summary. If specified, the jobs can be merged with a "
  },
  {
    "path": "docs/configuration/no_auto_install.md",
    "chars": 555,
    "preview": "---\ntitle: \"no_auto_install\"\n---\n\n# `no_auto_install`\n\n**Default: `false`**\n\nDisable automatic installation and synchron"
  },
  {
    "path": "docs/configuration/no_tty.md",
    "chars": 244,
    "preview": "---\ntitle: \"no_tty\"\n---\n\n# `no_tty`\n\n**Default: `false`**\n\nWhether hide spinner and other interactive things. This can b"
  },
  {
    "path": "docs/configuration/only.md",
    "chars": 901,
    "preview": "---\ntitle: \"only\"\n---\n\n# `only`\n\nYou can force a command, script, or the whole hook to execute only in certain condition"
  },
  {
    "path": "docs/configuration/output.md",
    "chars": 1115,
    "preview": "---\ntitle: \"output\"\n---\n\n# `output`\n\nYou can manage verbosity using the `output` config. You can specify what to print i"
  },
  {
    "path": "docs/configuration/parallel.md",
    "chars": 192,
    "preview": "---\ntitle: \"parallel\"\n---\n\n# `parallel`\n\n**Default: `false`**\n\n::: callout info Note\nLefthook runs commands and scripts "
  },
  {
    "path": "docs/configuration/piped.md",
    "chars": 448,
    "preview": "---\ntitle: \"piped\"\n---\n\n# `piped`\n\n**Default: `false`**\n\n::: callout info Note\nLefthook will return an error if both `pi"
  },
  {
    "path": "docs/configuration/priority.md",
    "chars": 801,
    "preview": "---\ntitle: \"priority\"\n---\n\n# `priority`\n\n**Default: `0`**\n\n::: callout info Note\nThis option makes sense only when `para"
  },
  {
    "path": "docs/configuration/rc.md",
    "chars": 1827,
    "preview": "---\ntitle: \"rc\"\n---\n\n# `rc`\n\nProvide an [**rc**](https://www.baeldung.com/linux/rc-files) file, which is actually a simp"
  },
  {
    "path": "docs/configuration/ref.md",
    "chars": 485,
    "preview": "---\ntitle: \"ref\"\n---\n\n# `ref`\n\nAn optional *branch* or *tag* name.\n\n::: callout info Note\nIf you initially had `ref` opt"
  },
  {
    "path": "docs/configuration/refetch.md",
    "chars": 422,
    "preview": "---\ntitle: \"refetch\"\n---\n\n# `refetch`\n\n**Default:** `false`\n\nForce remote config refetching on every run. Lefthook will "
  },
  {
    "path": "docs/configuration/refetch_frequency.md",
    "chars": 1170,
    "preview": "---\ntitle: \"refetch_frequency\"\n---\n\n# `refetch_frequency`\n\n**Default:** Not set\n\nSpecifies how frequently Lefthook shoul"
  },
  {
    "path": "docs/configuration/remotes.md",
    "chars": 851,
    "preview": "---\ntitle: \"remotes\"\n---\n\n# `remotes`\n\nYou can provide multiple remote configs if you want to share yours lefthook confi"
  },
  {
    "path": "docs/configuration/root.md",
    "chars": 870,
    "preview": "---\ntitle: \"root\"\n---\n\n# `root`\n\nYou can change the CWD for the command you execute using `root` option.\n\nThis is useful"
  },
  {
    "path": "docs/configuration/run.md",
    "chars": 4130,
    "preview": "---\ntitle: \"run\"\n---\n\n# `run`\n\nThis is a mandatory option for a command, which specifies the actual command to be run us"
  },
  {
    "path": "docs/configuration/runner.md",
    "chars": 379,
    "preview": "---\ntitle: \"runner\"\n---\n\n# `runner`\n\nYou should specify a runner for the script. This is a command that should execute a"
  },
  {
    "path": "docs/configuration/script.md",
    "chars": 297,
    "preview": "---\ntitle: \"script\"\n---\n\n# `script`\n\nName of a script to execute. The rules are the same as for [`scripts`](./Scripts.md"
  },
  {
    "path": "docs/configuration/setup.md",
    "chars": 762,
    "preview": "---\ntitle: 'setup'\n---\n\n# `setup`\n\n::: callout tip New feature\nAdded in lefthook `2.1.2`\n:::\n\nA list of instructions to "
  },
  {
    "path": "docs/configuration/skip.md",
    "chars": 2384,
    "preview": "---\ntitle: \"skip\"\n---\n\n# `skip`\n\nYou can skip all or specific commands and scripts using `skip` option. You can also ski"
  },
  {
    "path": "docs/configuration/skip_lfs.md",
    "chars": 230,
    "preview": "---\ntitle: \"skip_lfs\"\n---\n\n# `skip_lfs`\n\n**Default:** `false`\n\nSkip running LFS hooks even if it exists on your system.\n"
  },
  {
    "path": "docs/configuration/source_dir.md",
    "chars": 335,
    "preview": "---\ntitle: \"source_dir\"\n---\n\n# `source_dir`\n\n**Default: `.lefthook/`**\n\nChange a directory for script files. Directory f"
  },
  {
    "path": "docs/configuration/source_dir_local.md",
    "chars": 273,
    "preview": "---\ntitle: \"source_dir_local\"\n---\n\n# `source_dir_local`\n\n**Default: `.lefthook-local/`**\n\nChange a directory for *local*"
  },
  {
    "path": "docs/configuration/stage_fixed.md",
    "chars": 684,
    "preview": "---\ntitle: \"stage_fixed\"\n---\n\n# `stage_fixed`\n\n**Default: `false`**\n\n> Works **only for `pre-commit`** hook\n\nWhen set to"
  },
  {
    "path": "docs/configuration/tags.md",
    "chars": 403,
    "preview": "---\ntitle: \"tags\"\n---\n\n# `tags`\n\nYou can specify tags for commands and scripts. This is useful for [excluding](./exclude"
  },
  {
    "path": "docs/configuration/templates.md",
    "chars": 903,
    "preview": "---\ntitle: \"templates\"\n---\n\n# `templates`\n\n::: callout tip New feature\nAdded in lefthook `1.10.8`\n:::\n\nProvide custom re"
  },
  {
    "path": "docs/configuration/use_stdin.md",
    "chars": 988,
    "preview": "---\ntitle: \"use_stdin\"\n---\n\n# `use_stdin`\n\n::: callout info Note\nWith many commands or scripts having `use_stdin: true`,"
  },
  {
    "path": "docs/configuration.md",
    "chars": 1367,
    "preview": "---\ntitle: \"Configuration\"\n---\n\n# Config file name\n\nLefthook supports the following file names for the main config:\n\n| F"
  },
  {
    "path": "docs/examples/commitlint.md",
    "chars": 1159,
    "preview": "# Commitlint and commitzen\n\nUse lefthook to generate commit messages using commitzen and validate them with commitlint.\n"
  },
  {
    "path": "docs/examples/filters.md",
    "chars": 871,
    "preview": "# Filters\n\nFiles passed to your hooks can be filtered with the following options\n\n- [`glob`](../configuration/glob.md)\n-"
  },
  {
    "path": "docs/examples/lefthook-local.md",
    "chars": 1376,
    "preview": "# lefthook-local.yml\n\n::: callout tip Tip\nYou can put `lefthook-local.yml` into your `~/.gitignore`, so in every project"
  },
  {
    "path": "docs/examples/remotes.md",
    "chars": 308,
    "preview": "# Remotes\n\nUse configurations from other Git repositories via `remotes` feature.\n\nLefthook will automatically download t"
  },
  {
    "path": "docs/examples/skip.md",
    "chars": 682,
    "preview": "# Skip or run on condition\n\nHere are two hooks.\n\n`pre-commit` hook will only be executed when you're committing somethin"
  },
  {
    "path": "docs/examples/stage_fixed.md",
    "chars": 381,
    "preview": "# Stage fixed files\n\n> Works only for `pre-commit` Git hook\n\nSometimes your linter fixes the changes and you usually wan"
  },
  {
    "path": "docs/examples/wrap-commands.md",
    "chars": 423,
    "preview": "# Wrap commands in local config\n\nWrapping some commands defined in a main config with `dip`[^1].\n\n```yml\n# lefthook.yml\n"
  },
  {
    "path": "docs/index.md",
    "chars": 1396,
    "preview": "---\ntitle: \"What is Lefthook?\"\ndescription: \"Welcome to Lefthook documentation\"\n---\n\n**Lefthook** is a Git hooks manager"
  },
  {
    "path": "docs/install.md",
    "chars": 467,
    "preview": "---\ntitle: \"Install Lefthook\"\n---\n\nLefthook distributes as a standalone, no-deps binary. There are multiple ways to inst"
  },
  {
    "path": "docs/installation/alpine.md",
    "chars": 533,
    "preview": "---\ntitle: \"Alpine\"\n---\n\n# APK packages for Alpine\n\n```sh\nsudo apk add --no-cache bash curl\ncurl -1sLf 'https://dl.cloud"
  },
  {
    "path": "docs/installation/arch.md",
    "chars": 350,
    "preview": "---\ntitle: \"Arch Linux\"\n---\n\n# AUR for Arch\n\n- Official [AUR package](https://aur.archlinux.org/packages/lefthook) (comp"
  },
  {
    "path": "docs/installation/deb.md",
    "chars": 519,
    "preview": "---\ntitle: \"Debian-based\"\n---\n\n# APT packages for Debian/Ubuntu Linux\n\n```sh\ncurl -1sLf 'https://dl.cloudsmith.io/public"
  },
  {
    "path": "docs/installation/devbox.md",
    "chars": 476,
    "preview": "# Devbox\n\nAdd lefthook in the devbox environment.\nlefthook already exists in the [Nix package](https://search.nixos.org/"
  },
  {
    "path": "docs/installation/go.md",
    "chars": 251,
    "preview": "# Go\n\nThe minimum Go version required is 1.26 and you can install\n\n- as global package\n\n```bash\ngo install github.com/ev"
  },
  {
    "path": "docs/installation/homebrew.md",
    "chars": 93,
    "preview": "---\ntitle: \"Homebrew\"\n---\n\n# Homebrew for MacOS and Linux\n\n```bash\nbrew install lefthook\n```\n"
  },
  {
    "path": "docs/installation/manual.md",
    "chars": 193,
    "preview": "---\ntitle: \"Manual\"\n---\n\n# Manual installation with prebuilt executable\n\nDownload binaries from [latest release](https:/"
  },
  {
    "path": "docs/installation/mise.md",
    "chars": 326,
    "preview": "# Mise\n\n> See [https://github.com/jdx/mise](https://github.com/jdx/mise)\n\n```bash\nmise use lefthook@latest\n```\n\n::: call"
  },
  {
    "path": "docs/installation/node.md",
    "chars": 1286,
    "preview": "---\ntitle: \"NPM\"\n---\n\n# NPM package\n\n```bash\nnpm install --save-dev lefthook\n```\n\n```bash\nyarn add --dev lefthook\n```\n\n`"
  },
  {
    "path": "docs/installation/python.md",
    "chars": 124,
    "preview": "# Python\n\n```sh\npython -m pip install --user lefthook\n```\n\n```sh\nuv add --dev lefthook\n```\n\n```sh\npipx install lefthook\n"
  },
  {
    "path": "docs/installation/rpm.md",
    "chars": 522,
    "preview": "---\ntitle: \"RPM-based\"\n---\n\n# RPM packages for CentOS/Fedora Linux\n\n```sh\ncurl -1sLf 'https://dl.cloudsmith.io/public/ev"
  },
  {
    "path": "docs/installation/ruby.md",
    "chars": 275,
    "preview": "# Ruby\n\n```ruby\n# Gemfile\n\ngroup :development do\n  gem \"lefthook\", require: false\nend\n```\n\nOr globally\n\n```bash\ngem inst"
  },
  {
    "path": "docs/installation/scoop.md",
    "chars": 78,
    "preview": "---\ntitle: \"Scoop\"\n---\n\n# Scoop for Windows\n\n```sh\nscoop install lefthook\n```\n"
  },
  {
    "path": "docs/installation/snap.md",
    "chars": 83,
    "preview": "---\ntitle: \"Snap\"\n---\n\n# Snap for Linux\n\n```sh\nsnap install --classic lefthook\n```\n"
  },
  {
    "path": "docs/installation/swift.md",
    "chars": 362,
    "preview": "# Swift\n\nYou can find the Swift wrapper plugin [here](https://github.com/csjones/lefthook-plugin).\n\nUtilize lefthook in "
  },
  {
    "path": "docs/installation/winget.md",
    "chars": 94,
    "preview": "---\ntitle: \"Winget\"\n---\n\n# Winget for Windows\n\n```sh\nwinget install evilmartians.lefthook\n```\n"
  },
  {
    "path": "docs/misc/contributors.md",
    "chars": 1536,
    "preview": "# Contributors\n\n- [Arkweid](https://github.com/Arkweid)\n- [Envek](https://github.com/Envek)\n- [mrexox](https://github.co"
  },
  {
    "path": "docs/usage/commands/add.md",
    "chars": 549,
    "preview": "---\ntitle: \"lefthook add\"\n---\n\n## `lefthook add`\n\nInstalls the given hook to Git hook.\n\nWith argument `--dirs` creates a"
  },
  {
    "path": "docs/usage/commands/check-install.md",
    "chars": 218,
    "preview": "---\ntitle: \"lefthook check-install\"\n---\n\n## `lefthook check-install`\n\nChecks if Git hooks are installed and synchronized"
  },
  {
    "path": "docs/usage/commands/dump.md",
    "chars": 272,
    "preview": "---\ntitle: \"lefthook dump\"\n---\n\n## `lefthook dump`\n\nPrints the whole configuration after merging all secondary configs.\n"
  },
  {
    "path": "docs/usage/commands/install.md",
    "chars": 628,
    "preview": "---\ntitle: \"lefthook install\"\n---\n\n## `lefthook install`\n\nCreates an empty `lefthook.yml` if a configuration file does n"
  },
  {
    "path": "docs/usage/commands/run.md",
    "chars": 1064,
    "preview": "---\ntitle: \"lefthook run\"\n---\n\n## `lefthook run`\n\nExecutes the commands and scripts configured for a given hook. Install"
  },
  {
    "path": "docs/usage/commands/self-update.md",
    "chars": 312,
    "preview": "---\ntitle: \"lefthook self-update\"\n---\n\n## `lefthook self-update`\n\nUpdates the binary with the latest lefthook release on"
  },
  {
    "path": "docs/usage/commands/uninstall.md",
    "chars": 103,
    "preview": "---\ntitle: \"lefthook uninstall\"\n---\n\n## `lefthook uninstall`\n\nClears Git hooks installed by lefthook.\n\n"
  },
  {
    "path": "docs/usage/commands/validate.md",
    "chars": 182,
    "preview": "---\ntitle: \"lefthook validate\"\n---\n\n## `lefthook validate`\n\nValidates your lefthook configuration. Use `lefthook dump` t"
  },
  {
    "path": "docs/usage/commands/version.md",
    "chars": 266,
    "preview": "---\ntitle: \"lefthook version\"\n---\n\n## `lefthook version`\n\n`lefthook version` prints the current binary version. Print th"
  },
  {
    "path": "docs/usage/envs/CI.md",
    "chars": 427,
    "preview": "---\ntitle: \"CI\"\n---\n\n## `CI`\n\nWhen using NPM package `lefthook`, set `CI=true` in your CI (if it does not set it automat"
  },
  {
    "path": "docs/usage/envs/CLICOLOR_FORCE.md",
    "chars": 137,
    "preview": "---\ntitle: \"CLICOLOR_FORCE\"\n---\n\n## `CLICOLOR_FORCE`\n\nSet `CLICOLOR_FORCE=true` to force colored output in lefthook and "
  },
  {
    "path": "docs/usage/envs/LEFTHOOK.md",
    "chars": 476,
    "preview": "---\ntitle: \"LEFTHOOK\"\n---\n\n## `LEFTHOOK`\n\nUse `LEFTHOOK=0 git ...` or `LEFTHOOK=false git ...` to disable lefthook when "
  },
  {
    "path": "docs/usage/envs/LEFTHOOK_BIN.md",
    "chars": 497,
    "preview": "---\ntitle: \"LEFTHOOK_BIN\"\n---\n\n## `LEFTHOOK_BIN`\n\nSet `LEFTHOOK_BIN` to a location where lefthook is installed to use th"
  },
  {
    "path": "docs/usage/envs/LEFTHOOK_CONFIG.md",
    "chars": 209,
    "preview": "---\ntitle: \"LEFTHOOK_CONFIG\"\n---\n\n## `LEFTHOOK_CONFIG`\n\nOverride the main lefthook config with `LEFTHOOK_CONFIG=~/global"
  },
  {
    "path": "docs/usage/envs/LEFTHOOK_EXCLUDE.md",
    "chars": 399,
    "preview": "---\ntitle: \"LEFTHOOK_EXCLUDE\"\n---\n\n## `LEFTHOOK_EXCLUDE`\n\nUse `LEFTHOOK_EXCLUDE={list of tags or command names to be exc"
  },
  {
    "path": "docs/usage/envs/LEFTHOOK_OUTPUT.md",
    "chars": 437,
    "preview": "---\ntitle: \"LEFTHOOK_OUTPUT\"\n---\n\n## `LEFTHOOK_OUTPUT`\n\nUse `LEFTHOOK_OUTPUT={list of output values}` to specify what to"
  },
  {
    "path": "docs/usage/envs/LEFTHOOK_VERBOSE.md",
    "chars": 208,
    "preview": "---\ntitle: \"LEFTHOOK_VERBOSE\"\n---\n\n## `LEFTHOOK_VERBOSE`\n\nSet `LEFTHOOK_VERBOSE=1` or `LEFTHOOK_VERBOSE=true` to enable "
  },
  {
    "path": "docs/usage/envs/NO_COLOR.md",
    "chars": 142,
    "preview": "---\ntitle: \"NO_COLOR\"\n---\n\n## `NO_COLOR`\n\nSet `NO_COLOR=true` to disable colored output in lefthook and all subcommands "
  },
  {
    "path": "docs/usage/features/git-args.md",
    "chars": 473,
    "preview": "## Capture ARGS from git in the script\n\nLefthook passes Git arguments to your commands and scripts.\n\n```\n├── .lefthook\n│"
  },
  {
    "path": "docs/usage/features/git-lfs.md",
    "chars": 670,
    "preview": "## Git LFS support\n\n::: callout info Note\nIf git-lfs binary is not installed and not required in your project, LFS hooks"
  },
  {
    "path": "docs/usage/features/interactive.md",
    "chars": 242,
    "preview": "## Using an interactive command or script\n\nWhen you need to interact with user – specify [`interactive: true`](../../con"
  },
  {
    "path": "docs/usage/features/local.md",
    "chars": 694,
    "preview": "## Local config\n\nYou can extend and override options of your main configuration with `lefthook-local.yml`. Don't forget "
  },
  {
    "path": "docs/usage/features/pass-stdin.md",
    "chars": 282,
    "preview": "## Pass stdin to a command or script\n\nWhen you need to read the data from stdin – specify [`use_stdin: true`](../../conf"
  },
  {
    "path": "docs/usage.md",
    "chars": 555,
    "preview": "# Usage\n\nHere are the most common usage cases. You can find more info in the docs.\n\n## Basic CLI commands\n\n```bash\n# Cre"
  },
  {
    "path": "examples/commitlint/README.md",
    "chars": 738,
    "preview": "# Use commitlint and/or commitzen\n\n## Install dependencies\n\n```bash\nyarn add -D @commitlint/cli @commitlint/config-conve"
  },
  {
    "path": "examples/commitlint/commitlint.config.js",
    "chars": 65,
    "preview": "module.exports = {extends: ['@commitlint/config-conventional']};\n"
  },
  {
    "path": "examples/commitlint/lefthook.yml",
    "chars": 320,
    "preview": "# Use this to build commit messages\nprepare-commit-msg:\n  commands:\n    commitzen:\n      interactive: true\n      run: ya"
  },
  {
    "path": "examples/complete/lefthook.yml",
    "chars": 858,
    "preview": "commit-msg:\n  scripts:\n    \"template_checker\":\n      runner: bash\n\npre-commit:\n  commands:\n    stylelint:\n      tags: fr"
  },
  {
    "path": "examples/remote/ping.yml",
    "chars": 257,
    "preview": "# Test `remotes` config of lefthook.\n#\n# # lefthook.yml\n#\n# remotes:\n#   - git_url: git@github.com:evilmartians/lefthook"
  },
  {
    "path": "examples/verbose/lefthook.yml",
    "chars": 1484,
    "preview": "---\n# lefthook.yml\n\n# This hook executes on `git commit`\npre-commit:\n  parallel: true  # All commands will be executed c"
  },
  {
    "path": "examples/with_scripts/lefthook.yml",
    "chars": 61,
    "preview": "pre-commit:\n  scripts:\n    \"good_job.js\":\n      runner: node\n"
  },
  {
    "path": "gen/jsonschema.go",
    "chars": 3017,
    "preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"github.com/invopop/jsonschema\"\n\n\t\"github.com/"
  },
  {
    "path": "go.mod",
    "chars": 3021,
    "preview": "module github.com/evilmartians/lefthook/v2\n\ngo 1.26\n\ntoolchain go1.26.0\n\nrequire (\n\tgithub.com/bmatcuk/doublestar/v4 v4."
  },
  {
    "path": "go.sum",
    "chars": 12854,
    "preview": "github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=\ngithub.com/alessio/shellescape v1."
  },
  {
    "path": "integration_test.go",
    "chars": 415,
    "preview": "//go:build integration\n\npackage main_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/rogpeppe/go-i"
  },
  {
    "path": "internal/command/add.go",
    "chars": 1460,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"path/filepath\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/config\"\n\t\"g"
  },
  {
    "path": "internal/command/add_test.go",
    "chars": 4437,
    "preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/evilmartians/lefth"
  },
  {
    "path": "internal/command/check_install.go",
    "chars": 679,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"os\"\n)\n\ntype installationStatus int\n\nconst (\n\tinstalled installationStatus = iota\n"
  },
  {
    "path": "internal/command/dump.go",
    "chars": 670,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/config\"\n)\n\ntype DumpAr"
  },
  {
    "path": "internal/command/install.go",
    "chars": 14571,
    "preview": "package command\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"s"
  },
  {
    "path": "internal/command/install_test.go",
    "chars": 21365,
    "preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/spf13/afero\"\n\t\"github.com/stretchr/te"
  },
  {
    "path": "internal/command/lefthook.go",
    "chars": 5114,
    "preview": "package command\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/knadh/koanf"
  },
  {
    "path": "internal/command/run.go",
    "chars": 8971,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/ev"
  },
  {
    "path": "internal/command/run_test.go",
    "chars": 3038,
    "preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/spf13/afero\"\n\t\"github.com/stretchr/testify/as"
  },
  {
    "path": "internal/command/uninstall.go",
    "chars": 2301,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/evilmarti"
  },
  {
    "path": "internal/command/uninstall_test.go",
    "chars": 3828,
    "preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/spf13/afero\"\n\n\t\"github.com/evilmartians/lefth"
  },
  {
    "path": "internal/command/validate.go",
    "chars": 1725,
    "preview": "package command\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strings\"\n\n\t\"github.com/kaptinlin/jsonschema\"\n\n\t\"github.com/evilmartians"
  },
  {
    "path": "internal/config/available_hooks.go",
    "chars": 1552,
    "preview": "package config\n\n// ChecksumFileName - the file, which is used just to store the current config checksum version.\nconst C"
  },
  {
    "path": "internal/config/command.go",
    "chars": 3791,
    "preview": "package config\n\nimport (\n\t\"cmp\"\n\t\"errors\"\n\t\"slices\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar ErrFilesIncompatible = errors.New(\"one of "
  },
  {
    "path": "internal/config/command_executor.go",
    "chars": 988,
    "preview": "package config\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/log\"\n\t\"github.com/evilmarti"
  },
  {
    "path": "internal/config/command_test.go",
    "chars": 1150,
    "preview": "package config\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestCommandsToJobs(t *testing"
  },
  {
    "path": "internal/config/config.go",
    "chars": 5557,
    "preview": "package config\n\nimport (\n\t\"bytes\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/mi"
  },
  {
    "path": "internal/config/files.go",
    "chars": 335,
    "preview": "package config\n\nimport \"strings\"\n\nconst (\n\tSubFiles       string = \"{files}\"\n\tSubAllFiles    string = \"{all_files}\"\n\tSub"
  },
  {
    "path": "internal/config/hook.go",
    "chars": 3425,
    "preview": "package config\n\nconst CMD = \"{cmd}\"\n\ntype Hook struct {\n\tName              string   `json:\"-\"                           "
  },
  {
    "path": "internal/config/job.go",
    "chars": 3699,
    "preview": "package config\n\nimport \"time\"\n\ntype Job struct {\n\tName     string        `json:\"name,omitempty\"      mapstructure:\"name\""
  },
  {
    "path": "internal/config/jsonc_parser.go",
    "chars": 475,
    "preview": "package config\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/tidwall/jsonc\"\n)\n\ntype JSONC struct{}\n\nfunc jsoncParser() *JSONC"
  },
  {
    "path": "internal/config/jsonschema.go",
    "chars": 88,
    "preview": "package config\n\nimport (\n\t_ \"embed\"\n)\n\n//go:embed jsonschema.json\nvar JsonSchema []byte\n"
  },
  {
    "path": "internal/config/jsonschema.json",
    "chars": 16173,
    "preview": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"$id\": \"https://json.schemastore.org/lefthook.json\",\n  "
  },
  {
    "path": "internal/config/load.go",
    "chars": 16640,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/k"
  },
  {
    "path": "internal/config/load_test.go",
    "chars": 25706,
    "preview": "package config\n\nimport (\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/spf13/afero\"\n\t\"github.com/stretchr/"
  },
  {
    "path": "internal/config/remote.go",
    "chars": 1218,
    "preview": "package config\n\ntype Remote struct {\n\tGitURL string `json:\"git_url,omitempty\" jsonschema:\"description=A URL to Git repos"
  },
  {
    "path": "internal/config/script.go",
    "chars": 3376,
    "preview": "package config\n\nimport (\n\t\"cmp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n)\n\ntype Script struct {\n\tRunner strin"
  },
  {
    "path": "internal/config/script_test.go",
    "chars": 1332,
    "preview": "package config\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestScriptsToJobs(t *testing."
  },
  {
    "path": "internal/config/skip_checker.go",
    "chars": 2038,
    "preview": "package config\n\nimport (\n\t\"github.com/gobwas/glob\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/git\"\n\t\"github.com/evi"
  },
  {
    "path": "internal/config/skip_checker_test.go",
    "chars": 4343,
    "preview": "package config\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/git\"\n\t\"github.com/ev"
  },
  {
    "path": "internal/git/command_executor.go",
    "chars": 3800,
    "preview": "package git\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/evilmartians/lefthook/v2/interna"
  },
  {
    "path": "internal/git/command_executor_test.go",
    "chars": 754,
    "preview": "package git\n\nimport (\n\t\"io\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/evilmartians/lefthoo"
  },
  {
    "path": "internal/git/lfs.go",
    "chars": 535,
    "preview": "package git\n\nimport (\n\t\"os/exec\"\n)\n\nconst (\n\tLFSRequiredFile = \".lfs-required\"\n\tLFSConfigFile   = \".lfsconfig\"\n)\n\nvar lf"
  },
  {
    "path": "internal/git/remote.go",
    "chars": 3247,
    "preview": "package git\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/evilmartians/lefthook/v2/internal/log\"\n)"
  },
  {
    "path": "internal/git/repository.go",
    "chars": 13731,
    "preview": "package git\n\nimport (\n\t\"bufio\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\""
  },
  {
    "path": "internal/git/repository_test.go",
    "chars": 4601,
    "preview": "package git\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"github.com/evilmartians/lefthook/v2/tests/helpers/cmdtest\"\n)\n\nfunc Te"
  },
  {
    "path": "internal/git/state.go",
    "chars": 2220,
    "preview": "package git\n\nimport (\n\t\"bufio\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/evilmartians/lefthook/v2/intern"
  },
  {
    "path": "internal/log/builder.go",
    "chars": 1604,
    "preview": "package log\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype builder interface {\n\tAdd(string, any) builder\n\tString() string\n\tLog()\n}\n"
  },
  {
    "path": "internal/log/execution.go",
    "chars": 773,
    "preview": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/charmbracelet/lipgloss\"\n)\n\nconst execLogPadding = 2\n\nfunc Execution(nam"
  },
  {
    "path": "internal/log/log.go",
    "chars": 14460,
    "preview": "package log\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/briandowns/spinner\"\n\t\"githu"
  },
  {
    "path": "internal/log/log_test.go",
    "chars": 14851,
    "preview": "package log\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/briandowns/spinner\"\n\t\"github.c"
  },
  {
    "path": "internal/log/settings.go",
    "chars": 2696,
    "preview": "package log\n\nimport (\n\t\"strings\"\n)\n\nconst (\n\tmeta = 1 << iota\n\tsuccess\n\tfailure\n\tsummary\n\tskips\n\texecution\n\texecutionOut"
  }
]

// ... and 172 more files (download for full content)

About this extraction

This page contains the full source code of the evilmartians/lefthook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 372 files (699.5 KB), approximately 223.7k tokens, and a symbol index with 612 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!