Skip to content
Install

docs/formatters.md

Formatters

Files written by the model are formatted before diagnostics and the next model call.

Successful native write, edit and apply_patch calls format every written file before LSP diagnostics and the next model call. Moved files use their destination; deleted files are skipped. Clean formatting adds no transcript copy. Failures append a bounded Formatting: section to the existing tool result in chat and History; the original edit remains successful. Cancellation stops the formatter process group and subsequent commands.

A project without .mendcode/formatter.json pays one failed file read per written file and starts no process. A configured formatter always finishes, or is killed at its limit and reported under Formatting:, before the tool result is returned: the file never changes after the model was told the write succeeded.

Project configuration is .mendcode/formatter.json. Omit it or use false to disable formatting, true to enable builtins, or an object to enable builtins with overrides. Configuration is read on each invocation; changes apply without restarting.

{
  "prettier": { "environment": { "PRETTIER_LOG_LEVEL": "warn" } },
  "oxfmt": { "disabled": true },
  "biome": { "disabled": true },
  "custom": {
    "command": ["project-fmt", "--write", "$FILE"],
    "extensions": [".custom"],
    "environment": { "FORMAT_MODE": "strict" }
  }
}

command is an argv array, never implicitly a shell. $FILE is replaced with the absolute filename in every argument. environment (or the v2 alias env) extends only the child environment. Builtin overrides retain extensions and BUN_BE_BUN=1 unless replaced. Custom entries require command and extensions; disabled removes an entry. An explicit builtin command bypasses its project/experimental gate. Invalid entries are reported and never executed.

PresetGateCommand
prettierProject dependencies / devDependencies includes Prettierprettier --write $FILE
oxfmtProject dependency plus MENDCODE_EXPERIMENTAL_OXFMT=1oxfmt $FILE
biomeProject biome.json / biome.jsoncbiome format --write $FILE

Prettier/Biome match JS/TS families, HTML, CSS/SCSS/Sass/Less, Vue/Svelte, JSON/JSONC, YAML, TOML, XML, Markdown/MDX and GraphQL. Oxfmt matches JS/TS families. Actual syntax support depends on the installed version/plugins, as in v1.

Resolution prefers the nearest project node_modules/.bin, then ancestors through the enclosing Git worktree, then PATH. Commands run in the session project directory, sequentially in registry order; all matching enabled formatters run. UTF-8 BOMs are preserved. A file has a four-second formatter budget; at the limit the formatter's process group is killed and the result says timed out. Diagnostics after the write have their own, much shorter wait (lsp.md). Same-file invocations are serialized. Formatter stdout/stderr and environment values are never included in replies.

Protocol: formatter.status { directory } and formatter.file { directory, path }; typed helpers: client.formatterStatus(directory) and client.formatFile(directory, path). Status returns name/extensions/enabled/reason and config issues. File returns structured formatter outcomes. Neither command is permitted to read-only peers. No new TUI page or slash command is needed.

Other stacks (B14)

PresetExtensionsCommand / gate
gofmt.gogofmt -w $FILE
mix.ex .exs .eex .heex .leex .neex .sfacemix format $FILE
zig.zig .zonzig fmt $FILE
clang-format.c .cc .cpp .cxx .c++ .h .hh .hpp .hxx .h++ .ino .C .Hclang-format -i $FILE; project .clang-format
ktlint.kt .ktsktlint -F $FILE
ruff.py .pyiruff format $FILE; ruff.toml, .ruff.toml, or Ruff in pyproject.toml, requirements.txt, Pipfile
air.Rair format $FILE; first help line must identify an R language formatter
uv.py .pyiuv format -- $FILE; uv format --help must succeed; skipped when project Ruff is available
rubocop.rb .rake .gemspec .rurubocop --autocorrect $FILE
standardrb.rb .rake .gemspec .rustandardrb --fix $FILE
htmlbeautifier.erb .html.erbhtmlbeautifier $FILE
dart.dartdart format $FILE
ocamlformat.ml .mliocamlformat -i $FILE; project .ocamlformat
terraform.tf .tfvarsterraform fmt $FILE
latexindent.texlatexindent -w -s $FILE
gleam.gleamgleam format $FILE
shfmt.sh .bashshfmt -w $FILE
nixfmt.nixnixfmt $FILE
rustfmt.rsrustfmt $FILE
pint.php./vendor/bin/pint $FILE; Composer require / require-dev includes laravel/pint
ormolu.hsormolu -i $FILE
cljfmt.clj .cljs .cljc .edncljfmt fix --quiet $FILE
dfmt.ddfmt -i $FILE

Every preset needs an installed executable. Project gates search up to the enclosing worktree; Pint's executable remains relative to the session directory, as in v1. Disabling ruff or uv disables both backends. An explicit command bypasses builtin gates and help probes; extensions and child environment are configurable. Probes share the file's formatter budget and cancellation, and status probes stop with the manager. Only safe failure reasons reach chat/History or clients.

No formatter is installed or downloaded automatically. Real-tool validation requires installed binaries; local fake executables prove configuration, process lifecycle and daemon/client/engine integration only.