# Configuration `prek` reads **one configuration file per project**. You only need to choose **one** format: - **prek.toml** (TOML) — recommended for new users - **.pre-commit-config.yaml** (YAML) — best if you already use pre-commit or rely on tool/editor support Both formats are first-class and will be supported long-term. They describe the **same** configuration model: you list repositories under `repos`, then enable and configure hooks from those repositories. === "prek.toml" ```toml [[repos]] repo = "https://github.com/pre-commit/pre-commit-hooks" hooks = [{ id = "trailing-whitespace" }] ``` === ".pre-commit-config.yaml" ```yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks hooks: - id: trailing-whitespace ``` ## Pre-commit compatibility `prek` is **fully compatible** with [`pre-commit`](https://pre-commit.com/) YAML configs, so your existing `.pre-commit-config.yaml` files work unchanged. If you use **`prek.toml`**, there’s nothing to worry about from a `pre-commit` perspective: upstream `pre-commit` does not read TOML. If you use the same `.pre-commit-config.yaml` with both tools, keep in mind: - `prek` supports several extensions beyond upstream `pre-commit`. - Upstream `pre-commit` may warn about unknown keys or error out on unsupported features. - To stay maximally portable, avoid the extensions listed below (or keep separate configs). Notable differences (when using YAML): - **Workspace mode** is a `prek` feature that can discover multiple projects; upstream `pre-commit` is single-project. - `files` / `exclude` can be written as **glob mappings** in `prek` (in addition to regex), which is not supported by upstream `pre-commit`. - `repo: builtin` adds fast built-in hooks in `prek`. - Upstream `pre-commit` uses `minimum_pre_commit_version`, while `prek` uses `minimum_prek_version` and intentionally ignores `minimum_pre_commit_version`. ### Prek-only extensions These entries are implemented by `prek` and are not part of the documented upstream `pre-commit` configuration surface. They work in both YAML and TOML, but they only matter for compatibility if you share a YAML config with upstream `pre-commit`. - Top-level: - [`minimum_prek_version`](#prek-only-minimum-prek-version-config) - [`orphan`](#prek-only-orphan) - Repo type: - [`repo: builtin`](#prek-only-repo-builtin) - Hook-level: - [`env`](#prek-only-env) - [`shell`](#shell) - [`priority`](#prek-only-priority) - [`minimum_prek_version`](#prek-only-minimum-prek-version-hook) ## Configuration file ### Location (discovery) By default, `prek` looks for a configuration file starting from your current working directory and moving upward. It stops when it finds a config file, or when it hits the git repository boundary. If you run **without** `--config`, `prek` then enables **workspace mode**: - The first config found while traversing upward becomes the workspace root. - From that root, `prek` searches for additional config files in subdirectories (nested projects). Workspace discovery respects `.gitignore`, and also supports `.prekignore` for excluding directories from discovery. For the full behavior and examples, see [Workspace Mode](workspace.md). !!! tip After updating `.prekignore`, run with `--refresh` to force a fresh project discovery so the changes are picked up. If you pass `--config` / `-c`, workspace discovery is disabled and only that single config file is used. ### File name `prek` recognizes the following configuration filenames: - `prek.toml` (TOML) - `.pre-commit-config.yaml` (YAML, preferred for pre-commit compatibility) - `.pre-commit-config.yml` (YAML, alternate) In workspace mode, each project uses one of these filenames in its own directory. !!! note "One format per repo" We recommend using a **single format** across the whole repository to avoid confusion. If multiple configuration files exist in the same directory, `prek` uses only one and ignores the rest. The precedence order is: 1. `prek.toml` 2. `.pre-commit-config.yaml` 3. `.pre-commit-config.yml` ### File format Both `prek.toml` and `.pre-commit-config.yaml` map to the same configuration model (repositories under `repos`, then `hooks` under each repo). This section focuses on format-specific authoring notes and examples. #### TOML (`prek.toml`) Practical notes: - Structure is explicit and less indentation-sensitive. - Inline tables are common for hooks (e.g. `{ id = "ruff" }`). TOML supports both **inline tables** and **array-of-tables**, so you can choose between a compact or expanded hook style. Inline tables (best for small/simple hook configs): ```toml [[repos]] repo = "https://github.com/pre-commit/pre-commit-hooks" rev = "v6.0.0" hooks = [ { id = "end-of-file-fixer", args = ["--fix"] }, ] ``` Array-of-tables (more readable for larger hook configs): ```toml [[repos]] repo = "https://github.com/pre-commit/pre-commit-hooks" rev = "v6.0.0" [[repos.hooks]] id = "trailing-whitespace" [[repos.hooks]] id = "check-json" ``` Example: === "prek.toml" ```toml default_language_version.python = "3.12" [[repos]] repo = "local" hooks = [ { id = "ruff", name = "ruff", language = "system", entry = "python3 -m ruff check", files = "\\.py$", }, ] ``` The previous example uses multiline inline tables, a feature that was introduced in [TOML 1.1](https://toml.io/en/v1.1.0), not all parsers have support for it yet. You may want to use the longer form if your editor/IDE complains about it. === "prek.toml" ```toml default_language_version.python = "3.12" [[repos]] repo = "local" [[repos.hooks]] id = "ruff" name = "ruff" language = "system" entry = "python3 -m ruff check" files = "\\.py$" ``` #### YAML (`.pre-commit-config.yaml` / `.yml`) Practical notes: - Regular expressions are provided as YAML strings. If your regex contains backslashes, quote it (e.g. `files: '\\.rs$'`). - YAML anchors/aliases and merge keys are supported, so you can de-duplicate repeated blocks. Example: === ".pre-commit-config.yaml" ```yaml default_language_version: python: "3.12" repos: - repo: local hooks: - id: ruff name: ruff language: system entry: python3 -m ruff check files: "\\.py$" ``` #### Choosing a format **`prek.toml`** - Clearer structure and less error-prone syntax. - Recommended for new users or new projects. **`.pre-commit-config.yaml`** - Long-established in the ecosystem with broad tool/editor support. - Fully compatible with upstream `pre-commit`. **Recommendation** - If you already use `.pre-commit-config.yaml`, keep it. - If you want a cleaner, more robust authoring experience, prefer `prek.toml`. !!! tip If you want to switch, you can use [`prek util yaml-to-toml`](cli.md#prek-util-yaml-to-toml) to convert YAML configs to `prek.toml`. YAML comments are not preserved during conversion. ### Scope (per-project) Each configuration file (`prek.toml`, `.pre-commit-config.yaml`, or `.pre-commit-config.yml`) is scoped to the **project directory it lives in**. In workspace mode, `prek` treats every discovered configuration file as a **distinct project**: - A project’s config only controls hook selection and filtering (for example `files` / `exclude`) for that project. - A project may contain nested subprojects (subdirectories with their own config). Those subprojects run using *their own* configs. Practical implication: filters in the parent project do not “turn off” a subproject. Example layout (monorepo with a nested project): - `foo/.pre-commit-config.yaml` (project `foo`) - `foo/bar/.pre-commit-config.yaml` (project `foo/bar`, nested subproject) If project `foo` config contains an `exclude` that matches `bar/**`, then hooks for project `foo` will not run on files under `foo/bar`: === "prek.toml" ```toml # foo/prek.toml exclude = { glob = "bar/**" } ``` === ".pre-commit-config.yaml" ```yaml # foo/.pre-commit-config.yaml exclude: glob: "bar/**" ``` But if `foo/bar` is itself a project (has its own config), files under `foo/bar` are still eligible for hooks when running **in the context of project `foo/bar`**. !!! note "Excluding a nested project" If `foo/bar/.pre-commit-config.yaml` exists but you *don’t* want it to be recognized as a project in workspace mode, exclude it from discovery using [`.prekignore`](workspace.md#discovery). Like `.gitignore`, `.prekignore` files can be placed anywhere in the workspace and apply to their directory and all subdirectories. !!! tip After updating `.prekignore`, run with `--refresh` to force a fresh project discovery so the changes are picked up. ### Validation Use [`prek validate-config`](cli.md#prek-validate-config) to validate one or more config files. If you want IDE completion / validation, prek provides a JSON Schema at [https://prek.j178.dev/docs/prek.schema.json](https://prek.j178.dev/docs/prek.schema.json). And the schema is also submitted to the [JSON Schema Store](https://www.schemastore.org/prek.json), so some editors may pick it up automatically. That schema tracks what `prek` accepts today, but `prek` also intentionally tolerates unknown keys for forward compatibility. ## Configuration reference This section documents the configuration keys that `prek` understands. ### Top-level keys #### `repos` (required) A list of hook repositories. Each entry is one of: - a remote repository (typically a git URL) - `repo: local` for hooks defined directly in your repository - `repo: meta` for built-in meta hooks - `repo: builtin` for `prek`'s built-in fast hooks See [Repo entries](#repo-entries). #### `files` Global *include* regex applied before hook-level filtering. - Type: regex string (default, pre-commit compatible) **or** a prek-only glob pattern mapping - Default: no global include filter This is usually used to narrow down the universe of files in large repositories. !!! note "What path is matched? (workspace + nested projects)" `files` (and `exclude`) are matched against the file path **relative to the project root** — i.e. the directory containing the configuration file. - For the root project, this is the workspace root. - For a nested project, this is the nested project directory. Example (workspace mode): - Root project config: `./.pre-commit-config.yaml` - Nested project config: `./nested/.pre-commit-config.yaml` For a file at `nested/excluded_by_project`: - Root project sees the path as `nested/excluded_by_project` - Nested project sees the path as `excluded_by_project` This matters most for anchored patterns like `^...$`. !!! tip "Regex matching" When `files` / `exclude` are regex strings, they are matched with *search* semantics (the pattern can match anywhere in the path). Use `^` to anchor at the beginning and `$` at the end. `prek` uses the Rust [`fancy-regex`](https://github.com/fancy-regex/fancy-regex) engine. Most typical patterns are portable to upstream `pre-commit`, but very advanced regex features may differ from Python’s `re`. !!! note "prek-only globs" In addition to regex strings, `prek` supports glob patterns via: - `files: { glob: "..." }` (single glob) - `files: { glob: ["...", "..."] }` (glob list) This is a `prek` extension. Upstream `pre-commit` expects regex strings here. For more information on the glob syntax, refer to the [globset documentation](https://docs.rs/globset/latest/globset/#syntax). Examples: === "prek.toml" ```toml # Regex (portable to pre-commit) files = "\\.rs$" # Glob (prek-only) files = { glob = "src/**/*.rs" } # Glob list (prek-only; matches if any glob matches) files = { glob = ["src/**/*.rs", "crates/**/src/**/*.rs"] } ``` === ".pre-commit-config.yaml" ```yaml # Regex (portable to pre-commit) files: "\\.rs$" # Glob (prek-only) files: glob: "src/**/*.rs" # Glob list (prek-only; matches if any glob matches) files: glob: - "src/**/*.rs" - "crates/**/src/**/*.rs" ``` #### `exclude` Global *exclude* regex applied before hook-level filtering. - Type: regex string (default, pre-commit compatible) **or** a prek-only glob pattern mapping - Default: no global exclude filter `exclude` is useful for generated folders, vendored code, or build outputs. !!! note "What path is matched?" Same as [`files`](#top-level-files): the pattern is evaluated against the file path **relative to the project root** (the directory containing the config). !!! note "prek-only globs" Like `files`, `exclude` supports `glob` (single glob or glob list) as a `prek` extension. For glob syntax details, see the [globset documentation](https://docs.rs/globset/latest/globset/#syntax). Examples: === "prek.toml" ```toml # Regex (portable to pre-commit) exclude = "^target/" # Glob (prek-only) exclude = { glob = "target/**" } # Glob list (prek-only) exclude = { glob = ["target/**", "dist/**"] } ``` === ".pre-commit-config.yaml" ```yaml # Regex (portable to pre-commit) exclude: "^target/" # Glob (prek-only) exclude: glob: "target/**" # Glob list (prek-only) exclude: glob: - "target/**" - "dist/**" ``` Verbose regex example (useful for long allow/deny lists): === "prek.toml" ```toml # `(?x)` enables "verbose" regex mode (whitespace and newlines are ignored). exclude = """(?x)^( docs/| vendor/| target/ )""" ``` === ".pre-commit-config.yaml" ```yaml # `(?x)` enables "verbose" regex mode (whitespace and newlines are ignored). exclude: | (?x)^( docs/| vendor/| target/ ) ``` #### `fail_fast` Stop the run after the first failing hook. - Type: boolean - Default: `false` This is a global default; individual hooks can also set `fail_fast`. #### `default_language_version` Map a language name to the default `language_version` used by hooks of that language. - Type: map - Default: none (hooks fall back to `language_version: default`) Example: === "prek.toml" ```toml default_language_version.python = "3.12" default_language_version.node = "20" ``` === ".pre-commit-config.yaml" ```yaml default_language_version: python: "3.12" node: "20" ``` `prek` treats `language_version` as a version request (often a semver-like selector) and may install toolchains automatically. See [Difference from pre-commit](diff.md). #### `default_stages` Default `stages` used when a hook does not specify its own. - Type: list of stage names - Default: all stages Allowed values: - `manual` - `commit-msg` - `post-checkout` - `post-commit` - `post-merge` - `post-rewrite` - `pre-commit` - `pre-merge-commit` - `pre-push` - `pre-rebase` - `prepare-commit-msg` #### `default_install_hook_types` Default Git shim name(s) installed by `prek install` when you don’t pass `--hook-type`. - Type: list of `--hook-type` values - Default: `[pre-commit]` This controls which Git shims are installed (for example `pre-commit` vs `pre-push`). It is separate from a hook’s `stages`, which controls when a particular hook is eligible to run. Allowed values: - `pre-commit` - `pre-push` - `commit-msg` - `prepare-commit-msg` - `post-checkout` - `post-commit` - `post-merge` - `post-rewrite` - `pre-merge-commit` - `pre-rebase` #### `minimum_prek_version` !!! note "prek-only" This key is a `prek` extension. Upstream `pre-commit` uses `minimum_pre_commit_version`, which `prek` intentionally ignores. Require a minimum `prek` version for this config. - Type: string (version) - Default: unset If the installed `prek` is older than the configured minimum, `prek` exits with an error. Example: === "prek.toml" ```toml minimum_prek_version = "0.2.0" ``` === ".pre-commit-config.yaml" ```yaml minimum_prek_version: "0.2.0" ``` #### `orphan` !!! note "prek-only" `orphan` is a `prek` workspace-mode feature and is not recognized by upstream `pre-commit`. Workspace-mode setting to isolate a nested project from parent configs. - Type: boolean - Default: `false` When `orphan: true`, files under this project directory are handled only by this project’s config and are not “seen” by parent projects. Example: === "prek.toml" ```toml orphan = true [[repos]] repo = "https://github.com/astral-sh/ruff-pre-commit" rev = "v0.8.4" hooks = [{ id = "ruff" }] ``` === ".pre-commit-config.yaml" ```yaml orphan: true repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.4 hooks: - id: ruff ``` See [Workspace Mode - File Processing Behavior](workspace.md#file-processing-behavior) for details. ### Repo entries Each item under `repos:` is a mapping that always contains a `repo:` key. #### Remote repository Use this for hooks distributed in a separate repository. Required keys: - `repo`: repository location (commonly an https git URL) - `rev`: version to use (tag, branch, or commit SHA) - `hooks`: list of hook selections Remote hook definitions live inside the hook repository itself in the `.pre-commit-hooks.yaml` manifest (at the repo root). Your config only selects hooks by `id` and optionally overrides options. See [Authoring Hooks](authoring-hooks.md) if you maintain a hook repository. ##### `repo` Where to fetch hooks from. In most configs this is a git URL. `prek` also recognizes special values documented separately: `local`, `meta`, and `builtin`. ##### `rev` The revision to use for the remote repository. Use a tag or commit SHA for repeatable results. If you use a moving target (like a branch name), runs may change over time. ##### `hooks` The list of hooks to enable from that repository. Each item must at least specify `id`. You can also add hook-level options (filters, args, stages, etc.) to customize behavior. Example: === "prek.toml" ```toml [[repos]] repo = "https://github.com/astral-sh/ruff-pre-commit" rev = "v0.8.4" hooks = [{ id = "ruff", args = ["--fix"] }] ``` === ".pre-commit-config.yaml" ```yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.4 hooks: - id: ruff args: [--fix] ``` Notes: - For reproducibility, prefer immutable pins (tags or commit SHAs). - `prek auto-update` can help update `rev` values. #### `repo: local` Define hooks inline inside your repository. Keys: - `repo`: must be `local` - `hooks`: list of **local hook definitions** (see [Local hook definition](#local-hook-definition)) Example: === "prek.toml" ```toml [[repos]] repo = "local" hooks = [ { id = "cargo-fmt", name = "cargo fmt", language = "system", entry = "cargo fmt", files = "\\.rs$", }, ] ``` === ".pre-commit-config.yaml" ```yaml repos: - repo: local hooks: - id: cargo-fmt name: cargo fmt language: system entry: cargo fmt files: "\\.rs$" ``` #### `repo: meta` Use `pre-commit`-style meta hooks that validate and debug your configuration. `prek` supports the following meta hook ids: - `check-hooks-apply` - `check-useless-excludes` - `identity` Restrictions: - `id` is required. - `entry` is not allowed. - `language` (if set) must be `system`. You may still configure normal hook options such as `files`, `exclude`, `stages`, etc. Example: === "prek.toml" ```toml [[repos]] repo = "meta" hooks = [{ id = "check-useless-excludes" }] ``` === ".pre-commit-config.yaml" ```yaml repos: - repo: meta hooks: - id: check-useless-excludes ``` #### `repo: builtin` !!! note "prek-only" `repo: builtin` is specific to `prek` and is not compatible with upstream `pre-commit`. Use `prek`’s built-in fast hooks (offline, zero setup). Restrictions: - `id` is required. - `entry` is not allowed. - `language` (if set) must be `system`. Example: === "prek.toml" ```toml [[repos]] repo = "builtin" hooks = [ { id = "trailing-whitespace" }, { id = "check-yaml" }, ] ``` === ".pre-commit-config.yaml" ```yaml repos: - repo: builtin hooks: - id: trailing-whitespace - id: check-yaml ``` For the list of available built-in hooks and the “automatic fast path” behavior, see [Built-in Fast Hooks](builtin.md). ### Hook entries Hook items under `repos[*].hooks` have slightly different shapes depending on the repo type. #### Remote hook selection For a remote repo, the hook entry must include: - `id` (required): selects the hook from the repository All other hook keys are optional overrides (for example `args`, `files`, `exclude`, `stages`, …). !!! note "Advanced overrides" `prek` also supports overriding `name`, `entry`, and `language` for remote hooks. This can be useful for experimentation, but it may reduce portability to the original `pre-commit`. #### Local hook definition For `repo: local`, the hook entry is a full definition and must include: - `id` (required): stable identifier used by `prek run ` and selectors - `name` (required): label shown in output - `entry` (required): command to execute - `language` (required): how `prek` sets up and runs the hook #### Builtin/meta hook selection For `repo: builtin` and `repo: meta`, the hook entry must include `id`. You can optionally provide `name` and normal hook options (filters, stages, etc), but not `entry`. ### Common hook options These keys can appear on hooks (remote/local/builtin/meta), subject to the restrictions above. #### `id` The stable identifier of the hook. - For remote hooks, this must match a hook id defined by the remote repository. - For local hooks, you choose it. `id` is also used for CLI selection (for example `prek run ` and `PREK_SKIP`). !!! note "Hook ids containing `:`" If your hook id contains `:` (for example `id: lint:ruff`), `prek run lint:ruff` will not select that hook. `prek` interprets `lint:ruff` as the selector `:`, with project `lint` and hook `ruff`. To select the hook id `lint:ruff`, add a leading `:` and run `prek run :lint:ruff`. #### `name` Human-friendly label shown in output. - Required for `repo: local` hooks. - Optional as an override for remote/meta/builtin hooks. #### `entry` The command line to execute for the hook. - Required for `repo: local` hooks. - Optional override for remote hooks. - Not allowed for `repo: meta` and `repo: builtin`. If `pass_filenames: true`, `prek` appends matching filenames to this command when running. #### `shell` !!! note "prek-only" `shell` is a `prek` extension and may not be recognized by upstream `pre-commit`. Run `entry` through a predefined shell adapter. - Type: one of `sh`, `bash`, `pwsh`, `powershell`, `cmd` - Default: `null` (run `entry` directly without a shell) When `shell` is omitted, `prek` preserves the default no-shell behavior: it parses `entry` into argv, invokes the command directly, and appends `args` and matching filenames as process arguments. When `shell` is set, `entry` is treated as source for that shell. `prek` writes the source to a temporary script file, runs it with the selected shell adapter, and passes hook `args` followed by matching filenames as script arguments. | `shell` | Adapter command | Script arguments | | -- | -- | -- | | `bash` | `bash --noprofile --norc -eo pipefail