Fast. Always on. Fully configurable.
Curb reads your .editorconfig when you have one, and works out of the box when you don't. Mechanical style fixes happen before an AI agent ever reports a diagnostic, so it edits less and finishes faster.
// what it is
Most formatters are a step you have to remember: before commit, in CI, via a pre-push hook.
Curb runs before CoreCompile on every dotnet build. By the time the
compiler reads a file, the style is already applied.
One PackageReference. Curb runs BeforeTargets="CoreCompile", reading
the .editorconfig already in your repo. Nothing to remember, nothing to add
to CI.
A linter that isn't instant becomes a linter people skip. Curb never loads a compilation and starts in ~10 ms. MSBuild skips it entirely when a project is untouched — and even when it runs, curb itself reformats only the files that changed.
the numbers →All 39 IDE0055 keys, the 8 core EditorConfig keys, plus ReSharper's wrapping and blank-line keys — read from the .editorconfig you already have. Curb invents nothing.
the full option reference →
Curb's output is a fixed point of dotnet format — 100% with reflow off, 100%
with reflow on, 99.9% with preservation on, gated in CI on every push. curb cleanup
is held to the same standard against dotnet format style. Hit Format Document and
nothing moves.
The build an agent reads has already been formatted. What's left — rules that need a
compilation, like unused usings and var — curb cleanup fixes in one
pass, not one round trip per offence.
// how it works
Curb runs before CoreCompile, so the compiler reads source that is already formatted.
The mechanical offences are not reported — they are gone.
No second config file. Add max_line_length when you want reflow; leave it out and Curb will never wrap a line for you.
Build-only — no library, nothing in your output. Drop it in Directory.Build.props to cover a whole solution.
Files are rewritten in Debug and checked in Release. A developer wants the fix; CI wants to be told. An untouched project pays nothing: no process start, no directory walk.
[*.cs] indent_style = tab max_line_length = 120 # opt into reflow csharp_new_line_before_open_brace = all csharp_prefer_braces = true csharp_style_namespace_declarations = file_scoped
<PackageReference Include="curb" Version="*" PrivateAssets="all" />
Rewriting code unattended is only reasonable if it cannot go wrong. Every file is verified in memory before anything is written — the tokens emitted must match the tokens parsed — and a file that fails is reported and left exactly as it was. Syntax Curb has not learned yet is emitted verbatim, so it is incomplete long before it is ever destructive. How it is checked →
// where the time goes
Newtonsoft.Json — 945 files, no .editorconfig. Wall-clock time, best of three
cold runs — first invocation, nothing cached, every file reparsed.
dotnet format whitespaceCurb ships as a native-AOT binary — about 10 ms to start, no warm-up cost. And that's still not the fastest curb gets: MSBuild's own incremental build skips curb entirely once a project is untouched — no process start, no directory walk — and even when curb does run, it reformats only the files that changed since its own cache, not the whole project. Curb is fast cold, faster warm. Full benchmark table →
// scope
Style rules split by what they need to know. The split is not an implementation detail — it is
why Curb can run before the compiler and still leave nothing for dotnet format to
do afterwards.
Decided from the parse tree alone. No restore, no compilation, no project system — runs before CoreCompile, rewrites the file, and the compiler reads formatted source.
max_line_lengthManual, not automatic. Run curb cleanup yourself after a build, and set
EnforceCodeStyleInBuild = true (usually in Directory.Build.props) so
the build actually reports the diagnostics it reads.
var, readonly fieldsnew(), string interpolation, redundant #nullable
Pass 1's output is measured as a fixed point of dotnet format whitespace in CI, on
every push: 100% with reflow off, 100% with reflow on, 99.9% with
csharp_keep_existing_linebreaks = true. Pass 2 is held to the same discipline against
dotnet format style. Both read the same .editorconfig in your repo.
How it's measured →
// for coding agents
Style rules written as prose in an AGENTS.md get followed inconsistently, and
every correction is a round trip: the agent writes, the build complains, the agent edits, the
build runs again. Every one of those edits is mechanical — there was one right answer, and it
was already in your .editorconfig.
Layout needs no compilation, so it is already fixed by the time an agent's build even
finishes — that half of IDE0055 never reaches it. What's left after that is the handful of
rules that do need a compilation to decide: unused usings, var, readonly fields.
curb cleanup fixes all of them in one pass over the diagnostics the build already
produced — one command, not one round trip per offence.
## Style Formatting and syntax-level code style are applied automatically by the build. See .editorconfig. Do not hand-format code, and do not "fix" formatting you see in a diff. dotnet build will do it. After a build reports style diagnostics, run curb cleanup --forward. It owns IDE0005, IDE0007, IDE0034, IDE0040, IDE0044, IDE0071, IDE0090, IDE0240, IDE0250, and IDE0251, and forwards anything else to dotnet format style. Always safe to add: it skips dotnet format entirely once curb has already fixed everything.
$ dotnet build Order.cs(3,1): error IDE0005: Using directive is unnecessary Order.cs(4,1): error IDE0005: Using directive is unnecessary Build FAILED. $ curb cleanup fixed 2 of 2 diagnostics · 0 refused $ dotnet build Build succeeded.
// packages
The MSBuild package wires Curb into the build and needs no further configuration. The CLI is for standalone formatting outside a build — scripts, editors, CI steps that run without the SDK project system.
Build-only — PrivateAssets="all" keeps it out of your output. Drop one
PackageReference in Directory.Build.props to format every project
in a solution. Nothing else to configure.
A global dotnet tool. Format or check a directory tree from the command line — useful in
editors, Git hooks, or CI pipelines that do not go through dotnet build.
// outside the build
curb-cli isn't only for CI steps without the SDK. Point check at a
git pre-commit hook and most runs cost a hash comparison against a cache, not a full parse —
it reads the same .editorconfig your build already does. Husky.NET wires it
up for the whole team in one config file.
#!/bin/sh curb check --cache .git/curb.cache ./src
// ci without the sdk
A pre-built, distroless container image on GitHub Container Registry — pulled, not built, so
there is nothing to compile in your workflow. check fails the job if anything
would reformat; format rewrites in place.
One uses: line. No actions/setup-dotnet, no tool install, no restore.
Native-AOT on a chiseled, shell-less base image. Pulled, not built on every run.
Reads the .editorconfig already in your repo — nothing new to configure.
- uses: nullean/curb@main with: command: check # or "format" path: .
The .editorconfig you already have. Nothing new to learn.
Nothing to remember to run. Just dotnet build.