Skip to main content
Change of provenance

The repository or configuration that created this release differ from a previous release. View details.

words-for-humans

PyPI CI License: MIT

Most new code is written by an assistant and reviewed by a person. The code gets tests, types, and CI. The prose around it gets nothing, and the prose is what the reviewer reads first. words-for-humans checks that prose: comments, docstrings, Markdown, user-facing strings, and pull request descriptions, so generated changes cost a reviewer less attention to digest.

Your assistant writes comments like this:

// This function is responsible for calculating the total price of all the items
// that are present in the cart. It's worth noting that we will iterate over each
// of the items in order to accumulate the sum of their individual prices.
export function calculateCartTotal(items: CartItem[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

Three lines that tell a reader nothing the signature does not. Multiply that across a year of generated pull requests and the comments stop being read at all, which costs you the ones that mattered.

It also writes comments like this:

// Added retry per code review feedback. See JIRA-1234.
await retryWithBackoff(() => client.send(payload));

The review is not in the file, and the ticket will not always be reachable. The next reader gets a comment about a conversation they were not part of, instead of the reason the code is shaped this way.

words-for-humans finds both:

slop_demo.ts
  1:    error [V-4] "This function is responsible for" restates the declaration below.
        → Start with the verb: what it does, or why it exists.
  2:    error [V-2] "It's worth noting that" adds nothing to the sentence.
        → Delete it and start with the statement.
  8:    warn  [S-1] "Added retry per code review feedback" narrates how the text came to be.
        → State the reason the code or fact holds today. The history lives in version control.

And it leaves this one alone, because it says something the code cannot, and says it to a reader with no context beyond the file:

// Retry with backoff because the upstream rate limiter returns 429 in bursts
// after a deploy, and a flat retry makes the burst worse.
await retryWithBackoff(() => client.send(payload));

The four questions

Every piece of prose is held to four questions, one rule family per question:

Family Question
V- Does it say anything the code does not?
S- Does it stand alone, with only the file in front of the reader?
A- Does it sound like a person wrote it?
C- Does it read easily?

A fifth family, D-, asks the same kind of question of the code itself. Its rules come from A Philosophy of Software Design and Clean Code, and it runs in the GitHub App's model pass.

Standing alone is the question generated text fails most often. An assistant writes from the conversation that produced the change, so its prose leans on that conversation. The signals are lexical: a reason replaced by history, a comment that describes the edit, a ticket number in place of an explanation, a relative name with no anchor. Each of those reads fine in the pull request and means nothing a month later. The S- rules hold prose to one bar: a reader with zero background gets full value, without the review, the ticket, or the chat thread. References are welcome as citations that add depth, never as dependencies that gate understanding.

docs/standalone-rules.md records the design and the published authority behind each S- rule.

Sounding human is the question a prompt cannot settle. Asking an assistant for shorter comments works some of the time; the habits come from training, so they come back a few files later. The A- family catches the shapes that survive the prompt: definition by negation, praise adjectives, announced lists of benefits, stacked hedges, and decorative emoji. It also catches placeholder credentials, step narration, the em dash, a pleasantry before the first sentence, and an announced summary at the end. One of them matters beyond style: A-5 catches the comment that presents the code as a stand-in for the version somebody still has to write. That comment marks unfinished work, reads like documentation, and ships.

Two tiers do the checking. Rules a pattern can settle run in this package, free and offline. Rules that need a reader who understands what the text means run as model calls, through the judgment tier or the bundled words-for-humans-review skill. Whether a reference gates comprehension, and whether a comment earns its length, are questions of that kind. docs/detection.md covers the tiers and how the classifier tier is trained.

Install

uv tool install words-for-humans

Or run it without installing:

uvx words-for-humans

docs/installation.md walks a repository through full adoption: install, config, baseline, pre-commit, CI, and the review skill.

Use

words-for-humans                             # the whole repository
words-for-humans --staged                    # what you are about to commit
words-for-humans --diff origin/main...HEAD   # what a pull request changes
words-for-humans --stdout                    # print every finding, do not write a file
words-for-humans --format json               # for a script
words-for-humans --format html . > report.html # a page to send someone
words-for-humans --init                      # write a starter config
words-for-humans --list-rules                # every rule, its slug, and which half checks it

A run over the whole repository prints the summary to the terminal and writes the findings themselves to words-for-humans-report.txt. The counts that say where to start are then not buried under thousands of lines. A changed-files run (--staged, --diff) is small, so it prints in place. --stdout prints everything to the terminal instead. Add the report file to .gitignore.

Findings from rules with a fixed threshold or a closed set of phrases fail the run. Findings that depend on guessing a part of speech are reported as warnings and do not affect the exit code.

Adopting it in an existing repository

The first run against a codebase written before the tool will report a great deal. Record it and move on:

words-for-humans --write-baseline

Everything found so far stays visible in reports but stops failing the build. Only new findings can fail from then on.

The report

--format html writes a single self-contained page. It leads with the count of findings. Below that it shows the clearest examples, each with its replacement, then lists every rule that fired. No external assets, so it survives being saved to disk and forwarded.

words-for-humans --format html . > report.html

The page is for showing someone the tool run against their own repository, before they install anything.

Profiles

A profile is a named configuration of the rule set: which rules report, which fail, and what sentence lengths apply. Pick the preset that names what you are checking, and assign others to parts of the tree with [paths].

words-for-humans --profile code   # default

Strictest first:

Profile Genre What changes
ste Aerospace-grade technical writing The whole ASD-STE100 standard, enforced, with its strict sentence limits.
code Comments, docstrings, strings The default. Fails on prose that says nothing. Dialect rules are silent.
prose-technical READMEs, specs, design docs The code contract, named for the genre.
changelog Changelogs, release notes History narration is the genre, so the standalone rules stay out. Common changelog paths land here on their own.
prose-corporate Plans, reviews, briefs Prose sentences up to 35/50 words (warn/fail). Emoji allowed. Stated intent ("we will") warns rather than fails.
comms-external Announcements, customer-facing text As corporate, but praise adjectives warn instead of fail.
comms-internal Internal notes Sentences up to 40/60. Emoji allowed, em dashes warn.
minimal Diagnosis Nothing fails. Reports everything so you can see the shape first.

Empty prose, placeholder values, and the tells of generated text fail under every genre profile. concise, the old name of the default, still works as an alias of code. The comms-* pair is provisional: no corpus has tuned it yet.

Configure

words-for-humans --init writes a starter .words-for-humans.toml in the repository root. The settings can also live in a [tool.words-for-humans] table in pyproject.toml:

profile = "code"
scopes = ["comment", "docstring", "markdown", "string"]
exclude = ["vendor/**", "**/generated/**"]
disable = ["GR-8"]

[severity]
"C-3" = "hard"   # make passive voice fail the build
"V-5" = "soft"   # report padding without failing

[paths]
"docs/**" = "prose-technical"
"planning/**" = "prose-corporate"

One repository usually holds more than one genre, so the [paths] table maps directories to profiles. The last matching pattern wins, unmatched files use the top-level profile, and --profile on the command line overrides the whole table. Entries in [severity] stay in force whichever profile a path lands in.

Inside a git repository, discovery lists files with git, so anything in .gitignore is skipped. A vendored copy that is committed is still tracked, so .gitignore does not exclude it; add its path to exclude. To scan every file on disk, including ignored ones, set respect_gitignore = false or pass --no-gitignore. The report file path is report = "words-for-humans-report.txt" by default.

To silence one place rather than one rule, put the escape hatch in the comment:

# words-for-humans: ignore
# words-for-humans: ignore C-1, V-2

Pre-commit

The repository declares the hook in .pre-commit-hooks.yaml, so point pre-commit at it:

repos:
  - repo: https://github.com/superhelpful-ai/words-for-humans
    rev: v0.3.1
    hooks:
      - id: words-for-humans

Without pre-commit, a plain git hook that does the same is in docs/installation.md. Inside this repository, ./hooks/install.sh installs it directly.

GitHub

Run the check in your own Actions workflow. It checks only what the diff adds, so an author is never shown the repository's existing debt as though it were theirs:

- run: uvx words-for-humans --diff origin/${{ github.base_ref }}...HEAD --format github

docs/ci.md has full recipes for GitHub Actions, Buildkite, and GitLab, and a one-step composite action.

The hosted App

The words-for-humans GitHub App reviews pull requests without a workflow file. It annotates only the lines the diff adds, and it can gate the merge as a required check.

The mechanical rules are free. Most of the catalogue needs judgment, and those rules run when the account supplies its own Anthropic API key. The CLI draws the same line: the judgment tier runs only with a provider token. A paid plug-and-play tier is coming. wordsforhumans.io has the details.

Where the rules come from

The tool began as a checker for ASD-STE100 Simplified Technical English, the controlled English specification the aerospace and defence industries have used for technical documentation since 1986. The standard earned its keep in one place: its clarity rules, which hold for any reader. Those became the C- family: sentence and paragraph length, passive voice, noun clusters, slang, ambiguous pronouns, inclusive language.

The rest of the standard is a controlled dialect for aerospace maintenance writing, and mainstream clarity authorities prescribe plain language instead. So the catalogue keeps the ASD-STE100 sections under their spec numbering (3.4, GR-6) as a deprecated appendix: only the ste profile reports them, and the next major release removes them. words-for-humans --list-rules prints the whole catalogue, each rule with a slug that names what it checks (C-1 sentence-length, S-1 history-narration, V-1 says-nothing). Five counting rules define how words are tallied rather than reporting anything. A parenthesised aside counts as one word, and so do quoted text, a number with its unit, and a hyphenated compound.

The S- family stands on published authority outside the standard: the Federal Plain Language Guidelines on cross-references, Google's timeless-documentation rule, and GitLab's low-context communication guidance. docs/standalone-rules.md cites each.

The dictionary

ASD publishes the specification free of charge but owns the copyright, so the controlled vocabulary of roughly 900 approved words is not in this repository and never will be.

The tool ships a starter list of 22 words that the standard does not approve, chosen because they turn up in developer prose. Only the ste profile leans on the vocabulary; the genre profiles do not report dictionary conformance.

For the full dictionary, extract it from your own copy of the specification:

uv tool install 'words-for-humans[dictionary]'
words-for-humans-extract-dictionary ~/Downloads/ASD-STE100_ISSUE9.pdf

The result lands in .words-for-humans/dictionary.json, which is gitignored. words-for-humans --sync-dictionary will download it for an account instead; accounts are not yet open.

Layout

src/words_for_humans/     the engine, the command, and the review skill
tests/                    the test suite
action.yml                the composite GitHub Action
hooks/                    git hook installer for this repository

Naming

This project is not affiliated with or endorsed by ASD. ASD-STE100 and Simplified Technical English are ASD's. The tool checks text against the standard; it does not represent the standard.

Metadata

Release files for words-for-humans 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Change of provenance

The repository or configuration that created these release files differ from that of a previous release.

We recommend inspecting the repository and configuration before proceeding. View details.

Source distribution (sdist)

Source distribution for words-for-humans 0.3.1
File Size Uploaded Provenance Actions
words_for_humans-0.3.1.tar.gz 108.4 kB With provenance Details for words_for_humans-0.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for words-for-humans 0.3.1
File Interpreter ABI Platform Actions
words_for_humans-0.3.1-py3-none-any.whl Python 3 none any Details for words_for_humans-0.3.1-py3-none-any.whl

Total release size: 220.2 kB

Release files / words_for_humans-0.3.1.tar.gz

Change of provenance

This file belongs to a release with changed provenance: the repository or configuration that created the release files differ from that of a previous release.

We recommend inspecting the repository and configuration before proceeding. View details.

Download URL words_for_humans-0.3.1.tar.gz
Size 108.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d92428e51734f3ab82875f4a8eebf5af777ed2c1bbce19a81e3aabb95794b138
BLAKE2b-256 checksum
How to use checksums
8260c2417533091f95eee6afed0091afbcc1347a18f80c677143bfff8db92bf3
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 Aug 29, 2026.

Transparency log

Release files / words_for_humans-0.3.1-py3-none-any.whl

Change of provenance

This file belongs to a release with changed provenance: the repository or configuration that created the release files differ from that of a previous release.

We recommend inspecting the repository and configuration before proceeding. View details.

Download URL words_for_humans-0.3.1-py3-none-any.whl
Size 111.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
398b4f4acbd95f992d12edd3825908733d36a715917be14c5a0a4a4725faf42e
BLAKE2b-256 checksum
How to use checksums
99b096bef9f78907475f9cdfee7595d7eb5a9b1d8e8adf2e6da241678acb45f9
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 Aug 29, 2026.

Transparency log

Security for words-for-humans 0.3.1

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.

Change of provenance

The repository and configuration that created this release differ from a previous release.

While this could reflect a routine change by the project maintainers, it may also indicate that the release files have been compromised.

Select from 2 files

Attestation for words_for_humans-0.3.1.tar.gz

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 Aug 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page