Deterministic scoring for prose.
Agents close the loop on code with compilers, linters, and tests. Prose has no such signal.
prosemeter is one. Give it a document and a named profile. It returns a score, 15 per-dimension scores, and findings with line numbers and fix hints — enough to revise, measure, and know when to stop.
$ prosemeter score chat-jargon.md --profile chat
chat-jargon.md 49/100 (profile: chat)
Dimensions
grade-band 2 median grade 25.8 vs band 7–12 (FK 25…
sentence-simplicity 26 7 hard sentences / 194 words
clarity 45 4 wordy phrase(s) / 194 words
directness 71 2 weasel/hedge word(s) / 194 words
active-voice 100 0 passive construction(s) / 194 words
… 10 more dimensions
Findings
chat-jargon.md:1:1 warn Sentence reads at ~grade 23, above…
→ 26 words, grade ~23 — split or simplify.
… 12 moreThe same answer, two registers
Both documents answer the same question with the same fix. One scores 92 on the chatprofile. The other scores 49. Toggle between them and watch which dimensions move.
A correct answer nobody wants to read. Every idea is right; the register buries all of them.
49/100·floor 75below·194 words·v0.4.4
| grade-band | 2 | median grade 25.8 vs band 7–12 (FK 25.8, Fog 30.5, SMOG 24.1, CL 25.1, ARI 26.0); Flesch Reading Ease -40.2 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sentence-simplicity | 26 | 7 hard sentences / 194 words | |||||||||||||||
| clarity | 45 | 4 wordy phrase(s) / 194 words | |||||||||||||||
| lexical-diversity | 31 | MTLD 195.1 vs band 50–120 | |||||||||||||||
| directness | 71 | 2 weasel/hedge word(s) / 194 words | |||||||||||||||
| sentence-variety | 80 | sentence-length variation 0.22 vs band 0.4–0.9 | |||||||||||||||
5 dimensions at 100
| |||||||||||||||||
| heading-hierarchy | — | disabled by profile | |||||||||||||||
| section-length | — | disabled by profile | |||||||||||||||
| document-balance | — | disabled by profile | |||||||||||||||
| acronym-definition | — | disabled by profile | |||||||||||||||
Findings
sentence-simplicity
clarity
directness
Try another document
Scored in your browser. Nothing was uploaded, and there is no model in the loop.
A score is not enough
A linter says what is wrong. It does not say whether the rewrite helped, or when to stop rewriting.
Those are the two questions that make a revision loop terminate, and prosemeter answers both.compareBaseline diffs a result against the previous one and reports which findings resolved and which are new. checkConvergence reads a score history and returns one of five verdicts.
const result = score(draft, { profile: "readme" })
const delta = compareBaseline(current, previous)
const verdict = checkConvergence([62, 71, 74, 74.5], { threshold: 80 })
// "improving" | "plateaued" | "oscillating" | "regressing" | "converged"The distinct stop reasons let a harness attach policy. regressing means revert to the highest-scoring earlier draft rather than push the latest one further.
You cannot buy the score by shortening sentences
The obvious way to game a readability metric is to chop every sentence. Here is what that gets you.
The cat sat. The cat is big. The cat is red. It ran. It ran fast.
Perfect on 100 for sentence-simplicity, 100 for clarity, and 100 for concision. It still scores 71 and falls below the chat floor of 75.
grade-band is the reason. It is a band, not a goal — full marks inside the target range, falling off on either side. This reads at grade -2.1 against a floor of 7, so the dimension carrying the profile's largest weight scores 5.
The design generalizes. Targets are ranges, so there is no single number to run away with, and anything gamed by chopping sentences is counterweighted by something that punishes it.
$ prosemeter score choppy-simplistic.md --profile chat
choppy-simplistic.md 71/100 (profile: chat)
Dimensions
grade-band 5 median grade -2.1 vs band 7–12 (FK -2…
sentence-simplicity 100 0 hard sentences / 90 words
clarity 100 0 wordy phrase(s) / 90 words
concision 100 0 concision issues (repeats, redundan…
… 11 more dimensions
Findings
choppy-simplistic.md:4:1 warn Unexpected weasel (vague or a…
→ If the claim holds only under a condition, name the condi…
… 4 moreMeasured, not asserted
236 scored answers across three runs on claude-opus-5, plus a Sonnet transfer check.
A style instruction against a no-instruction control on the same six tasks: 438 → 298 words, jargon 10.7% → 8.7%, sentence-simplicity 50.2 → 69.9.
n=90 · 2026-08-03
“Be concise. Avoid jargon. Write clearly.” reaches 350 words and 10.3% jargon. Telling a model which words to prefer moved jargon; telling it to avoid jargon did not.
Two runs and one targeted mechanism test, no effect. The instruction written to fix an observed error failed on that exact error in 4 of 5 replicates.
Across seven variants the composite spanned 79.5–86.6 while the spread within one variant was about 15 points. The dimensions moved 2–3x over the same data.
One engine, four ways in
The library is the whole thing. The rest are thin wrappers so an agent or a CI job does not have to be one.
TypeScript
ESM, typed, returns an Either so errors stay explicit.
pnpm add prosemeterCommand line
Exits non-zero below a threshold, so CI can gate on it.
prosemeter score README.mdMCP server
Five tools over stdio. Each description teaches the loop.
npx @prosemeter/mcpClaude Code
The server plus a skill that says when to stop revising.
/plugin install prosemeter@prosemeterWhat it is not
A deterministic prose metric is a proxy, and any agent optimizing a proxy will find its seams.
The bands blunt the worst of that. The honest framing matters more than the guardrail: prosemeter is a floor and a loop terminator, not a quality oracle. It tells an agent whether a draft cleared the objective bar and when to stop iterating. It does not tell you the prose is good.
It also cannot check facts. Two eval runs and one targeted mechanism test found that style instructions move length and vocabulary by 2–3x and move factual accuracy not at all. A high score is not a correctness signal.
And it measures how a document reads, not whether it makes sense to a particular reader — a gap we have measured and not yet closed →
prosemeter 0.4.4. Every score on this page was computed at build time.