Why Flint

June 24, 2026 ยท View on GitHub

๐Ÿ“– Blog post: Flint โ€” a linter setup that doesn't slow down your AI agent

Flint exists to make repository linting fast, predictable, and easy to keep consistent between local development, hooks, CI, and agentic workflows.

It uses the tools the repo has chosen to install, runs only the checks that are actually opted in, and keeps behavior aligned across every place the repo is linted.

For comparisons with other lint runners and hook managers, see Alternatives / Comparisons.

Fast

This is the primary goal; everything else serves it.

  • Native execution only: no Docker startup overhead
  • Parallel runs in check mode
  • Small binary, cached by mise
  • Diff-aware by default: changed files only unless --full is requested
  • Opt-in activation: undeclared tools are skipped entirely
  • Local runs skip slower checks by default unless you use --full or name the linter explicitly

Local and CI stay aligned

One binary, one config model, and the same pinned tools in both environments. Local runs default to the change-triggered subset for day-to-day speed, while CI activates the full linter set.

Predictable and updatable linter versions

Flint runs pinned linter versions chosen by the repo, so lint behavior does not suddenly change just because an upstream release landed. When a repo wants a new lychee, ruff, or shellcheck, it updates that version explicitly and reviews the result as a normal change. In practice that also works well with tools like dependabot and Renovate, because the pinned versions live in mise.toml.

Easy setup, sane defaults

flint init bootstraps a repo quickly, the active checks come from mise.toml, and most repos do not need much custom configuration beyond choosing tools.

Opinionated where it matters

Flint prefers one canonical config shape per linter to avoid discovery drift, while still letting repos choose a config directory with FLINT_CONFIG_DIR when the tool supports explicit config injection.

Separated ownership

Linters and formatters are distinct checks, and overlapping file types have a clear style owner. editorconfig-checker defers where formatter ownership should win, which avoids contradictory output.

Examples:

  • Markdown style is owned by rumdl, not split between multiple Markdown tools
  • JS/TS/JSON formatting is owned by Biome, with root biome.jsonc as the canonical config
  • editorconfig-checker defers to active formatters for file types where the formatter should be authoritative

Formatter Deferral And .editorconfig

Formatter-owned file types still need one shared source of truth for editors and for editorconfig-checker. Flint handles that in two layers:

  • The formatter remains authoritative for formatting when it is active.
  • editorconfig-checker skips file types owned by active formatters instead of reporting conflicting style failures.
  • Flint writes matching .editorconfig line-length carve-outs such as [*.rs] max_line_length = off when a formatter owns line width, so editors and editorconfig-checker do not keep enforcing a stale numeric line length.

This is intentionally narrow. Flint does not try to copy every formatter rule into .editorconfig; it only aligns the overlapping settings that would otherwise create contradictory behavior.

AI-friendly

--fix fixes what's fixable silently, prints output only for issues needing review, and exits with a structured summary:

[shellcheck]
...
flint: fixed: cargo-fmt โ€” commit before pushing | review: shellcheck

Only unfixable issues surface for review; no reasoning step is required.

Cross-platform

Flint runs on Linux, macOS, and Windows. The built-in registry accounts for platform differences such as binary names and path quoting.

Autofix where possible

--fix checks first, fixes what's fixable, and reports what needs review. Fix mode runs serially to avoid concurrent writes. Pass specific linter names to limit which fixers run, for example flint run --fix rumdl shfmt.