Skip to main content

readwright

Render GitHub READMEs from Jinja2 templates with badge and screenshot helpers.

PyPI Python License CI pre-commit Ruff

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)

Source distribution for readwright 0.4.0
File Size Uploaded
readwright-0.4.0.tar.gz 496.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for readwright 0.4.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

0.5.0

2 release files

This release

0.4.0 This release

2 release files

0.3.0

2 release files

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