Overview
A linter for lean English, before it reaches the commit.
Unswell checks the English in documentation, comments and string literals. It measures sentence length, readability, passive density and repetition, matches a catalog of formulaic phrasings, explains what to revise, and fails the build when your editorial policy says so. It runs offline as a Go CLI, library, GitHub Action and MCP server.
$ unswell check README.md docs/guide.md
README.md:3:1: warning [filler.announced-importance] State the fact without announcing its importance.
docs/guide.md:1:1: error [scaffold.chat-preamble] Start with the subject instead of a conversational acknowledgment.
FAIL: 2 documents, 2 findings; analysis complete.
gate: docs/guide.md: Start with the subject instead of a conversational acknowledgment.
Revision probability: unavailable (no calibrated alpha model).
$ echo $?
1Not an authorship detector. A finding is about the wording, whoever typed it.
A check, live
Section titled “A check, live”Two files went in. The first sentence of README.md announced its own importance
before stating a fact, so filler.announced-importance reported a warning and left
the gate alone. The first block of docs/guide.md opened with a chat
acknowledgment, and that rule was set to gate: forbid in the project policy, so
the run failed with exit code 1.
Every finding carries the original byte range and a one-based column, the rule that produced it, and a message that says what to change. Nothing left the machine: there is no service to call and no key to hold.
Why a linter
Section titled “Why a linter”Wording is a policy your project can state, argue about and version, so a linter is the place to put it. Unswell ships 40 rules with weights, caps and default severities, and a configuration file decides which of them your project cares about. The alpha index is an editorial-policy index from 0 to 100. It is not a probability, and no score claims to know who wrote anything.
What fires, and what does not
Section titled “What fires, and what does not”The 40 rules do not fire at one rate, and the difference is worth knowing before you run it. Measured over 81,690 paragraphs of documentation and comments from 259 repositories, under the strict profile:
| Rule | Paragraphs flagged |
|---|---|
syntax.long-sentence |
3.70% |
syntax.passive-candidate-density |
2.39% |
readability.grade-metric |
1.96% |
repetition.exact-sentence |
1.49% |
filler.wordy-phrase |
0.30% |
filler.announced-importance |
0.001% |
every scaffold. and hype. rule |
0% |
On prose someone wrote, the structural rules do the work. The phrase rules match exact wordings such as a chat acknowledgment or a stock opening, and an engineer writing documentation does not produce those, so they stay silent. They earn their keep on text pasted out of an assistant’s reply, which does contain them.
That is a deliberate trade. A phrase rule that fired on ordinary documentation
would be a phrase rule you turn off. Silence on clean prose is the correct
behavior for a linter, and it is why unswell check on a well-edited repository
reports long sentences and little else.
One finding, one revision
Section titled “One finding, one revision”Each rule reports evidence you can act on, not a verdict you have to accept.
filler.announced-importance matches the phrase and points at the sentence that
carries it:
It is important to note that the client retries a request only after a connection failure.The revision keeps the condition and drops the announcement:
The client retries a request only after a connection failure.When wording has to stay, a source comment permits that exact finding and records why. The evidence stays in the report, the permission is audited, and an unused permission fails the run rather than rotting in place.
Where it runs
Section titled “Where it runs”CLI
unswell check over paths or stdin, with text, JSON, SARIF, HTML and Markdown
reports from one analysis.
Go library
unswell.New and Engine.Analyze return the same result the CLI prints. The
library reads no configuration file of its own.
GitHub Action
A pinned release archive, verified by SHA-256, with JSON and SARIF paths as step outputs.
MCP server
unswell-mcp exposes the same engine over stdio, so an assistant can check a
draft against your policy before it proposes the change.
Inputs are plain text, Markdown, and the comments and string literals of Go, JavaScript, TypeScript, TSX, Python, Rust, Java, C, C++, C#, YAML, Bash, POSIX shell, Zsh, Fish and PowerShell. Code itself is never checked. See how it works for what extraction keeps and what it leaves out.