Skip to main content

stylint

Voice, formatting, and code-style rules for technical write-ups, plus a mechanical checker that catches the worst offenders.

Install

uv tool install stylint

Or from a local clone:

git clone https://github.com/alexeygrigorev/stylint ~/git/stylint
uv tool install --from ~/git/stylint stylint

Then run:

stylint docs/
stylint --list-tags
stylint --ignore tables docs/
stylint --style-guide
stylint --print-style-guide voice

What's here

  • style-guide/voice.md - tone, voice rules, content rules (include/leave out).
  • style-guide/formatting.md - markdown mechanics: headings, code blocks, lists, links, asides.
  • style-guide/code-style.md - educational code style inside example blocks (no defensive try/except, no chained .get(), one blank line between definitions).
  • style-guide/polish.md - judgment-level prose patterns the script cannot detect (plain words over abstractions, banned-but-context-sensitive words).
  • The same style guide docs are bundled in the Python package. Run stylint --style-guide to print their installed paths, or stylint --print-style-guide voice to print one document.
  • stylint/ - mechanical checker package. Edit BANNED_WORDS, BANNED_PHRASES, BANNED_OPENERS in stylint/patterns.py to extend the enforced list.
  • check_style.py - compatibility wrapper for direct script usage.

Run the checker

Scan the current directory:

stylint

Scan one file or a specific folder:

stylint path/to/post.md
stylint docs/

Suppress specific rules with --ignore:

stylint --ignore tables docs/
stylint --ignore tables,long-clause-likely docs/
stylint --list-tags

To skip files, drop a .prose-style-ignore at the scan root, one fnmatch pattern per line:

# build outputs
_site/*
node_modules/*
# generated docs
docs/api/*

Rule tags

Every finding starts with a [tag] so you know which rule fired and can pass that tag to --ignore.

Markdown mechanics

  • bold - **text** or __text__ used for emphasis.
  • italic - *text* or _text_ used for emphasis.
  • tables - GFM table row (a line starting with |).
  • hr - horizontal rule --- on its own line.
  • em-dash - the em-dash character —.
  • double-hyphen - -- used as a substitute for an em dash.
  • dash-parenthetical - sentence with two - dashes inserting a parenthetical.
  • smart-quotes - curly quotes ‘, ’, “, ”.
  • backticks-in-link - link text wrapped in backticks like [`file.py`](url).
  • bare-url - raw URL in prose like see https://example.com.
  • angle-url - URL wrapped in angle brackets like <https://...>.
  • frontmatter-blank - missing blank line between YAML frontmatter closing --- and the body.

Headings

  • heading-question-word - heading starts with Why, How, What, etc.
  • heading-question-mark - heading ends with ?.
  • heading-too-deep - heading depth ### or deeper.
  • lazy-heading - heading like ## The problem, ## The RAG idea, ## The challenge.

Code blocks

  • code-no-lang - fenced code block with no language tag.
  • code-too-long - code block over 40 lines.
  • consecutive-code - two code blocks back-to-back with no prose between them.
  • lead-in - code block or list directly under a heading with no lead-in sentence.
  • lead-in-multi - the paragraph that introduces a list or code block has multiple sentences and ends with :. The lead-in sentence should be its own one-sentence paragraph.
  • chained-get - chained .get(...).get(...) in example Python.
  • double-blank - two blank lines between Python definitions.

Banned tokens (extend in stylint/patterns.py)

  • banned-word - single word on the BANNED_WORDS list (e.g. delve, leverage, crucial).
  • banned-phrase - multi-word phrase on the BANNED_PHRASES list (e.g. the power of, serves as, in action, the backbone of).
  • banned-opener - sentence-start word on the BANNED_OPENERS list (e.g. Additionally, Moreover).

Voice / fragments

  • third-person - the string Alexey appears in prose (third-person presenter reference).
  • anaphoric-no - sentence-start No X, no Y verbless fragment (e.g. No frameworks, no magic).
  • semicolon - any ; in prose.
  • cleft - pointless [This/That/It] is what X is about cleft.
  • gerund-opener - sentence opens with an -ing participial phrase (e.g. Reading through it, I noticed...).

Paragraph / sentence shape

  • paragraph-too-long - paragraph with more than 5 sentences.
  • long-sentence - sentence over 20 words, no commas.
  • long-list-likely - sentence over 20 words with commas where a colon precedes the commas or the sentence closes with and X / or X over 3+ chunks. The commas look like enumeration. Fix: convert to a bullet list.
  • long-clause-likely - sentence over 20 words with commas where the commas look like clause boundaries (subordinating conjunctions like which, that, while, because, but, however, so, when, or chains of actions with mixed subjects). Fix: split into 2-3 shorter sentences. Do NOT bullet — it would break the meaning.
  • many-commas - sentence with more than 3 commas.
  • colon-inline - colon followed by 3+ comma-separated items and a terminal and/or (likely should be a bullet list).
  • parallel-sentences - 3+ adjacent sentences sharing the same 1-2 word opener (e.g. Maybe it should... Maybe it should...).
  • choppy-rhythm - 3+ adjacent sentences of 9 words or fewer. The staccato rhythm reads worse than one or two joined sentences. Fix: combine two of them with a conjunction ("so", "because", "and", "but") or restructure as a single longer sentence.
  • repeated-and - polysyndetic chain ("A and B and C") of 3+ items joined by and instead of the oxford comma form. Fix: use commas with and only between the last two items. Idioms like "more and more" or "date and time" only fire when embedded in a longer chain ("X and more and more Y"); standalone uses don't trigger the rule.
  • meta-framing - sentence has the shape [The/A/Another] <abstract noun> [of X] is that <claim> (e.g. A big advantage of Recorder is that it keeps recording, Another limitation is that..., The key insight is that...). Writer announces the shape of the claim instead of stating it. Fix: drop the framing and lead with the claim.
  • label-colon - paragraph opens with The problem:, Goal:, What we want: followed by prose. Note: and Important: are exempt as callouts.
  • question-opener - paragraph opens with a rhetorical question (Why do we need X?).

File-level

  • now-lets-overuse - file uses more than 3 sentence-starting Now/Let's openers total. Vary openers or use the bare imperative.
  • now-lets-combo - sentence starts with the redundant pair Now let's... or Let's now.... Drop both softeners and use the bare imperative.

Reading order for a write-up

Run stylint --style-guide to locate the installed copies when you consume Stylint as a dependency. Run stylint --print-style-guide NAME to print one document directly.

  1. voice.md while drafting.
  2. formatting.md for any markdown syntax question.
  3. code-style.md for example code blocks.
  4. Run stylint and fix every reported finding.
  5. polish.md for a final judgment-level pass.

Hooking it into a project

The simplest setup is a pre-commit hook. From the target project root:

ln -s ~/git/stylint/check_style.py .git/hooks/post-add-style-check

Or run it from CI:

- run: stylint docs/

Notes on what's enforced vs. what's judgment

The script catches every rule listed in the "Rule tags" section above. Each error message includes the tag, a numbered list of common fixes, and (for list-conversion rules) the inline heuristic for deciding whether a bullet list is right.

Everything else (tone, voice, abstractions, "explain why", source material handling) is in the reference docs and needs a human or agent to apply judgment.

Metadata

Release files for stylint 0.1.1

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

Source distribution (sdist)

Source distribution for stylint 0.1.1
File Size Uploaded Provenance
stylint-0.1.1.tar.gz 49.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for stylint 0.1.1
File Interpreter ABI Platform
stylint-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 97.0 kB

Release files / stylint-0.1.1.tar.gz

Download URL stylint-0.1.1.tar.gz
Size 49.5 kB
Tags Source
SHA-256 checksum
How to use checksums
07429af279f9dc383199000fcfeece121ed3a6f84d1d44c0dcf8727319bc481c
BLAKE2b-256 checksum
How to use checksums
39ce1c663b90c2c5050934cb360c44c99fea48659b4675ecc96502805de5e21e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.16.5 cpython/3.14.3 HTTPX/0.28.1

Release files / stylint-0.1.1-py3-none-any.whl

Download URL stylint-0.1.1-py3-none-any.whl
Size 47.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c6ba5a9f94de829c79b21349db52226576d0f109017b597b6ac3505342871eb0
BLAKE2b-256 checksum
How to use checksums
0b255a402b77089be075adc0970e7684ab5706ed856d928ec976394e548e486e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.16.5 cpython/3.14.3 HTTPX/0.28.1

Security for stylint 0.1.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

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