Skip to content

Quickstart

poly needs no configuration to be useful. Point it at a repository and it works out what each file is and which backend should handle it.

  1. Terminal window
    $ poly lint
    ./app.py
    warning ruff T201 5:5 `print` found
    warning ruff ANN201 4:5 Missing return type annotation for public function `main`
    error ruff F401 1:8 `os` imported but unused
    3 issues found.
    2 files linted
    1 issue fixable with the `--fix` option

    Only the error line fails the run. Warning-severity findings — the code-quality tier, typos, and the widened rule selections — never redden CI on their own.

  2. Terminal window
    $ poly fmt --check
    would reformat ./src/main.rs
    1 file will change.
    2 files checked

    poly fmt is a dry run by default, which is what a CI job wants. --check is just the explicit form of the default.

  3. Terminal window
    $ poly fmt --fix
    reformatted ./src/main.rs
    1 file reformatted.
    2 files checked
  4. Terminal window
    $ poly hooks install
    ✓ Installed 2 git hooks in .git/hooks
    › commit-msg
    › pre-commit

    Lint, format and Conventional-Commit checks now run on every git commit, with no external hook framework in between.

Everything above works with no config at all. Add one when you want to change poly’s opinions:

poly.toml
[defaults]
line_length = 120
line_ending = "lf"
final_newline = true
trim_trailing_whitespace = true
[discovery]
exclude = ["vendor/**", "fixtures/**"]
[lint.python.ruff]
extend_select = ["D"]
[hooks]
stages = ["pre-commit", "commit-msg"]
[hooks.builtin]
lint = true
fmt = true
commit = { stages = ["commit-msg"] }
file_safety = true

poly.local.toml layers machine-local overrides on top, and nested poly.toml files cascade in a monorepo. See Configuration for the full surface.

Code Meaning
0 Clean.
1 Error-severity findings, a failed whole-project tool, or (for poly fmt) files that would change.
2 The run verified less than it claims — a file an engine failed on, a skip budget exceeded, a config error, or a report that failed to serialize.

A --format json or --format toon consumer must check the exit code, not just the payload: the whole-project phase’s own pass/fail is written to stderr so that stdout stays a single valid document.

.github/workflows/ci.yaml
name: lint
on: [push, pull_request]
jobs:
poly:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Goldziher/poly@v0
- run: poly fmt --check .
- run: poly lint --deny-skips .

--deny-skips turns any skipped file into an exit-2 failure, so the job cannot quietly stop covering part of the tree. Use --max-skips <N> if you have a known, budgeted set of skips.

Add -q on a large repository to trim the per-file discovery detail; every count and reason still appears in the summary.