readme-gauge
v1.1.1
Published
Lint READMEs for missing sections, broken and stale badges, stale install commands, missing usage examples, broken relative links and anchors, placeholders and license mismatches. Checks JS code examples and imports, and the table of contents (--fix-toc).
Maintainers
Readme
readme-gauge
Lint READMEs for missing sections, broken badges, stale install commands and missing usage examples.
readme-gauge reads your README the way a new user does. It flags what is missing or wrong: no install or usage section, a usage section without a code example, an install command for a package that was renamed (npm i acme-widget when the package is acme-widgets), a pinned old major, a Travis badge for a service that shut down in 2021, an npm badge for a different package, a workflow badge pointing at a file that no longer exists, relative links and #anchors that go nowhere, placeholder text (TODO, lorem ipsum), and a License section that disagrees with package.json. Every finding has a line number and a concrete fix.
CLI, library and GitHub Action. Node.js 18+, one runtime dependency (static-link-sentinel, for the optional live link check). By Louis Wright.
Features
- Sections: required sections (
install,usage,licenseby default) found by heading text with sensible aliases ("Getting started", "Licence", "Examples"…), plus empty sections, a missing#title and skipped heading levels. - Install commands: parses
npm/pnpm/yarn/bun/npxcommands in code blocks and inline code. Flags the wrong or misspelled package name, pinned majors older thanpackage.json, global installs of packages without abin,--save, Bower, and a CLI that's never shown. - Badges: shields.io / npm / packagephobia / bundlephobia badges naming another package, GitHub badges for another repository, Actions badges for a workflow file that doesn't exist, dead services (Travis CI .org, David DM, Greenkeeper, Gemnasium).
- Links: relative link and image targets are checked on disk;
#anchorsare checked against GitHub's heading slugs (including duplicates and Unicode).--check-linksalso fetches external links and badge images using static-link-sentinel's checker (retries, per-host rate limits, optional--link-cache). - Output: text, JSON, SARIF 2.1.0 and Markdown;
--fail-on; exit codes 0/1/2; allowlist entries with expiry dates.
Rules
| Rule | Default severity | What it catches |
|---|---|---|
| readme-missing | high | No README file |
| missing-section | medium | A required section is missing |
| missing-usage-example | medium | No code example in the usage section |
| stale-install-command | medium | Install command names the wrong package or an old major version |
| broken-badge | medium | Badge points at another package/repo, a missing workflow or a dead service |
| broken-link | medium | Relative link or image target does not exist (or URL is unreachable with --check-links) |
| placeholder-text | medium | Placeholder text left in the README |
| broken-anchor | low | In-page #anchor does not match any heading |
| empty-section | low | Section heading with no content |
| license-mismatch | low | README license differs from package.json |
| cli-undocumented | low | Package has a CLI (bin) but the README never shows it |
| deprecated-install-flag | low | Install command uses a deprecated tool or flag |
| missing-title | low | README does not start with a level-1 title |
| too-short | low | README is very short |
| heading-level-skip | info | Heading level jumps (e.g. ## to ####) |
| insecure-url | info | http:// URL where https:// is available |
| allowlist-expired | medium | Allowlist entry has expired |
| allowlist-unused | info | Allowlist entry matched nothing |
Install and run
npx readme-gauge # lint ./README.md against ./package.json
npx readme-gauge packages/core --fail-on medium
npx readme-gauge --require install,usage,api,license,contributing
npx readme-gauge --check-links # also fetch external links and badgesSample output
readme-gauge — README.md
98 words, 6 heading(s), 4 badge(s), 7 link(s)
MEDIUM README.md:3 badge uses travis-ci.org (travis-ci.org shut down in 2021) [broken-badge]
↳ fix: Replace it with a GitHub Actions badge or remove it.
MEDIUM README.md:5 badge references workflow missing.yml, which does not exist in .github/workflows [broken-badge]
↳ fix: Point the badge at an existing workflow file.
MEDIUM README.md:15 "npm install --save [email protected]" installs "acme-widget" but this package is "acme-widgets" [stale-install-command]
↳ fix: Use "acme-widgets".
MEDIUM README.md:20 "Usage" section has no code example [missing-usage-example]
↳ fix: Add a fenced code block showing a minimal, copy-pasteable example.
LOW README.md:10 link to #options-list matches no heading [broken-anchor]
↳ fix: Fix the anchor or add the heading.Options
readme-gauge 1.0.0 — lint READMEs for missing sections, broken badges, stale install commands and missing examples
Usage: readme-gauge [dir] [options]
README
--file <path> README to lint (default: README.md / readme.* in dir)
--require <list> Required sections, comma-separated (default: install,usage,license;
known: install, usage, license, contributing, api, changelog, security, or any heading text)
--min-words <n> Minimum word count before too-short (default: 80)
--check-links Also fetch external links and badge images (HEAD, then GET)
--timeout <ms> Per-URL timeout for --check-links (default: 8000)
Common
-c, --config <file> JSON config (default: .readme-gauge.json or "readmeGauge" in package.json)
-f, --format <fmt> text (default) | json | sarif | markdown
--json <file> Also write a JSON report
--sarif <file> Also write SARIF 2.1.0
--markdown <file> Also write Markdown (e.g. for a job summary)
--fail-on <sev> critical | high | medium | low | info | none (default: high)
--list-rules Print rules and default severities
--no-fixes Hide fix suggestions in text output
--no-color Disable colours
-v, --version / -h, --help
Exit codes: 0 = passed, 1 = findings at or above --fail-on, 2 = usage, config or read error.Required section names can be the built-in keys (install, usage, license, contributing, api, changelog, security) or any heading text. Give your own aliases with "sections" in the config. URLs that block bots can be skipped in --check-links with "ignoreUrls": ["https://example.com/*"].
Configuration and allowlist
Options can live in .readme-gauge.json (or a "readmeGauge" key in package.json, or --config <file>):
{
"failOn": "medium",
"severity": { "heading-level-skip": "low" },
"allowlist": [
{ "rule": "stale-install-command", "match": "legacy-name", "reason": "Migration note for users of the old name", "expires": "2027-03-31" }
],
"require": ["install", "usage", "license", "contributing"],
"sections": { "usage": ["usage", "getting started", "examples"] }
}severitychanges a rule's severity, or turns it"off".allowlistentries suppress matching findings. Each needs areasonand matches on any combination ofrule,file,subject(globs with*) andmatch(a substring of the message). Addexpires(YYYY-MM-DD) so the exception is time-boxed: once it lapses the finding comes back along with anallowlist-expiredfinding (medium). Entries that no longer match anything are reported asallowlist-unused(info).
Output and exit codes
--format text(default),json,sarif(SARIF 2.1.0 for GitHub code scanning) ormarkdown(for job summaries and PR comments).--json,--sarifand--markdownwrite extra report files in the same run.--fail-on <severity>sets the threshold (critical,high,medium,low,info,none; defaulthigh).- Exit codes: 0 passed, 1 findings at or above
--fail-on, 2 usage, config or read error.
GitHub Action
permissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
readme-gauge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with: { persist-credentials: false }
- uses: MrLouisWright/readme-gauge@v1
with:
fail-on: high
sarif: readme-gauge.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: readme-gauge.sarif }| Input | Description | Default |
|---|---|---|
| path | Project directory | . |
| require | Comma-separated required sections (default: install,usage,license) | `` |
| check-links | Also fetch external badge and link URLs (true/false) | false |
| fail-on | Failure threshold | high |
| config | Config file path | |
| sarif / json | Write SARIF / JSON report to this path | |
| job-summary | Add the Markdown report to the job summary | true |
| args | Extra CLI arguments | |
The action runs the bundled CLI with Node 20; it has no dependencies to install. Pin it to a commit SHA if your policy requires it.
Library
import { check, formatMarkdown } from 'readme-gauge';
const report = await check('.', { require: ['install', 'usage', 'license'] });
console.log(formatMarkdown(report, { failOn: 'medium' }));scan (headings, sections, code blocks, links), slug (GitHub anchors) and installCommands are exported too. Types are included.
Requirements
Node.js 18 or newer. One runtime dependency: static-link-sentinel (live link checks).
Support
If this project is useful to you, you can support its development with a donation: Buy me a coffee. It helps keep these tools maintained, documented and free for everyone.
Credit (optional)
Free to use under MIT, no attribution required. If readme-gauge helps you, a link back is appreciated:
<a href="https://louiswright.co.uk">Built with readme-gauge by Louis Wright</a>[](https://louiswright.co.uk)Licence
MIT © Louis Wright (Louis Wright)
Code examples and table of contents
```js
import { check } from 'readme-gauge'; // "readme-gauge" must be exported; the snippet must parse
```example-syntax-error: ajs/mjs/cjsblock does not parse. Snippets are compiled, never run; ESM and top-levelawaitare fine, shell sessions are skipped.example-import-missing: an example importsyour-package/subbutexportshas no./sub. With--check-exportsthe package entry is loaded and every named import is checked as well (build first).toc-out-of-date: the TOC (between<!-- toc -->and<!-- tocstop -->, or a "Contents" section) lists a heading that no longer exists, or misses one.npx readme-gauge --fix-tocrewrites it, or inserts a "Contents" section with markers.
Live link checks (--check-links) use static-link-sentinel's checker, with retries, per-host rate limits and an optional --link-cache <file>. For whole-site link checking, use static-link-sentinel directly.
Using with markdownlint
readme-gauge checks README content; leave formatting (line length, list style, spacing) to markdownlint-cli2. Turn off the rules both tools cover so each problem is reported once:
{ "default": true, "MD001": false, "MD041": false, "MD051": false, "MD013": false }Save it as .markdownlint.json (it is also exported as MARKDOWNLINT_CONFIG), then run npx markdownlint-cli2 README.md && npx readme-gauge.
Module formats and types
Ships ESM (import) and a bundled CommonJS build (require), with TypeScript declarations generated from the source:
import * as lib from 'readme-gauge'; // ESM
const lib = require('readme-gauge'); // CommonJSTests live in test/ and run on Node 18, 20 and 22 in GitHub Actions, including a require+import check. Releases are published with npm publish --provenance.
