No server. No API key. No network. No opinions about your argument. Eight deterministic checkers read a Markdown, LaTeX, or plain-text manuscript and emit one compact JSON report. The scripts analyze; the agent reports. Same input, same findings, every time.
An LLM asked to proofread a text will read all 9,000 words into its context, forget the middle, and confidently invent a citation error on page 4. This tool doesn't. The checkers find the problems and hand over line, column, and a short excerpt — the agent summarizes that and never reads the manuscript to find anything.
Install
1. Claude Code plugin
/plugin marketplace add domrice/prose-preflight
/plugin install prose-preflight
2. Any other harness (Cursor, Codex, Copilot, Gemini, Windsurf, …)
npx skills@latest add domrice/prose-preflight
3. CLI only, no agent — for CI or pre-commit
uvx prose-preflight text.md
4. From source (contributors)
git clone https://github.com/domrice/prose-preflight && cd prose-preflight
uv sync && uv run pytest
Routes 1–3 resolve the package from PyPI, so none of them needs the repo on disk and
SKILL.md carries no paths. Needs Python ≥ 3.11; uvx supplies
its own interpreter, so route 3 needs nothing but uv.
Use
uvx prose-preflight text.md
The full report is written to PREFLIGHT_text.md — one per document, so checking a second
file never overwrites the first. A capped JSON summary goes to stdout, with report naming
that file. That split is
the whole point — the agent reads stdout, links the file, and stays small.
| flag | does |
|---|---|
--md PATH |
move the report (default PREFLIGHT_<file>.md) |
--max-findings N |
cap stdout findings (default 20; 0 lifts it) |
--checks a,b |
run a subset |
--config FILE |
deep-merge a YAML file over the bundled config |
--version |
the packaged version |
Findings are capped round-robin across categories, so 400 Vale alerts cannot crowd out the
one missing Methods section. counts and total always cover everything; truncated says
what was dropped.
The two skills
The CLI is deterministic on purpose — offline, no API key, same input same output. So nothing calls a model inside it. The judgment lives in the agent that drives it, where it is free, and it ships as two skills:
prose-preflightruns the command and reports it, but does not just print it. It diagnoses what the counts mean, and gives everyseverity: reviewfinding a verdict —keep,soften, orcut, with a rewrite. The checker can see that a hedge is there; only the agent can weigh it against the argument. It never edits the document.prose-deepreadis the opposite of preflight: it reads the manuscript end to end and looks for what no regex can reach — claims the results do not support, terms used before they are defined, sections that do not deliver what their heading promises, and passages that can be cut without losing an argument. Expensive, so it runs only when you ask for it by name. It never edits either.
Config
Every check is configurable from one YAML file. Write it once per project:
uvx prose-preflight --init-config # writes prose-preflight.yaml, every key commented
uvx prose-preflight text.md # picked up automatically from the working directory
A prose-preflight.yaml next to where you run is used without any flag; --config FILE
points elsewhere. Keep only what you change — the rest falls back to the defaults. Lists are
replaced, not appended, and unknown keys are reported on stderr, so a typo never fails
silently.
Two lists ship empty, because there is no universal right answer and a wrong entry costs
you a false positive on every run: terminology.terms (your journal's preferred spellings)
and structure.required_sections (most documents are fragments, not whole papers). Fill
them in and those checks switch on.
checks:
terminology:
terms:
finite element: [finite-element, FE]
sentence_length:
max_words: 32
structure:
required_sections:
[Abstract, Introduction, Methods, Results, Discussion, References]
units:
disabled_rules: [range_dash] # if your journal insists on hyphens
Everything a document convention needs lives in YAML. If a new convention would need a code change, that's a bug in the checker, not a missing feature.
Checks
| check | flags | severity |
|---|---|---|
vale.* |
grammar, spelling, style — whatever your Vale config says | error/warning |
terminology.variant |
dataset where the config wants data set (opt-in: terms) |
warning |
acronym.undefined |
used without expansion at first use | warning |
structure.missing_section · .section_order |
required sections, absent or out of order (opt-in) | error/warning |
sentence_length.long |
sentences over max_words |
warning |
readability.flesch · .fog |
per section, skipping ones too short to score | warning |
units.space_before_unit · .percent_spacing · .range_dash · .p_value_spacing |
12 mm → NBSP, 5 % → 5%, 3-7 → 3–7, p<.05 → p < 0.05 |
warning |
claim.hedge · .booster |
"may possibly suggest", "clearly demonstrates" | review |
review is not a quieter warning. It is the line between what a script can decide and what
it cannot: hedging and overclaiming are judgment calls about whether your evidence carries
your claim. Nothing marked review is ever auto-applied, by either the tool or the agent.
Code, math, verbatim blocks, and LaTeX command sequences are blanked before any regex runs — character-for-character, so every offset stays a true source position. Your equations are not prose and are not judged as such.
Vale
Vale powers grammar and style and is pinned as a dependency — no
brew install, no manual step. The binary is fetched once on first run and cached; after
that, runs are offline.
Vale has no LaTeX reader, so a .tex file is handed to it as masked prose — commands,
preamble, math, and verbatim already blanked, line for line — and the alerts still carry true
source positions. Markdown and plain text go to Vale directly, parsed by its own reader.
If that download is blocked (air-gapped machine, proxy, locked-down CI), the run emits one
vale.unavailable warning and the other seven checkers still report. Vale is never a hard
prerequisite. Install it yourself and it's picked up from PATH:
brew install vale # or: apt install vale / scoop install vale
Develop
uv sync # from the lockfile
uv run prose-preflight text.md --md out.md
uv run pytest # all tests
uv run pytest tests/test_checks.py -k units
uv run ruff check . && uv run ruff format .
Every checker has a fixture pair in tests/fixtures/: an intentionally flawed document and
the exact set of (check, line) pairs it must produce. Pinning positions is deliberate —
false positives cost more here than misses, since each one burns agent tokens and spends
trust in the report.
Adding a checker is one module exposing check(doc, rule) -> list[Finding] plus one line in
the registry. If it also needs a change to the runner, the design was wrong.
License
MIT
Metadata
Release files for prose-preflight 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| prose_preflight-0.1.0.tar.gz | 55.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prose_preflight-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.5 kB
Release files / prose_preflight-0.1.0.tar.gz
| Download URL | prose_preflight-0.1.0.tar.gz |
|---|---|
| Size | 55.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1bef404871d3850baa784d18ff46e22c7fbfddefc6f412b69ff58464e5caf5e2
|
|
BLAKE2b-256 checksum How to use checksums |
05858cd2e4c179762d73140c21bcb1f97c8321d46835c0dc487dec7ab78ccbe6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / prose_preflight-0.1.0-py3-none-any.whl
| Download URL | prose_preflight-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0e2f4820a54f2dc3152c8d0318f6779f475ad85bf808c8f62be4e69889a10856
|
|
BLAKE2b-256 checksum How to use checksums |
3f6b7b60e7097e3543d6005edd9b045f40b4833317dd36db3cd5e4e238991c35
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log