readwright
Render GitHub READMEs from Jinja2 templates with badge and screenshot helpers.
- Installation
- Quick start
- Template helpers
- Configuration
- pre-commit and GitHub Actions
- Agent skill
- Contributing
- License
Installation
pip install readwright
Or with uv:
uv tool install readwright
Quick start
cd my-repo
readwright init # writes readme.yaml + README.md.j2 (autodetects owner/repo, name, license, ...)
readwright render # writes README.md (--watch to re-render on change, -o - for stdout)
readwright check # exit 1 with a diff if README.md is stale (use as a pre-commit hook / in CI)
readwright badges # list badge presets
readwright blocks # list overridable blocks and partials
readwright show partials/install.md.j2 # print a packaged template to copy and tweak
Already have a README? readwright init --from-readme moves its body into the template so nothing is
lost, then takes over README.md. Prefer pyproject.toml? readwright init --pyproject writes the config
to [tool.readme].
README.md.j2 extends the packaged base.md.j2 and overrides only what it needs:
{% extends "base.md.j2" %}
{% block usage %}
## Usage
{{ screenshot("main", width=600) }}
Run `{{ project.name }} --help`.
{% endblock %}
See examples/ for a kitchen-sink project using every helper,
plus Rust, Go, .NET and Node examples showing how the install section adapts per language.
Template helpers
| Helper | Result |
|---|---|
badge("pypi"), badge("ci", workflow="test.yml") |
Preset badge built from repo metadata |
shield("Discord", "chat", "5865F2", link=...) |
Custom shields.io static badge |
badges() / donate_badges() |
All badges from badges: / donate: in config |
screenshot("main", alt=..., width=...) |
Finds docs/screenshots/main.{png,jpg,gif,webp,svg}; main-dark.* + main-light.* become a theme-aware pair |
screenshots(columns=2) |
Gallery table of every image in the screenshots dir |
image("path/or/url", "alt", width=...) |
Explicit image, no discovery |
screenshots(order=[...], captions={...}, subdir=...) |
Control gallery order/captions (or drop a captions.yaml in the folder) |
toc(), toc(1, 2) |
Table of contents from the headings below it (min/max level) |
changelog(n=1) |
Newest n entries of CHANGELOG.md |
project.*, vars.* |
Repo metadata and free-form config values |
cli_help("mytool --help") |
Runs the command and fences its output (needs allow_exec: true) |
include_file(path), code_block(path), snippet(path, start, end) |
Pull a file, a fenced file, or a marked region into the README |
config_table(path, section=...), env_table(".env.example"), entry_points_table() |
Markdown tables from YAML/TOML/JSON, env files, [project.scripts] |
gh_link("issues", "Issues"), spdx_link(), my_ha_link("hacs_repository", owner=..., repository=...) |
Repo-relative GitHub links, SPDX license link, My Home Assistant buttons |
callout("tip", text), details(summary, body), center(html), columns([...]) |
GitHub alerts, collapsibles, centered blocks, side-by-side cells |
logo(width=120), video("demo"), contributors([...]) |
Theme-aware logo from docs/logo.*, video/gif embed, avatar grid |
unsplash("photo-1518…", credit="Name", user="handle", width=1000, height=280) |
Hero image from Unsplash's CDN with the required attribution line; banner: in config puts one above the title |
flow_install_cmd(), mc_versions(), mod_dependencies(), related_repos() |
Flow Launcher / Minecraft mod / related-repo tables |
git_sha(), git_tag(), today() |
Build metadata (these change between renders, so check will flag them) |
To add a badge to the top row without touching the config list, fill the badges_extra hook (there is a
donate_extra too):
{% block badges_extra %} {{ shield("docs", "latest", "success", link=gh_link("wiki")) }}{% endblock %}
Badge presets: pypi, pypi-downloads, python, license, ci, codecov, npm, github-release,
github-stars, pre-commit, ruff, version, modrinth, curseforge, hacs, ha-version, plus donation presets kofi, buymeacoffee, github-sponsors,
patreon, paypal. Add your own under badges_custom; set badges_style: flat-square (or pass
style= to any badge helper) to restyle them all.
Blocks in base.md.j2: header, badges, donate, toc, screenshots, install, usage, extra,
contributing, license. Any packaged partial can be shadowed by a file of the same name under
templates/partials/ in the repo (or ~/.config/readwright/templates/ for all your repos).
Image helpers emit plain markdown by default (dark/light pairs use GitHub's
#gh-light-mode-only/#gh-dark-mode-only fragments instead of <picture>), and only fall back to HTML
when you ask for something markdown can't do, like a width=. Set screenshots.style: html to always
get <img>/<picture>/<table> output, or pass html=True to unsplash()/banner:.
Configuration
readme.yaml in the repo root (or [tool.readme] in pyproject.toml); see
examples/config-only/readme.yaml for every key,
annotated. Everything is optional;
metadata is autodetected from the git remote, the LICENSE file and whichever manifest the project
has: pyproject.toml, package.json, Cargo.toml, go.mod, *.csproj, Gradle (gradle.properties
mod metadata for Minecraft mods), hacs.json or a Flow Launcher plugin.json. The install section
adapts to the project type.
template: README.md.j2
templates: [../shared-readme-templates, "pkg:my_org_templates"] # extra template search paths
output: README.md
strict: false # missing screenshot -> error instead of warning
allow_exec: false # let cli_help() run commands during render
badges_style: flat-square # optional shields.io style for every badge
related: [{repo: other-tool, description: Sibling project}] # for related_repos()
banner: {unsplash: photo-1518770660439-4636190af475, credit: Alexandre Debiève, user: alexkixa}
screenshots: {dir: docs/screenshots, width: 720, style: markdown} # style: html for width/alignment
badges: [pypi, python, license, {preset: ci, workflow: test.yml}, {shield: {label: Docs, message: latest, color: success}}]
badges_custom:
discord: {label: Discord, message: chat, color: 5865F2, link: https://discord.gg/xyz}
donate: [kofi, github-sponsors]
donate_handles: {kofi: yourname, github-sponsors: yourname}
project: {name: ..., owner: ..., repo: ..., tagline: ..., pypi: ..., npm: ..., license: ..., ci_workflow: ...}
vars: {anything: you like}
Put the values you repeat across repos (donation handles, owner, custom badges) in
~/.config/readwright/config.yaml; readwright init bakes them into each new readme.yaml so rendering
stays reproducible in CI. readwright render --user-config merges them ad hoc.
pre-commit and GitHub Actions
# .pre-commit-config.yaml
- repo: https://github.com/Garulf/readwright
rev: v0.4.0
hooks:
- id: readwright-check
# .github/workflows/ci.yml
- uses: Garulf/readwright@v0.4.0
with:
mode: check # or render
Agent skill
The repo ships an agent skill at .agents/skills/readwright/
that teaches coding agents (Claude Code, Codex, Copilot CLI, Gemini CLI, ...) the
render/check workflow, the config keys and every template helper, so they edit
README.md.j2 instead of the generated README.md. Claude Code picks it up from
this repo automatically via .claude/skills/readwright; the same directory is
bundled inside the wheel, so any project can install it:
readwright skill --install # copies it to ./.agents/skills/readwright
readwright skill --install --dest ~/.claude/skills # or a user-level skills directory
readwright skill # just print where the bundled copy lives
Contributing
Issues and pull requests are welcome at Garulf/readwright.
License
MIT
Metadata
Release files for readwright 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| readwright-0.4.0.tar.gz | 496.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| readwright-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 539.3 kB
Release files / readwright-0.4.0.tar.gz
| Download URL | readwright-0.4.0.tar.gz |
|---|---|
| Size | 496.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ae3f56c50c0713489e73f9c03e59aee88689c17881328a7e3d36c74eb0a217d0
|
|
BLAKE2b-256 checksum How to use checksums |
6d6d99c2422cbe805505b8da5410f14ba82429ad79a474c8debde11c591d2853
|
| 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 Sep 13, 2026.
Transparency logRelease files / readwright-0.4.0-py3-none-any.whl
| Download URL | readwright-0.4.0-py3-none-any.whl |
|---|---|
| Size | 42.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9b8bd5fde672ef5734fa4b9778bc418978b931a66e492a05cb654a5e7aca462d
|
|
BLAKE2b-256 checksum How to use checksums |
dd21dd02b4e0d2048627107638b724c3c0d5149359f4ce87b6d8433b5ee11b8c
|
| 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 Sep 13, 2026.
Transparency log