README Generator from Your Project

Your manifest, your scripts, your README.

  • Reads your manifest
  • Links checked
  • Keeps your prose
A small rounded open notebook on a desk beside a soft folder with a few blank cards peeking out, in amber and teal

The guide

A README Is a Promise About One Folder, and the Folder Can Check It

Why the commands, links and licence in a README must match the manifest, the lockfile and the files beside it, and how to keep them matching as the project moves.

  • mid-1970s when the README convention began
  • 2004 the year Markdown was created
  • 6 levels ATX headings, from one to six hash marks
  • 740 ids on SPDX License List 3.29.0, 32 of them deprecated
  • AND, OR, WITH the operators of an SPDX licence expression
Read the full guide Fold the guide 8 min · 20 sources

A README is the first file a stranger opens and the last file anyone updates. That gap is the whole problem. The text was true on the day it was written, and then the project kept moving: a script was renamed, a folder of screenshots was tidied away, the team switched package managers, a licence was changed in one place and not the other. Nobody lied. The folder simply stopped agreeing with its own front page.

The habit is old. The convention of shipping a file called README began in the mid-1970s, and on Unix the name was written in capitals so that it would stand out and sort near the top of a directory listing. The job has not changed since then. It tells a newcomer what the directory holds and what to do first, before they open anything else.

Who reads it first, and what they do next

The first reader is usually in a hurry and about to type something. They want three answers: what is this, how do I install it, and how do I run it. Everything else in a README, from the history of the project to the list of contributors, is read later or not at all. That ordering matters, because the three answers people act on are also the three that go stale fastest.

The most popular code hosting site shows the README on the front page of a repository, and the Cargo Book notes that the Rust package registry renders a crate’s README as Markdown on its page. So the same file serves a person browsing in a web page and a person who has just cloned the code into a terminal. The first reads it; the second copies from it. A command copied from a README is a test of that README, run by someone who did not write it.

Most READMEs today are written in Markdown, which John Gruber created in 2004 as a plain-text format that reads well before it is converted to HTML. The first description left questions open, implementations drifted apart, and in 2014 a group of long-standing contributors released CommonMark, a precise specification with a test suite. That precision is useful here: when a file is read by the CommonMark rules, there is one right answer to whether a line is a heading.

Take a line that starts with a hash mark inside a block of shell commands. In a shell it is a comment. In Markdown it would be a level-one heading, except that it sits inside a fenced code block, and the specification says everything inside a fence is literal code. A fence opens with at least three backticks or tildes and closes with a line of the same character at least as long. If no closing fence comes, the code block runs to the end of the document. One forgotten closing line can swallow every section below it, and the page still renders without a single error message.

Headings carry their own rule, but it comes from the web rather than from Markdown. ATX headings come in 6 levels, from one hash mark to six, and they become the outline of the rendered page. The heading guidance on MDN asks authors not to skip levels, because people using screen readers jump from heading to heading to learn the shape of a page, and a jump from level one straight to level three suggests something is missing.

A README that says to run an install command is making a claim about the lockfile. Each package manager writes its own: npm writes package-lock.json, pnpm writes pnpm-lock.yaml, Yarn writes yarn.lock, uv writes uv.lock next to pyproject.toml, Poetry writes poetry.lock, and Cargo writes Cargo.lock. The Yarn documentation says its lockfile should always be kept in the repository, even for a library; the uv documentation says uv.lock should be checked into version control, and Poetry advises application developers to commit poetry.lock. The lockfile in the folder is therefore the best evidence of which tool the project really uses.

The difference is not cosmetic. The npm documentation describes npm ci as a clean install for automated settings: it requires an existing package-lock.json, exits with an error when that file disagrees with package.json, and never writes to either. pnpm, in a continuous integration setting, refuses to install when the lockfile needs an update. A README that says npm install in a folder that holds pnpm-lock.yaml sends the reader down a path that ignores the lock the maintainers rely on.

Run commands make a similar claim about the manifest. In a Node project, scripts live under the “scripts” key of package.json, and npm run followed by a name runs the entry of that name. If the README says to run a script the manifest does not define, the first thing a newcomer sees is an error. There are small exceptions worth knowing: npm start falls back to running node server.js when no start script exists. In Python, the [project.scripts] table of pyproject.toml declares console commands. In Rust, Cargo finds a binary in src/main.rs or src/bin, and cargo run --bin picks one by name.

Links fail more quietly. A relative link to docs/usage.md or an image at docs/screenshot.png is a claim that the file exists at that path. Rename a folder and every such link breaks, yet nothing in the build notices, because Markdown does not check paths. Links to a heading in the same file, such as a table of contents, depend on how a site turns heading text into an anchor, which CommonMark does not define. The tool on this page uses a widespread convention, labelled as one: lowercase the text, drop punctuation, turn spaces into hyphens and number repeats.

Each of these failures is invisible to the author, who already knows the right command and the real path. Only a newcomer trips over them.

A worked example shows how these claims pile up. Open the sample project in the tool above. Its package.json names the package sample-notes-cli at version 0.4.1, defines four scripts (build, test, lint and start), and says the licence is MIT. The folder holds pnpm-lock.yaml. Its old README jumps from a level-one title straight to a level-three heading, tells you to run npm install, mentions npm run serve, shows docs/screenshot.png, links to a heading called options that does not exist, and leaves its last code fence open. The LICENSE file holds the ISC text.

The tool reports four errors and three warnings, each with its line. The README it writes instead installs with pnpm install, lists pnpm run build, pnpm run test, pnpm run lint and pnpm run start, and passes the same checks with no problems.

Licences and how they are named

Licences are where a mismatch costs the most, because a reader may rely on what the README says. The manifests have converged on one naming scheme. The npm documentation asks for an SPDX licence identifier in package.json, or the string “SEE LICENSE IN” followed by a file name for a custom licence. The Cargo Book says the license field is read as an SPDX 2.3 expression and notes that the older slash between licences is deprecated. The Python packaging specification now takes the license key of pyproject.toml as an SPDX expression too, and calls the older table form deprecated.

An SPDX expression is small but strict. Annex D of the SPDX specification defines it: licence identifiers from the SPDX License List, an optional plus sign meaning a version or any later one, user-defined LicenseRef references, and three operators, AND, OR and WITH. After the plus sign, WITH binds tightest, then AND, then OR, and parentheses override that order. The operators must be written in capitals, while identifiers match without regard to case. The list released in September 2026, version 3.29.0, holds 740 licence identifiers, 32 of them deprecated, along with 85 current exception identifiers for use after WITH.

The licence also lives in a second place, the LICENSE file itself. When the manifest says MIT and the file holds a different text, at least one of them is wrong, and a reader cannot tell which. Comparing them is mostly a matter of recognising a licence by its own title and key sentences. One case cannot be settled that way: the GNU licences use the same text whether a project means “only” this version or “or later”, so the choice lives in the project’s notices, not in the licence text.

The fix is dull and worth doing: one licence, named the same way in every place it appears.

Keeping it true as the project changes

A README stays true when the parts that describe the project are generated from the project, and the parts that explain it are written by a person. The manifest already knows the name, the version, the licence and the scripts. The lockfile knows the package manager. The folder knows which files exist. Copying those facts by hand is how they drift.

Generation has to respect the human part, though. One workable pattern is to fence each generated section between two marker comments. A later run replaces only what sits between its own markers and leaves every other byte alone, so an introduction written by hand, a design note or a thank-you paragraph survives every rebuild. HTML comments suit the job because Markdown passes them through without showing them.

Reading the manifests correctly takes some care. pyproject.toml and Cargo.toml are written in TOML, whose version 1.0.0 specification covers four string forms, integers in four bases with a 64-bit range, dates and times, inline tables and arrays of tables, and strict rules against defining anything twice. The toml-test project publishes valid and invalid files for checking a reader; the reader behind this page accepts all 208 valid files and rejects all 501 invalid ones in its version 1.0.0 list.

There are things no check of this kind can know. It cannot tell whether a script does what its name suggests, whether a link to a website still works, or whether the prose is clear. It reads the top of the folder, not every nested package, and it never runs a command. What it can do is narrower and more reliable: catch the moment the README and the folder stop telling the same story, before a newcomer finds out by typing.

Sources

Every source consulted for this guide. Links checked October 24, 2026.

  1. README Wikipedia (CC BY-SA 4.0)
  2. Markdown Wikipedia (CC BY-SA 4.0)
  3. CommonMark Spec, version 0.31.2 John MacFarlane, CommonMark (CC BY-SA 4.0)
  4. <h1>-<h6> HTML section heading elements MDN Web Docs (CC BY-SA 2.5)
  5. package.json npm CLI documentation, npm/cli repository (Artistic License 2.0)
  6. npm-ci npm CLI documentation, npm/cli repository (Artistic License 2.0)
  7. npm-start npm CLI documentation, npm/cli repository (Artistic License 2.0)
  8. cargo run The Cargo Book (MIT or Apache-2.0)
  9. pnpm install pnpm documentation (MIT)
  10. Questions & Answers Yarn documentation (BSD-2-Clause)
  11. TOML v1.0.0 Tom Preston-Werner and the TOML authors (MIT)
  12. PEP 621 - Storing project metadata in pyproject.toml Python Enhancement Proposals (public domain or CC0-1.0)
  13. pyproject.toml specification Python Packaging User Guide, PyPA
  14. Structure and files uv documentation, Astral (MIT or Apache-2.0)
  15. Basic usage Poetry documentation (MIT)
  16. The Manifest Format The Cargo Book (MIT or Apache-2.0)
  17. Cargo.toml vs Cargo.lock The Cargo Book (MIT or Apache-2.0)
  18. Annex D: SPDX license expressions SPDX Specification v2.3.1, The Linux Foundation (CC BY 3.0)
  19. SPDX License List SPDX Workgroup, a Linux Foundation project (CC BY 3.0)
  20. toml-test The TOML authors (MIT)