repomatic.tooling.registry module

Declarative registry of the external tools repomatic run manages.

Each ToolSpec entry pins a tool’s version and, for binary-distributed tools, its per-platform download URLs and SHA-256 digests in CHECKSUMS; the paired VERSIONS map records the version each checksum set was computed for. The ArchiveFormat, NativeFormat, BinarySpec, and NpmSpec types describe how each tool is fetched and how its [tool.X] section is translated to the tool’s native config format. The repomatic run engine in tooling/runner.py consumes this data to install and invoke each tool, and tooling/binaries.py installs the binaries.

Note

sync-tool-versions and update-checksums rewrite this module’s version=, VERSIONS, and CHECKSUMS literals in place by string substitution, so their formatting must stay stable. The lint, autofix, and docs workflows key their tool caches on a hash of this file, so only a genuine version or checksum bump invalidates a cached tool download.

Todo

Drop the YAML-block fixup in the mdformat post-process callback once executablebooks/mdformat-myst#49 merges upstream. See _DIRECTIVE_YAML_OPTIONS_RE for the shape it repairs.

Todo

Drop the colon-fence fixup in the same callback once mdformat-myst ships colon-fence support, via either executablebooks/mdformat-myst#36 or executablebooks/mdformat-myst#48. See _ESCAPED_COLON_FENCE_RE for the shape it repairs.

Todo

Once lychee ships 0.25.0, the release expected to carry lycheeverse/lychee#2250, run the {#id} reproduction from lycheeverse/lychee#2249 through repomatic run lychee, and report the result there, as that comment commits to. The file:// docs-fragment entry in [tool.lychee] stays either way: lychee reads the Markdown source, which holds neither MyST (target)= labels nor the anchors directives generate at build time.

exception repomatic.tooling.registry.ToolInstallError[source]

Bases: RuntimeError

Raised when a registry tool cannot be installed.

The base of every install failure, whatever the backend. The CLI turns it into an error message, so a failed install never ends in a traceback.

exception repomatic.tooling.registry.BinaryInstallError[source]

Bases: ToolInstallError

Raised when a binary tool cannot be downloaded, verified or extracted.

Every failure of the binary install path ends as this error or one of its subclasses, so a caller that degrades on failure catches one type. The gh resolver of repomatic.github.gh is such a caller: it falls back to the gh on PATH.

exception repomatic.tooling.registry.UnsupportedPlatformError[source]

Bases: BinaryInstallError

Raised when a tool publishes no binary for the running platform.

Distinguished from every other install failure (a failed download, a checksum mismatch) because it is a property of the tool’s release matrix rather than a fault: nothing about the current run can make the binary exist. Asking for such a tool directly is still fatal, but a caller provisioning it as a companion can catch this alone and carry on without it. See repomatic.tooling.runner._path_tools_env.

class repomatic.tooling.registry.ArchiveFormat(*values)[source]

Bases: Enum

Archive format for binary tool downloads.

RAW = 'raw'
TAR_GZ = 'tar.gz'
TAR_XZ = 'tar.xz'
ZIP = 'zip'
tarfile_mode()[source]

Return the tarfile.open mode string for this format.

Raises:

ValueError – If called on a non-tar format.

Return type:

Literal['r:gz', 'r:xz']

class repomatic.tooling.registry.NativeFormat(*values)[source]

Bases: Enum

Target format for [tool.X] translation.

YAML = 'yaml'
TOML = 'toml'
JSON = 'json'
EDITORCONFIG = 'editorconfig'
FLAGS = 'flags'
serialize(data, tool_name='')[source]

Serialize a config dict to this format’s string representation.

When data is a live [tool.X] table parsed from pyproject.toml (a tomlrt.Table), the TOML branch keeps the user’s comments and spacing. tomlrt.Document rebuilds the section at the document root, so each [tool.X.sub] header becomes [sub]. A plain dict carries no trivia, so it is rendered as-is. The other formats (YAML, JSON, editorconfig) cannot carry TOML comments across the format boundary, so they serialize the values only.

Parameters:
  • data (dict) – Configuration dictionary to serialize.

  • tool_name (str) – Tool name for the generated-by header comment. JSON has no comment syntax, so it gets no header.

Raises:

ValueError – For FLAGS, which is not a file format.

Return type:

str

repomatic.tooling.registry.PlatformKey

A (platform_or_group, architecture) pair used as binary lookup key.

The platform element can be a single Platform (like MACOS) or a Group (like LINUX, which matches any Linux distribution). The architecture is always a concrete Architecture.

Resolution order in BinarySpec.resolve_platform():

  1. Exact Platform match (current_platform() == key_platform).

  2. Group membership (current_platform() in key_group), preferring the group with fewest members (most specific).

  3. The LINUX family, only when current_platform() is UNKNOWN_PLATFORM, so a distribution extra-platforms cannot name still reaches a family-wide key.

alias of tuple[Platform | Group, Architecture]

class repomatic.tooling.registry.ToolBackend(short_label, long_label)[source]

Bases: Enum

How a registry tool is delivered and executed.

Each member carries the display labels the documentation generators render, so backends and their vocabulary live in one place: adding a backend means adding a member here and a branch in ToolSpec.backend(), and every consumer (docs tables, version-sync candidate sources) follows.

Note

Code that dereferences a backend’s payload still tests the field directly (spec.binary is not None narrows the optional for mypy in a way an enum comparison cannot); this enum serves the sites that only need to know which backend, not its payload.

BINARY = ('Binary', 'Binary (downloaded from GitHub Releases)')
NPM = ('npm', 'npm registry, run via `node_modules/.bin`')
VENV = ('PyPI (venv)', 'PyPI, runs in project virtualenv via `uv run`')
UVX = ('PyPI', 'PyPI, installed via `uvx`')
short_label

Cell text for the docs summary table.

long_label

Installation-method line in the per-tool reference sections.

property enforces_cooldown: bool

Whether the install holds back a release younger than the cooldown.

The uv backends resolve the pin under --exclude-newer, UV_EXCLUDE_NEWER or the project’s [tool.uv] exclude-newer, and npm under min-release-age. A binary download only verifies the registry checksum, so minimum-release-age binds a binary pin when sync-tool-versions selects it, and never again after.

property reads_uv_exemptions: bool

Whether the install reads the project’s [tool.uv] exclude-newer-package.

Only the virtualenv backend installs through uv commands that read the project configuration: uv run --with, uv run --no-project --with and uv pip install. An entry there admits a pin still inside the window. uvx reads no project configuration, and npm installs into a scratch prefix with no exemption flag.

Probed with uv 0.13.1 on 2026-10-20: each of the three commands honored an entry, with and without UV_EXCLUDE_NEWER set, and uvx ignored it.

class repomatic.tooling.registry.BinarySpec(urls, checksums, archive_format, archive_executable=None, strip_components=0)[source]

Bases: object

Platform-specific binary download specification.

Keys are PlatformKey tuples pairing an extra-platforms Platform or Group with an Architecture. This lets callers use broad groups (LINUX matches any distro) or specific platforms (DEBIAN) with full detection heuristics from extra-platforms.

Hint

Structural integrity checks (key types, checksum format, URL placeholders, strip_components consistency) are enforced in test_tool_spec_integrity.

Todo

Move those integrity checks to __post_init__ if the registry ever becomes user-configurable: a test only covers the specs shipped here.

urls: dict[tuple[Platform | Group, Architecture], str]

Platform key to URL template mapping. URLs use {version} placeholders.

checksums: dict[tuple[Platform | Group, Architecture], str]

Platform key to SHA-256 hex digest mapping.

archive_format: ArchiveFormat | dict[tuple[Platform | Group, Architecture] | Platform | Group, ArchiveFormat]

Archive format of the downloaded file.

A single ArchiveFormat applies to every platform. A dict maps platform specifiers to formats, allowing mixed archives in one spec:

archive_format={ALL_PLATFORMS: ArchiveFormat.TAR_GZ, WINDOWS: ArchiveFormat.ZIP}

Dict keys follow the same resolution as resolve_platform(): exact PlatformKey tuple first, then bare Platform equality, then Group membership (smallest group wins).

archive_executable: str | None = None

Path of the executable inside the archive. None defaults to the tool name. For RAW format, used as the final filename.

strip_components: int | dict[tuple[Platform | Group, Architecture] | Platform | Group, int] = 0

Number of leading path components to strip when extracting.

A single int applies to every platform. A dict maps platform specifiers to counts, using the same resolution as get_archive_format(), for a project whose archives are not laid out identically across platforms:

strip_components={ALL_PLATFORMS: 1, WINDOWS: 0}

gh is the motivating case: its Linux and macOS archives nest everything under a gh_{version}_{platform}_{arch}/ directory, while the Windows zip puts bin/gh.exe at the root. The nesting cannot be absorbed by archive_executable instead, since that is one string for all platforms and the directory name carries the version and platform.

resolve_platform()[source]

Match the current environment against registered platform keys.

Uses current_platform() and current_architecture() from extra-platforms, inheriting its full detection heuristics, then falls back to the LINUX family when those heuristics name no distribution at all.

Return type:

tuple[Platform | Group, Architecture]

Returns:

The matching PlatformKey.

Raises:

UnsupportedPlatformError – If no key matches the current environment.

get_archive_format(key)[source]

Return the archive format for the given platform key.

When archive_format is a single ArchiveFormat, returns it directly. When it is a dict, resolves through _resolve_per_platform.

Return type:

ArchiveFormat

get_strip_components(key)[source]

Return the leading path components to strip for a platform key.

When strip_components is a plain int, returns it directly. When it is a dict, resolves through _resolve_per_platform.

Return type:

int

static platform_cache_key(key)[source]

Derive a filesystem-safe cache path segment from a platform key.

Return type:

str

Returns:

A string like linux-aarch64 or macos-x86_64.

resolve(tool_name, version, key=None)[source]

Pick the download of one platform, at one version.

Parameters:
  • tool_name (str) – Registry key of the tool, the default executable name.

  • version (str) – Version to fill into the URL template.

  • key (tuple[Platform | Group, Architecture] | None) – Platform to resolve. Defaults to the running platform.

Raises:

UnsupportedPlatformError – When key is not given and no binary matches the running platform.

Return type:

ResolvedBinary

class repomatic.tooling.registry.ResolvedBinary(*, key, url, checksum, archive_format, strip_components, executable)[source]

Bases: object

The download of a binary tool for one platform, at one version.

key: tuple[Platform | Group, Architecture]

The platform this download serves.

url: str

Download URL, with the version filled in.

checksum: str | None

SHA-256 of the download, or None when the registry records none.

archive_format: ArchiveFormat

Format of the download.

strip_components: int

Leading path components to drop from archive members.

executable: str

Path of the executable inside the archive, after stripping.

The final filename of a RAW download.

property cache_key: str

Filesystem-safe name of the platform, like linux-aarch64.

property archive_name: str

Filename of the download, as its URL names it.

property archive_members: tuple[str, str]

Names the executable can carry inside a ZIP archive.

A Windows build adds the .exe suffix to the executable.

property cached_names: tuple[str, str]

Names the cache can hold the extracted executable under.

The cache keys an entry on the extracted file’s own name: a path inside the archive (bin/gh) loses its directories, and a Windows build keeps its .exe suffix.

repomatic.tooling.registry.TOOL_LIST_HEADER_DEFS: tuple[ColumnSpec, ...] = (ColumnSpec(id='tool', label='Tool', description='', max_width=None, optional=False, sortable=True), ColumnSpec(id='version', label='Version', description='', max_width=None, optional=False, sortable=True), ColumnSpec(id='config-source', label='Config source', description='', max_width=None, optional=False, sortable=True))

Column definitions for the repomatic run --list table.

Lives beside the registry it renders; the CLI derives its --sort-by choices from it.

repomatic.tooling.registry.NPM_MIN_VERSION_FOR_COOLDOWN = '11.10.0'

First npm release honoring min-release-age, the cooldown gate for npm tools.

Older npm silently ignores the --min-release-age flag, so _install_npm warns when it cannot enforce the cooldown. This is a fixed floor (the release that introduced the option), distinct from the pinned NPM_BOOTSTRAP_TOOL, which sync-tool-versions keeps on the latest npm past the cooldown.

repomatic.tooling.registry.NPM_BOOTSTRAP_TOOL = 'npm'

Registry name of the npm that installs every npm-backed tool.

The npm on PATH is whatever the machine carries: unpinned, and too old to honor the cooldown on many images. It only installs this pinned npm, which then installs the tool. The npm package bundles its own dependencies, so that first install resolves nothing an old npm could leave outside the cooldown.

class repomatic.tooling.registry.NpmSpec[source]

Bases: object

npm-registry backend marker for a ToolSpec.

Presence (ToolSpec.npm is not None) selects the npm backend, the way a BinarySpec selects the download backend. The package name, executable, and version all derive from the ToolSpec fields, so no per-tool npm config is needed today; the class exists as a typed discriminator and a home for future npm-specific options.

Note

npm tools need Node.js and npm on PATH at run time: the one backend that depends on a runtime repomatic neither bundles nor provisions (binary tools are self-contained; the uv backends use uv). Integrity is npm’s own per-tarball verification on install, so unlike BinarySpec there is no repomatic-pinned checksum; the minimum-release-age cooldown (npm’s min-release-age, npm 11.10.0+) gates the transitive tree instead. Older npm ignores the gate, so the runner warns rather than silently skipping it.

class repomatic.tooling.registry.ToolSpec(name, display_name=None, version='', package=None, module=None, native_config_files=(), config_flag=None, native_format=NativeFormat.YAML, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url=None, tag_pattern=None, config_docs_url=None, cli_docs_url=None, docs_notes='')[source]

Bases: object

Specification for an external tool managed by repomatic.

Hint

Structural integrity checks (name format, version format, flag conventions, field consistency) are enforced in test_tool_spec_integrity.

Todo

Move those integrity checks to __post_init__ if the registry ever becomes user-configurable: a test only covers the specs shipped here.

Hint

CLI parser quirks for config_after_subcommand

Tools that use subcommands (tool <subcmd> [flags] [files]) may require config_flag to appear after the subcommand name, depending on the CLI parser framework:

  • clap (Rust): global flags accepted before or after the subcommand. No special handling needed. Used by: ruff, labelmaker.

  • cobra (Go): root-level flags inherited by all subcommands, accepted in both positions. No special handling needed. Used by: gitleaks.

  • click (Python): global flags accepted before or after the subcommand. No special handling needed. Used by: bump-my-version.

  • bpaf (Rust): #[bpaf(external)] fields are scoped inside the subcommand variant, so tool <subcmd> --flag works but tool --flag <subcmd> does not. Set config_after_subcommand=True. Used by: biome.

name: str

Tool identity: CLI name for repomatic run <name>, default PyPI package name, and default executable name.

display_name: str | None = None

Human-readable name with proper casing for documentation (like 'Biome', 'Gitleaks'). None defaults to name.

version: str = ''

Pinned version (e.g., '1.38.0').

package: str | None = None

Install target passed to uvx/uv run (and pip). None defaults to name. Only set when it differs from the tool name, and may carry an install extra (Nuitka’s nuitka[onefile]); for PyPI lookups query the bare project name through pypi_name, which strips the extra.

module: str | None = None

Python module name for -m module invocation, e.g. 'nuitka'.

When set, the tool is invoked as python -m <module> instead of the console script. Requires needs_venv=True. Use when the tool’s script entry point is not reliably found across platforms (for example, Nuitka installs only a .cmd wrapper on Windows, which uv run -- nuitka cannot locate).

native_config_files: tuple[str, ...] = ()

Config filenames the tool auto-discovers, checked in order.

Paths relative to repo root (e.g., 'zizmor.yaml', '.github/actionlint.yaml'). Empty for tools with no config file.

config_flag: str | None = None

CLI flag to pass a config file path (e.g., '--config', '--config-file'). None if the tool only reads from fixed paths.

native_format: NativeFormat = 'yaml'

Target format for [tool.X] translation.

NativeFormat.FLAGS translates the table to CLI flags (via config_table_to_flags) instead of a config file, for tools that expose their config keys as long options but read no config file themselves. It is mutually exclusive with reads_pyproject, config_flag, and native_config_files.

default_config: str | None = None

Filename in repomatic/data/ for bundled defaults, stored in native_format. None if no bundled default exists.

reads_pyproject: bool = False

Whether the tool natively reads [tool.X] from pyproject.toml.

When True and [tool.X] exists in pyproject.toml, repomatic skips Level 2 translation (the tool reads it directly). Resolution still falls through to Level 3 (bundled default) and Level 4 (bare) when no config is found.

pyproject_table: str | None = None

Name of the tool’s table under [tool] in pyproject.toml, when it differs from name. None defaults to the tool name.

bump-my-version reads [tool.bumpversion]: the table name predates the rename of the project.

default_flags: tuple[str, ...] = ()

Flags always passed to the tool (e.g., ('--strict',)).

ci_flags: tuple[str, ...] = ()

Flags added only when $GITHUB_ACTIONS is set (e.g., output format).

default_args: tuple[str, ...] = ()

Arguments used when the caller passes none of their own.

Together with default_paths, this makes a bare repomatic run <tool> the invocation CI performs, so nobody has to reconstruct it from a workflow step. It applies only when extra_args is empty: any explicit argument means the caller is driving, and nothing is injected on top of it.

That all-or-nothing rule is what keeps a subcommand safe to put here. biome’s defaults open with format, and splicing them into a caller’s check . would build biome format … check .; because an explicit argument suppresses them entirely, that command cannot be built.

default_paths: str | None = None

Name of the FileInventory attribute supplying this tool’s targets, when the caller passes no arguments.

Caution

An empty inventory means the tool is skipped, not invoked with no path. The distinction is the whole point: a formatter handed zero paths does not no-op, it walks the entire tree in write mode. Replaying a workflow’s xargs pipe on a repo with no matching file did exactly that once, rewriting 3,000+ files, and it is the reason this resolves the list in-process rather than leaving it to a shell.

per_file: bool = False

Invoke the tool once per target rather than once for all of them.

Mirrors xargs -n1, for a tool whose per-file behaviour differs from its batch behaviour. Only meaningful alongside default_paths.

with_packages: tuple[str, ...] = ()

Extra packages installed alongside the tool (e.g., mdformat plugins).

Passed as --with <pkg> to uvx.

path_tools: tuple[str, ...] = ()

Other registry tools whose executable must be on PATH while this runs.

For a plugin that shells out to a second binary rather than importing it: mdformat-shfmt formats fenced shell blocks by invoking shfmt from PATH, so mdformat declares path_tools=("shfmt",).

Each name is installed through the same registry path as a direct repomatic run, so the companion arrives at the pinned version, checksum verified, from the shared cache. The alternative, letting the environment supply it, is what this field exists to prevent: a system package manager hands over whatever its archive holds, unpinned and outside the cooldown, and the same tool then behaves differently depending on which job invoked it.

Names must resolve in TOOL_REGISTRY and carry a binary spec; test_tool_spec_integrity enforces both.

needs_venv: bool = False

If True, use uv run (project venv) instead of uvx (isolated).

Required when the tool imports project code (mypy, pytest). The project venv materializes from the frozen uv.lock; in a repository without one the runner degrades to an isolated, cooldown-gated environment (uv run --no-project), see _build_install_args in tooling/runner.py.

install_into_venv: bool = False

If True, install the tool into the project venv before it runs.

Requires needs_venv=True. Without it, a needs_venv tool is layered over the project venv with uv run --with, and uv keeps that layer in a directory of its cache that it names at random.

Note

Nuitka turns its own location into the -I flags of every C compilation, and the compile cache keys on those flags. Under uv run --with, each layer that uv builds again makes the next build miss every file. The project venv sits at the same path on every runner, so a tool installed there keeps its cache. docs/nuitka.md holds the measurements.

The install stays in the venv after the run. uv sync removes it. In a repository without a uv.lock the field has no effect, because the tool then runs isolated from the project.

computed_params: Callable[[Metadata], list[str]] | None = None

Callable that receives a Metadata instance and returns extra CLI args derived from project metadata (e.g., mypy’s --python-version from requires-python). None if no computed params.

prepare: Callable[[Metadata], dict[str, str]] | None = None

Callable readying the machine for a run, before the tool is installed.

Receives a Metadata instance, does what the run needs beyond its arguments, and returns the environment variables to set for the tool on top of the inherited ones. Called once per repomatic run, never by a documentation or a check that only reads the spec. None if the tool needs no preparation.

Nuitka is the one user: see prepare_nuitka_build().

config_after_subcommand: bool = False

Insert config_flag after the first token of extra_args.

Needed for tools whose CLI parser (e.g., bpaf) scopes global options inside the subcommand, so tool subcommand --config-path X is valid but tool --config-path X subcommand is not. When True, config_args are spliced after the first element of extra_args (the subcommand name).

post_process: Callable[[Sequence[str]], None] | None = None

Callback invoked on extra_args after the tool exits successfully.

run_tool adds each file whose content changed below a directory argument during the run, so a callback that reads its arguments as files covers a tree too. A callback must skip a file it cannot read: another process can be the one that changed it.

Intended for temporary workarounds that fix known upstream formatting bugs in-place. Each callback carries its own todo admonition naming the upstream release that retires it.

Note

The callback runs only after a successful write-mode exit (return code 0) and rewrites files on disk, so it cannot apply in check/dry-run mode, which writes nothing. Pair it with check_flags so run_tool warns when a check invocation would silently bypass it. See check_bypasses_post_process().

output_flag: str | None = None

Flag whose argument names the tool’s report destination, when the tool refuses to create missing parent directories itself.

run_tool pre-creates the parent directory of the path following this flag (both --flag path and --flag=path forms), so a workflow can point the tool into a scratch subdirectory without a separate mkdir step. lychee is the motivating case: a report path in a scratch subdirectory, which lychee errors out on rather than creating it.

problem_matcher: str | None = None

Filename in repomatic/data/ of a GitHub Actions problem matcher.

When the tool runs in GitHub Actions, run_tool registers the matcher before the tool starts and removes it once the tool exits, so each finding the tool prints becomes an annotation on the file and the line it names. Bundled rather than fetched from the upstream repository at run time: the matcher then travels with the pinned release, and no workflow step has to download it.

check_flags: tuple[str, ...] = ()

Flags that put the tool in check/dry-run mode, writing no files.

Warning

Check mode bypasses post_process: that fixup rewrites files on disk, but check mode writes nothing. So when a tool defines both a post_process and check_flags, its check-mode exit status is unreliable. run_tool detects the pairing via check_bypasses_post_process() and warns. Verify formatting by running the write path, not the check flag: repomatic.tooling.runner.verify_via_write_path() does exactly that against throwaway copies, so the answer is authoritative and the working tree is still never written to.

rewrite_exit_code: int | None = None

Exit code the tool returns when it rewrote at least one file.

Formatters that signal “I reformatted something” with a non-zero status force every caller to tolerate that code, which is what lets a crash pass for a success: pyproject-fmt exits 1 both when it reformats a file and when it dies on a PanicException, and the autofix job cannot tell the two apart from the status alone.

Declaring the code here gives run_tool the second signal it needs: the files themselves. A run exiting with this code after rewriting a target reports 0, the success the caller asked for, so no caller has to tolerate the code. A run exiting with it and leaving every target byte-identical contradicts what the code claims, so it is reported as a failure instead of being waved through. See repomatic.tooling.runner.TOOL_CRASH_EXIT_CODE.

None for tools with no such convention, which is most of them: a formatter that exits 0 whether or not it wrote anything needs no disambiguation.

binary: BinarySpec | None = None

Platform-specific binary download spec. When set, the tool is downloaded as a binary instead of installed via uvx or uv run.

npm: NpmSpec | None = None

npm-registry backend marker. When set, the tool is installed from npm and run via its node_modules/.bin executable, instead of a binary download or a uv install. Mutually exclusive with binary and needs_venv.

source_url: str | None = None

GitHub repository or project homepage URL.

tag_pattern: str | None = None

Regex extracting the version from a GitHub release tag.

Used by sync-tool-versions for binary tools whose tags do not follow the common vX.Y.Z scheme. The pattern must define a version named group (e.g. r"^lychee-v(?P<version>.+)$" for lychee, r”^@biomejs/biome@ (?P<version>.+)$”``for biome). When``None, the version is the tag with a leading v stripped.

config_docs_url: str | None = None

URL to the tool’s configuration reference.

cli_docs_url: str | None = None

URL to the tool’s CLI usage documentation.

docs_notes: str = ''

Hand-written Markdown appended to the tool’s section in tool-runner.md.

Free-form usage notes the registry cannot derive: a **Try it:** shell session, a minimal [tool.X] example, caveats. Rendered live by tool_reference() after the generated metadata lines, so the prose stays next to the spec it documents.

property backend: ToolBackend

Delivery mechanism, derived from which spec fields are set.

binary and npm win over needs_venv; test_tool_spec_integrity keeps the three mutually exclusive so the order never actually decides.

property label: str

Name to show in documentation: display_name, else the name.

property install_target: str

Package the backend installs: package, else the tool name.

property pyproject_key: str

Key of the tool’s table under [tool] in pyproject.toml.

property pypi_name: str

Bare PyPI project name for version and metadata lookups.

package doubles as the install target, so it may carry an install extra (Nuitka’s nuitka[onefile]) that _build_install_args needs at install time. The PyPI JSON API is keyed by the bare project name, though, and 404s on a bracketed extra, so sync-tool-versions and the held-back PR links query this stripped name (nuitka) instead.

property pypi_pin: str

Exact PyPI requirement that installs the pinned version.

The install extra of package stays, as in nuitka[onefile]==4.3: this is the string uvx --from, uv run --with and uv pip install take.

property with_package_pins: tuple[tuple[str, str], ...]

(package, version) of each with_packages entry.

The package name keeps the spelling of the entry, which is what a rewrite of the pin in this module has to match. test_tool_spec_integrity holds every entry to the package==version form.

property datasource_url: str

Human-facing URL for the tool’s version datasource.

npmjs for npm tools, the GitHub source_url when set, else the PyPI project page. Used by sync-tool-versions for the diff-table and held-back links.

property writes_config_in_cwd: bool

Whether a generated config reaches the tool as a working-directory file.

A tool with no config_flag gets its translated or bundled config written to its first native config path, for the length of one run.

property tracks_rewrites: bool

Whether a run has to compare its targets before and after the tool.

The files say what the tool rewrote: rewrite_exit_code checks a rewrite the exit code claims, and post_process fixes the files that changed below a directory argument.

find_native_config(root=None)[source]

Return the first of native_config_files that exists.

Parameters:

root (Path | None) – Directory the relative config paths resolve against. Defaults to the working directory.

Return type:

Path | None

resolve_binary(key=None)[source]

Pick the download of this tool’s binary, at its pinned version.

Parameters:

key (tuple[Platform | Group, Architecture] | None) – Platform to resolve. Defaults to the running platform.

Raises:
Return type:

ResolvedBinary

with_overrides(*, version=None, checksum=None)[source]

Return a copy pinned to another version or another checksum.

Parameters:
  • version (str | None) – Version to install instead of the pinned one.

  • checksum (str | None) – SHA-256 to expect from the download of the running platform. Ignored for a tool that ships no binary.

Raises:

UnsupportedPlatformError – When checksum is given and no binary matches the running platform.

Return type:

ToolSpec

splice_config_args(config_args, args)[source]

Combine config and caller arguments in the order the tool expects.

Config arguments come first, unless config_after_subcommand puts them after the subcommand that opens args.

Return type:

list[str]

output_paths(args)[source]

Return the report destinations output_flag names in args.

Reads both the --flag path and the --flag=path forms.

Return type:

list[Path]

check_bypasses_post_process(extra_args)[source]

Return True when a check-mode flag will skip post_process.

Check/dry-run flags (check_flags) make the tool exit without writing files, so the post_process fixup never runs and the exit status cannot be trusted: it may flag drift the write path would reconcile, or miss drift the write path would introduce. run_tool warns on this. Returns False for tools with no post_process, where check mode is authoritative.

Return type:

bool

repomatic.tooling.registry.CHECKSUMS: dict[str, dict[tuple[Platform | Group, Architecture], str]] = {'actionlint': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'cadcf7ea4efe3a68728893813643cebe1185e5b1d4be5b96245f65c9a4d5ea41', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9'}, 'biome': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'a56bcd73ddbfdb0f57a82bb67b7ea698eed4027aff602b95aa18fafe919d59e0', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '5d867a0b2ccea1755b7e508b01d836a8c64e31a1e4d11ec24b43b6b4eec8b3b6', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '3636796d8c78bfcfe0d45946dc93e21e12d27a49eba7401e18af1ea854d25503', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '86fc618563d19f373ac24eb5b8f1e41bd753a506d5df1f198c152ec365141490', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '6244a2bb366c76ad869b94f999ee60ce41b94112a11dc23c4f05fd1a7053256a', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'f2beadee753b78fe76081158123538be886a31b5e67a34863e42535f3f491926'}, 'gh': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '7862c86c72f43df3a2d93ddde6f473285b4e2af61b494849846827e513ef6484', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'da922c20d1792e5b2cbf375593d7a658acf034c12c84e007e71c76ef959c337e', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'b245f24eb2bf5f75b426b4c26da3651a107f8d5b6f4fddfbfccc5679041378b3', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '5dcf12aa8525eabd0c46ec414f323ab6cf65229fc2cd46543cc705001bbaf223', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'ae64e556ecc240b200f7eba60d550e4bb60d78e860e69dd88c449405b86067f4'}, 'gitleaks': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b95f5e4f5c425cedca7ee203d9afd29597e692c4924a12ed42f970537c72cc0f', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd29144deff3a68aa93ced33dddf84b7fdc26070add4aa0f4513094c8332afc4e'}, 'labelmaker': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '4685e142da55150904d16624fe1052161de5dba1a859cddef19ab41833c37728', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd76f8e64f9671884dac1758fe54a28a6680c5d9bf0ffd593a2c68ba558cc49a2', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'a52a4e102f0760ce1632da5fdaee2b0debe0e6ddea577b88a94a60172fe85751', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'dc8374d6a9bec4ebf143fb42e3024aeffabe8585bb9bd6f134cfaf0693be7688', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '939195930f9d5fd2b15a5cf43497019a52083e6c6713807d3379de49395c2e10'}, 'lychee': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '91a7bd65685da41b90ccb9bc867a3d649a7818042dae04ff405e55a25bddee4c', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'c9d3740ea2d891854d37116c9fba840f37b6e7c89d330e7db84ac333631c4977', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '32975d1493ee1a975d6bb41e4fb56fe419cb442ded628bb772ba2e614acfacad'}, 'oxipng': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'f51bb7c9836202da52a8181549641940662aa87ec2e3ae68115d822d893015b3', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '46e3c4beb9aae57290ad809dd3374b07153579d3322a3778c53900633618b7c6', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '7039fcfc78e8aa1ed2b57d848057a0296f082e92b3e1807ac65402d10d926764', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '111883bbe42b25e01cb1ca41f39f8094e4830b173f0e42be342ecc4ca48f3131', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '7e940f83ee46874b73f53031f96a15834cb70b220af27391fb06fe7b4dd798e1'}, 'shellcheck': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '56affdd8de5527894dca6dc3d7e0a99a873b0f004d7aabc30ae407d3f48b0a79', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '3c89db4edcab7cf1c27bff178882e0f6f27f7afdf54e859fa041fca10febe4c6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8a4e35ab0b331c85d73567b12f2a444df187f483e5079ceffa6bda1faa2e740e'}, 'shfmt': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '5f2db09dae91fca848f7adbdd014632e921a383863a2ad7e0450ad3aba0c6489', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '76e77641faa025814b77f153b29796b8e6fa2fca03e0c76a691608b86c7ea7bf', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b7c872db63553ccffc7253aba3ed7d4885a27d83f1ba567b1138c6315a5847e5', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd33eee0da0f92835b3562e9767a05cee7e4eaeef47daa03bfd09da17b4b590a6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '13629ce28442ca80b6b5a819f7574ab39e1c28c6e26734ca816c9714e04851df'}, 'typos': {(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '2ac5e37b3236b3c07dec5cdf1527acf143aec4674a6089b0bf424d021b35e099', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '93c301fd4120ff076e2cc2a6d6a61cab4a4bdb81899287c0938b452e56d1f454', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '316d50a32c3726a80ab39c6d12141b70080498973c5e8a9b068b12cde78b10c1', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'f3451079971633a7f769fb958fe06b0d64b5f1dc46df0854aa1ddb9a6f4031d6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '518f759fba43205f17a717656054c1d25bc883455a6296989e9d021c6edb7c59'}}

Tool name to platform-keyed SHA-256 hex digest mapping.

Recomputed in place by repomatic update-checksums and sync-tool-versions. Kept as a flat sidecar dict (rather than inline in each BinarySpec) so the checksum recompute can replace a hash by exact string match without re-parsing the registry, and so VERSIONS can anchor the offline staleness test.

repomatic.tooling.registry.VERSIONS: dict[str, str] = {'actionlint': '1.7.12', 'biome': '2.5.15', 'gh': '2.102.0', 'gitleaks': '8.30.1', 'labelmaker': '0.6.4', 'lychee': '0.24.2', 'oxipng': '10.2.1', 'shellcheck': '0.11.0', 'shfmt': '3.14.1', 'typos': '1.51.1'}

Tool name to the version each checksum set was computed for.

test_tool_spec_integrity asserts this equals the matching ToolSpec.version, so a bump whose checksums were never refreshed (a stale CHECKSUMS entry) fails CI offline, without downloading anything.

repomatic.tooling.registry.TOOL_REGISTRY: dict[str, ToolSpec] = {'actionlint': ToolSpec(name='actionlint', display_name=None, version='1.7.12', package=None, module=None, native_config_files=('.github/actionlint.yaml', '.github/actionlint.yml'), config_flag='--config-file', native_format=<NativeFormat.YAML: 'yaml'>, default_config='actionlint.yaml', reads_pyproject=False, pyproject_table=None, default_flags=('-color',), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=('shellcheck',), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher='actionlint-matcher.json', check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_linux_arm64.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_linux_amd64.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_darwin_arm64.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_darwin_amd64.tar.gz', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_windows_arm64.zip', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/rhysd/actionlint/releases/download/v{version}/actionlint_{version}_windows_amd64.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'cadcf7ea4efe3a68728893813643cebe1185e5b1d4be5b96245f65c9a4d5ea41', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=0), npm=None, source_url='https://github.com/rhysd/actionlint', tag_pattern=None, config_docs_url='https://github.com/rhysd/actionlint/blob/main/docs/config.md', cli_docs_url='https://github.com/rhysd/actionlint/blob/main/docs/usage.md', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run actionlint\n```\n\n**Minimal `[tool.actionlint]`:**\n\n```toml\n[tool.actionlint.self-hosted-runner]\nlabels = ["my-linux-runner"]\n```\n\nWith no arguments actionlint lints every workflow under `.github/workflows`.\nThe `[tool.actionlint]` section is bridged to a temporary YAML config:\ndeclaring self-hosted runner labels stops custom `runs-on:` values being\nflagged as unknown.\n\nThe pinned ShellCheck goes on `PATH` for the run, which is what lints the\nshell scripts of the `run:` steps. In GitHub Actions, repomatic also\nregisters actionlint\'s problem matcher, so each finding annotates the line\nit names.'), 'autopep8': ToolSpec(name='autopep8', display_name=None, version='2.3.2', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=True, pyproject_table=None, default_flags=('--recursive', '--in-place', '--max-line-length', '88', '--select', 'E501'), ci_flags=(), default_args=(), default_paths='python_files', per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/hhatto/autopep8', tag_pattern=None, config_docs_url=None, cli_docs_url='https://pypi.org/project/autopep8/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run autopep8 -- .\n```\n\nautopep8 takes its configuration from CLI flags only. repomatic passes\n`--recursive --in-place --max-line-length 88 --select E501` by default;\nappend more flags after `--`.'), 'awesome-lint': ToolSpec(name='awesome-lint', display_name=None, version='2.3.0', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=NpmSpec(), source_url=None, tag_pattern=None, config_docs_url=None, cli_docs_url='https://github.com/sindresorhus/awesome-lint#usage', docs_notes=''), 'biome': ToolSpec(name='biome', display_name='Biome', version='2.5.15', package=None, module=None, native_config_files=('biome.json', 'biome.jsonc', '.biome.json', '.biome.jsonc'), config_flag='--config-path', native_format=<NativeFormat.JSON: 'json'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=('format', '--write', '--no-errors-on-unmatched', '--json-parse-allow-comments=true', '--json-parse-allow-trailing-commas=true'), default_paths='json_files', per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=True, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-linux-arm64', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-linux-x64', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-darwin-arm64', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-darwin-x64', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-win32-arm64.exe', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/biomejs/biome/releases/download/%40biomejs%2Fbiome%40{version}/biome-win32-x64.exe'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'a56bcd73ddbfdb0f57a82bb67b7ea698eed4027aff602b95aa18fafe919d59e0', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '5d867a0b2ccea1755b7e508b01d836a8c64e31a1e4d11ec24b43b6b4eec8b3b6', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '3636796d8c78bfcfe0d45946dc93e21e12d27a49eba7401e18af1ea854d25503', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '86fc618563d19f373ac24eb5b8f1e41bd753a506d5df1f198c152ec365141490', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '6244a2bb366c76ad869b94f999ee60ce41b94112a11dc23c4f05fd1a7053256a', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'f2beadee753b78fe76081158123538be886a31b5e67a34863e42535f3f491926'}, archive_format=<ArchiveFormat.RAW: 'raw'>, archive_executable=None, strip_components=0), npm=None, source_url='https://github.com/biomejs/biome', tag_pattern='^@biomejs/biome@(?P<version>.+)$', config_docs_url='https://biomejs.dev/reference/configuration/', cli_docs_url='https://biomejs.dev/reference/cli/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run biome -- check .\n```\n\n**Minimal `[tool.biome]`:**\n\n```toml\n[tool.biome.formatter]\nindentStyle = "space"\n```\n\n`biome check` reports formatting and lint issues; add `--write` after `--`\nto apply fixes. The `[tool.biome]` section is bridged to a temporary\n`biome.json`, so keys keep Biome\'s camelCase spelling.'), 'bump-my-version': ToolSpec(name='bump-my-version', display_name=None, version='1.6.0', package=None, module=None, native_config_files=('.bumpversion.toml',), config_flag='--config-file', native_format=<NativeFormat.TOML: 'toml'>, default_config=None, reads_pyproject=True, pyproject_table='bumpversion', default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/callowayproject/bump-my-version', tag_pattern=None, config_docs_url='https://callowayproject.github.io/bump-my-version/reference/configuration/', cli_docs_url='https://callowayproject.github.io/bump-my-version/reference/cli/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run bump-my-version -- show-bump\n```\n\n**Minimal `[tool.bumpversion]`:**\n\n```toml\n[tool.bumpversion]\ncurrent_version = "1.2.3"\n```\n\nThe configuration table is `[tool.bumpversion]`, not\n`[tool.bump-my-version]`: the section name predates the project\'s rename.\n`show-bump` previews the next versions without writing; `repomatic run\nbump-my-version -- bump minor` performs the bump.'), 'gh': ToolSpec(name='gh', display_name='GitHub CLI', version='2.102.0', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_linux_arm64.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_linux_amd64.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_macOS_arm64.zip', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_macOS_amd64.zip', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_windows_arm64.zip', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/cli/cli/releases/download/v{version}/gh_{version}_windows_amd64.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '7862c86c72f43df3a2d93ddde6f473285b4e2af61b494849846827e513ef6484', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'da922c20d1792e5b2cbf375593d7a658acf034c12c84e007e71c76ef959c337e', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'b245f24eb2bf5f75b426b4c26da3651a107f8d5b6f4fddfbfccc5679041378b3', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '5dcf12aa8525eabd0c46ec414f323ab6cf65229fc2cd46543cc705001bbaf223', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'ae64e556ecc240b200f7eba60d550e4bb60d78e860e69dd88c449405b86067f4'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='macos', name='macOS'): <ArchiveFormat.ZIP: 'zip'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable='bin/gh', strip_components={Group(id='all_platforms', name='All platforms'): 1, Platform(id='windows', name='Windows'): 0}), npm=None, source_url='https://github.com/cli/cli', tag_pattern=None, config_docs_url=None, cli_docs_url='https://cli.github.com/manual/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run gh -- --version\n```\n\nPinned so the release lane gets the same `gh` everywhere. The manylinux\ncontainer the Linux binaries compile in ships no `gh`, and the runner images\nthat do ship one leave its version to the image. `gh` reads no project\nconfiguration: it authenticates from `GH_TOKEN` in the environment.'), 'gitleaks': ToolSpec(name='gitleaks', display_name='Gitleaks', version='8.30.1', package=None, module=None, native_config_files=('.gitleaks.toml',), config_flag='--config', native_format=<NativeFormat.TOML: 'toml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=('git', '.'), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_linux_arm64.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_linux_x64.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_darwin_arm64.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_darwin_x64.tar.gz', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_windows_arm64.zip', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/gitleaks/gitleaks/releases/download/v{version}/gitleaks_{version}_windows_x64.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709', (Platform(id='windows', name='Windows'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b95f5e4f5c425cedca7ee203d9afd29597e692c4924a12ed42f970537c72cc0f', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd29144deff3a68aa93ced33dddf84b7fdc26070add4aa0f4513094c8332afc4e'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=0), npm=None, source_url='https://github.com/gitleaks/gitleaks', tag_pattern=None, config_docs_url='https://github.com/gitleaks/gitleaks#configuration', cli_docs_url='https://github.com/gitleaks/gitleaks#usage', docs_notes="**Try it:**\n\n```shell-session\n$ repomatic run gitleaks\n```\n\n**Minimal `[tool.gitleaks]`:**\n\n```toml\n[tool.gitleaks.extend]\nuseDefault = true\n\n[tool.gitleaks.allowlist]\npaths = ['''\\.env\\.sample$''']\n```\n\nWith no arguments, gitleaks scans the git history of the repository\n(`gitleaks git .`), as the `lint-secrets` job does. `repomatic run gitleaks\n-- dir .` scans the working tree instead. The `[tool.gitleaks]` section is\nbridged to a temporary `.gitleaks.toml`: keep `extend.useDefault = true`, or\na custom config silently replaces the built-in rule set."), 'labelmaker': ToolSpec(name='labelmaker', display_name=None, version='0.6.4', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/jwodder/labelmaker/releases/download/v{version}/labelmaker-aarch64-unknown-linux-gnu.tar.xz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/jwodder/labelmaker/releases/download/v{version}/labelmaker-x86_64-unknown-linux-gnu.tar.xz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/jwodder/labelmaker/releases/download/v{version}/labelmaker-aarch64-apple-darwin.tar.xz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/jwodder/labelmaker/releases/download/v{version}/labelmaker-x86_64-apple-darwin.tar.xz', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/jwodder/labelmaker/releases/download/v{version}/labelmaker-x86_64-pc-windows-msvc.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '4685e142da55150904d16624fe1052161de5dba1a859cddef19ab41833c37728', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd76f8e64f9671884dac1758fe54a28a6680c5d9bf0ffd593a2c68ba558cc49a2', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'a52a4e102f0760ce1632da5fdaee2b0debe0e6ddea577b88a94a60172fe85751', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'dc8374d6a9bec4ebf143fb42e3024aeffabe8585bb9bd6f134cfaf0693be7688', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '939195930f9d5fd2b15a5cf43497019a52083e6c6713807d3379de49395c2e10'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_XZ: 'tar.xz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=1), npm=None, source_url='https://github.com/jwodder/labelmaker', tag_pattern=None, config_docs_url=None, cli_docs_url='https://github.com/jwodder/labelmaker', docs_notes="labelmaker syncs a repository's issue and PR labels from a label-definition\nfile, so unlike the linters it needs a target repository and a\n`GITHUB_TOKEN`, not a path in the working tree. There is no\n`[tool.labelmaker]` section: the label file is the configuration. See the\n[upstream usage docs](https://github.com/jwodder/labelmaker) for its flags\nand file schema."), 'lychee': ToolSpec(name='lychee', display_name='Lychee', version='0.24.2', package=None, module=None, native_config_files=('lychee.toml',), config_flag='--config', native_format=<NativeFormat.TOML: 'toml'>, default_config=None, reads_pyproject=True, pyproject_table=None, default_flags=(), ci_flags=(), default_args=('--hidden', '--suggest', '--no-progress', '--include-fragments', '--exclude-all-private'), default_paths='doc_files', per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag='--output', problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/lycheeverse/lychee/releases/download/lychee-v{version}/lychee-aarch64-unknown-linux-gnu.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/lycheeverse/lychee/releases/download/lychee-v{version}/lychee-x86_64-unknown-linux-gnu.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/lycheeverse/lychee/releases/download/lychee-v{version}/lychee-aarch64-apple-darwin.tar.gz', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/lycheeverse/lychee/releases/download/lychee-v{version}/lychee-x86_64-pc-windows-msvc.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '91a7bd65685da41b90ccb9bc867a3d649a7818042dae04ff405e55a25bddee4c', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'c9d3740ea2d891854d37116c9fba840f37b6e7c89d330e7db84ac333631c4977', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '32975d1493ee1a975d6bb41e4fb56fe419cb442ded628bb772ba2e614acfacad'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=1), npm=None, source_url='https://github.com/lycheeverse/lychee', tag_pattern='^lychee-v(?P<version>.+)$', config_docs_url='https://lychee.cli.rs/guides/config/', cli_docs_url='https://lychee.cli.rs/guides/cli/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run lychee\n```\n\n**Minimal `[tool.lychee]`:**\n\n```toml\n[tool.lychee]\nmax_redirects = 5\n```\n\nWith no arguments, lychee checks the links of every documentation file, as\nthe `check-broken-links` job does through `repomatic broken-links`. Pass\npaths after `--` to check other files. Since v0.24 it reads `[tool.lychee]`\nfrom `pyproject.toml` natively, so repomatic does not translate it.'), 'mdformat': ToolSpec(name='mdformat', display_name=None, version='1.0.0', package=None, module=None, native_config_files=('.mdformat.toml',), config_flag=None, native_format=<NativeFormat.TOML: 'toml'>, default_config='mdformat.toml', reads_pyproject=True, pyproject_table=None, default_flags=('--strict-front-matter',), ci_flags=(), default_args=(), default_paths='markdown_files', per_file=True, with_packages=('mdformat_admon==2.1.1', 'mdformat-config==0.2.1', 'mdformat_deflist==0.1.4', 'mdformat_footnote==0.1.3', 'mdformat-front-matters==2.0.0', 'mdformat-gfm==1.0.0', 'mdformat_gfm_alerts==2.1.0', 'mdformat_myst==0.3.0', 'mdformat-pelican==1.0.0', 'mdformat_pyproject==0.1.1', 'mdformat-recover-urls==0.0.2', 'mdformat-shfmt==0.2.0', 'mdformat_simple_breaks==0.1.0', 'mdformat-toc==0.5.0', 'mdformat-web==0.2.0'), path_tools=('shfmt',), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=<function _fix_myst_directives>, output_flag=None, problem_matcher=None, check_flags=('--check',), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/hukkin/mdformat', tag_pattern=None, config_docs_url='https://mdformat.readthedocs.io/en/stable/users/configuration_file.html', cli_docs_url='https://mdformat.readthedocs.io/en/stable/users/installation_and_usage.html', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run mdformat -- .\n```\n\n**Minimal `[tool.mdformat]`:**\n\n```toml\n[tool.mdformat]\nwrap = "no"\n```\n\nmdformat rewrites Markdown in place. repomatic bundles a plugin set (GFM,\nMyST, front-matter, and others) and a baseline `mdformat.toml`;\n`[tool.mdformat]` in your `pyproject.toml` overrides it.'), 'mypy': ToolSpec(name='mypy', display_name=None, version='2.4.0', package=None, module=None, native_config_files=(), config_flag='--config-file', native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=True, pyproject_table=None, default_flags=('--color-output',), ci_flags=(), default_args=(), default_paths='python_files', per_file=False, with_packages=(), path_tools=(), needs_venv=True, install_into_venv=False, computed_params=<function <lambda>>, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/python/mypy', tag_pattern=None, config_docs_url='https://mypy.readthedocs.io/en/stable/config_file.html', cli_docs_url='https://mypy.readthedocs.io/en/stable/command_line.html', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run mypy -- .\n```\n\n**Minimal `[tool.mypy]`:**\n\n```toml\n[tool.mypy]\nstrict = true\n```\n\nmypy runs inside the project virtualenv (via `uv run`) so it can import your\ndependencies. repomatic derives `--python-version` from `requires-python`,\nso the check matches your lowest supported interpreter. In a repository\nwithout a `uv.lock` there is no project virtualenv to freeze, so mypy runs\nin an isolated environment instead and only resolves the standard library:\nfine for standalone scripts, but dependency imports then report\n`import-not-found`.\n\n`uv run` provisions only the default dependency groups, so a module that\nimports a dep declared solely in a non-default group (`docs`, `typing`, …)\nsees it as missing and mypy reports `import-not-found`. Either move the\nstub/dependency somewhere mypy resolves, or silence it with an override:\n\n```toml\n[[tool.mypy.overrides]]\nmodule = "the_docs_only_package.*"\nignore_missing_imports = true\n```'), 'npm': ToolSpec(name='npm', display_name=None, version='12.2.0', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=NpmSpec(), source_url='https://github.com/npm/cli', tag_pattern=None, config_docs_url=None, cli_docs_url='https://docs.npmjs.com/cli/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run npm -- --version\n```\n\nThe npm every npm-backed tool installs through, like awesome-lint. The npm\non `PATH` only installs this pinned one into the scratch prefix of the run,\nwhich then installs the tool: an npm older than 11.10.0 ignores the\n`minimum-release-age` cooldown, and the one a runner image carries is not\npinned at all. Node.js still comes from `PATH`.'), 'nuitka': ToolSpec(name='nuitka', display_name='Nuitka', version='4.3.1', package='nuitka[onefile]', module='nuitka', native_config_files=(), config_flag=None, native_format=<NativeFormat.FLAGS: 'flags'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=('--mode=onefile', '--assume-yes-for-downloads', '--update-check=never'), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=True, install_into_venv=True, computed_params=<function nuitka_params>, prepare=<function prepare_nuitka_build>, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/Nuitka/Nuitka', tag_pattern=None, config_docs_url='https://nuitka.net/doc/user-manual.html', cli_docs_url='https://nuitka.net/doc/user-manual.html', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run nuitka -- --project-name=papaya \\\n    --main-entry-point=papaya=papaya.__main__:main\n```\n\n`--main-entry-point` takes a `[project.scripts]` entry, written\n`name=module:function`, and compiles the binary that console script would\nstart. It needs `--project-name`, which names the build folders.\n\n**Minimal `[tool.nuitka]`:**\n\n```toml\n[tool.nuitka]\nonefile = true\noutput-dir = "build"\n```\n\nrepomatic reads every key from `[tool.nuitka]` and forwards it as a CLI\nflag: `true` becomes a bare `--flag`, a string or number becomes\n`--key=value`, and a list repeats the flag once per item. Nuitka `4.2` reads\nthe section itself, but only under `--project`\n([Nuitka#3909](https://github.com/Nuitka/Nuitka/issues/3909)). That mode\ntakes its entry points from the project configuration, not from the command\nline, so repomatic keeps its own bridge.\n\nBinaries skip `tkinter` by default, via the `nuitka.nofollow-imports`\nsetting of [`[tool.repomatic]`](configuration.md): set it to `[]` to bundle\nTcl/Tk in a GUI project.\n\nIn a project with a `uv.lock`, repomatic installs Nuitka into the project\nvirtualenv before the build, and Nuitka stays there until the next `uv\nsync`. The compile cache depends on that fixed path: see [Compile\ncaching](nuitka.md#compile-caching).'), 'oxipng': ToolSpec(name='oxipng', display_name='Oxipng', version='10.2.1', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/shssoichiro/oxipng/releases/download/v{version}/oxipng-{version}-aarch64-unknown-linux-gnu.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/shssoichiro/oxipng/releases/download/v{version}/oxipng-{version}-x86_64-unknown-linux-gnu.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/shssoichiro/oxipng/releases/download/v{version}/oxipng-{version}-aarch64-apple-darwin.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/shssoichiro/oxipng/releases/download/v{version}/oxipng-{version}-x86_64-apple-darwin.tar.gz', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/shssoichiro/oxipng/releases/download/v{version}/oxipng-{version}-x86_64-pc-windows-msvc.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'f51bb7c9836202da52a8181549641940662aa87ec2e3ae68115d822d893015b3', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '46e3c4beb9aae57290ad809dd3374b07153579d3322a3778c53900633618b7c6', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '7039fcfc78e8aa1ed2b57d848057a0296f082e92b3e1807ac65402d10d926764', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '111883bbe42b25e01cb1ca41f39f8094e4830b173f0e42be342ecc4ca48f3131', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '7e940f83ee46874b73f53031f96a15834cb70b220af27391fb06fe7b4dd798e1'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=1), npm=None, source_url='https://github.com/shssoichiro/oxipng', tag_pattern=None, config_docs_url=None, cli_docs_url='https://github.com/shssoichiro/oxipng#usage', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run oxipng -- --opt 4 --strip safe image.png\n```\n\nLossless PNG optimizer. `repomatic format-images` reaches it through\n{func}`repomatic.tooling.binaries.ensure_binary`, so the pinned,\nchecksum-verified build is used instead of whatever the runner image or the\ndistro archive supplies.'), 'pyproject-fmt': ToolSpec(name='pyproject-fmt', display_name=None, version='2.30.3', package=None, module=None, native_config_files=('pyproject-fmt.toml',), config_flag='--config', native_format=<NativeFormat.TOML: 'toml'>, default_config=None, reads_pyproject=True, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths='pyproject_files', per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=1, binary=None, npm=None, source_url='https://github.com/tox-dev/pyproject-fmt', tag_pattern=None, config_docs_url='https://pyproject-fmt.readthedocs.io/en/latest/', cli_docs_url='https://pyproject-fmt.readthedocs.io/en/latest/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run pyproject-fmt -- pyproject.toml\n```\n\n**Minimal `[tool.pyproject-fmt]`:**\n\n```toml\n[tool.pyproject-fmt]\nindent = 4\n```\n\npyproject-fmt normalizes and reorders `pyproject.toml` in place. It reads\nits own `[tool.pyproject-fmt]` section natively.'), 'ruff': ToolSpec(name='ruff', display_name='Ruff', version='0.17.0', package=None, module=None, native_config_files=('.ruff.toml', 'ruff.toml'), config_flag='--config', native_format=<NativeFormat.TOML: 'toml'>, default_config='ruff.toml', reads_pyproject=True, pyproject_table=None, default_flags=('--config=force-exclude=true',), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/astral-sh/ruff', tag_pattern=None, config_docs_url='https://docs.astral.sh/ruff/configuration/', cli_docs_url='https://docs.astral.sh/ruff/configuration/#command-line-interface', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run ruff -- check .\n```\n\n**Minimal `[tool.ruff]`:**\n\n```toml\n[tool.ruff]\nline-length = 100\n```\n\n`ruff check .` lints; `ruff format .` reformats. Both read `[tool.ruff]`\nnatively. With no project config, repomatic falls back to its bundled\n`ruff.toml` baseline.\n\nThe default `--config=force-exclude=true` applies `exclude` and\n`extend-exclude` to a path named on the command line too, as in `repomatic\nrun ruff -- check {path}`. ruff alone honours those lists only while it\nwalks a directory, and lints or reformats an excluded file handed to it by\nname. To process such a file once, pass `--no-force-exclude` after the\nsubcommand.'), 'shellcheck': ToolSpec(name='shellcheck', display_name='ShellCheck', version='0.11.0', package=None, module=None, native_config_files=(), config_flag=None, native_format=<NativeFormat.YAML: 'yaml'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=(), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/koalaman/shellcheck/releases/download/v{version}/shellcheck-v{version}.linux.aarch64.tar.xz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/koalaman/shellcheck/releases/download/v{version}/shellcheck-v{version}.linux.x86_64.tar.xz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/koalaman/shellcheck/releases/download/v{version}/shellcheck-v{version}.darwin.aarch64.tar.xz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/koalaman/shellcheck/releases/download/v{version}/shellcheck-v{version}.darwin.x86_64.tar.xz', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/koalaman/shellcheck/releases/download/v{version}/shellcheck-v{version}.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '56affdd8de5527894dca6dc3d7e0a99a873b0f004d7aabc30ae407d3f48b0a79', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '3c89db4edcab7cf1c27bff178882e0f6f27f7afdf54e859fa041fca10febe4c6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '8a4e35ab0b331c85d73567b12f2a444df187f483e5079ceffa6bda1faa2e740e'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_XZ: 'tar.xz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components={Group(id='all_platforms', name='All platforms'): 1, Platform(id='windows', name='Windows'): 0}), npm=None, source_url='https://github.com/koalaman/shellcheck', tag_pattern=None, config_docs_url='https://github.com/koalaman/shellcheck/wiki/Directive', cli_docs_url='https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run shellcheck -- script.sh\n```\n\nShellCheck lints shell scripts. repomatic puts it on `PATH` for `repomatic\nrun actionlint`, which lints the `run:` scripts of the workflows with it. It\nreads its own `.shellcheckrc` and the `# shellcheck` directives of each\nscript, so there is no `[tool.shellcheck]` section.'), 'shfmt': ToolSpec(name='shfmt', display_name=None, version='3.14.1', package=None, module=None, native_config_files=('.editorconfig',), config_flag=None, native_format=<NativeFormat.EDITORCONFIG: 'editorconfig'>, default_config=None, reads_pyproject=False, pyproject_table=None, default_flags=('--write',), ci_flags=(), default_args=(), default_paths='shfmt_files', per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/mvdan/sh/releases/download/v{version}/shfmt_v{version}_linux_arm64', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/mvdan/sh/releases/download/v{version}/shfmt_v{version}_linux_amd64', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/mvdan/sh/releases/download/v{version}/shfmt_v{version}_darwin_arm64', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/mvdan/sh/releases/download/v{version}/shfmt_v{version}_darwin_amd64', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/mvdan/sh/releases/download/v{version}/shfmt_v{version}_windows_amd64.exe'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '5f2db09dae91fca848f7adbdd014632e921a383863a2ad7e0450ad3aba0c6489', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '76e77641faa025814b77f153b29796b8e6fa2fca03e0c76a691608b86c7ea7bf', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'b7c872db63553ccffc7253aba3ed7d4885a27d83f1ba567b1138c6315a5847e5', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'd33eee0da0f92835b3562e9767a05cee7e4eaeef47daa03bfd09da17b4b590a6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '13629ce28442ca80b6b5a819f7574ab39e1c28c6e26734ca816c9714e04851df'}, archive_format=<ArchiveFormat.RAW: 'raw'>, archive_executable=None, strip_components=0), npm=None, source_url='https://github.com/mvdan/sh', tag_pattern=None, config_docs_url='https://github.com/mvdan/sh/blob/master/cmd/shfmt/shfmt.1.scd', cli_docs_url='https://github.com/mvdan/sh#shfmt', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run shfmt -- .\n```\n\nshfmt formats shell scripts in place. It has no `[tool.shfmt]` section:\nindentation and style come from `.editorconfig` (`indent_size`,\n`shell_variant`, and the `shfmt`-specific keys).'), 'typos': ToolSpec(name='typos', display_name=None, version='1.51.1', package=None, module=None, native_config_files=('typos.toml', '_typos.toml', '.typos.toml'), config_flag='--config', native_format=<NativeFormat.TOML: 'toml'>, default_config=None, reads_pyproject=True, pyproject_table=None, default_flags=('--force-exclude', '--write-changes'), ci_flags=(), default_args=(), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=BinarySpec(urls={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/crate-ci/typos/releases/download/v{version}/typos-v{version}-aarch64-unknown-linux-musl.tar.gz', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/crate-ci/typos/releases/download/v{version}/typos-v{version}-x86_64-unknown-linux-musl.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): 'https://github.com/crate-ci/typos/releases/download/v{version}/typos-v{version}-aarch64-apple-darwin.tar.gz', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/crate-ci/typos/releases/download/v{version}/typos-v{version}-x86_64-apple-darwin.tar.gz', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'https://github.com/crate-ci/typos/releases/download/v{version}/typos-v{version}-x86_64-pc-windows-msvc.zip'}, checksums={(Group(id='linux', name='Linux distributions'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '2ac5e37b3236b3c07dec5cdf1527acf143aec4674a6089b0bf424d021b35e099', (Group(id='linux', name='Linux distributions'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '93c301fd4120ff076e2cc2a6d6a61cab4a4bdb81899287c0938b452e56d1f454', (Platform(id='macos', name='macOS'), Architecture(id='aarch64', name='ARM64 (AArch64)')): '316d50a32c3726a80ab39c6d12141b70080498973c5e8a9b068b12cde78b10c1', (Platform(id='macos', name='macOS'), Architecture(id='x86_64', name='x86-64 (AMD64)')): 'f3451079971633a7f769fb958fe06b0d64b5f1dc46df0854aa1ddb9a6f4031d6', (Platform(id='windows', name='Windows'), Architecture(id='x86_64', name='x86-64 (AMD64)')): '518f759fba43205f17a717656054c1d25bc883455a6296989e9d021c6edb7c59'}, archive_format={Group(id='all_platforms', name='All platforms'): <ArchiveFormat.TAR_GZ: 'tar.gz'>, Platform(id='windows', name='Windows'): <ArchiveFormat.ZIP: 'zip'>}, archive_executable=None, strip_components=0), npm=None, source_url='https://github.com/crate-ci/typos', tag_pattern=None, config_docs_url='https://github.com/crate-ci/typos/blob/master/docs/reference.md', cli_docs_url='https://github.com/crate-ci/typos/blob/master/docs/reference.md', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run typos -- .\n```\n\n**Minimal `[tool.typos]`:**\n\n```toml\n[tool.typos.files]\nextend-exclude = ["*.lock"]\n```\n\ntypos scans the tree and, with repomatic\'s default `--write-changes`, fixes\nwhat it finds. It reads `[tool.typos]` natively; use\n`[tool.typos.default.extend-words]` to map project-specific terms to their\nintended spelling.\n\nThe default `--force-exclude` applies `extend-exclude` to a path named on\nthe command line too, as in `repomatic run typos -- {path}`. typos alone\nhonours that list only while it walks a directory, and rewrites an excluded\nfile handed to it by name.\n\nBecause the `fix-typos` workflow job ships whatever typos rewrites as an\nunattended pull request, guard content where a "correction" is a corruption\nwith `[tool.typos.default.extend-ignore-re]` patterns. The two known traps\nare encoded hashes, whose random letter runs typos happily respells (a Guix\n`(base32 "...")` source hash losing its value to an `an`-to-`and` fix), and\nintentional-typo examples that docs or tests exercise on purpose:\n\n```toml\n[tool.typos.default]\nextend-ignore-re = [\n  \'base32 "[0-9a-z]{52}"\',\n  "\\\\{query\\\\}",\n]\n```'), 'yamllint': ToolSpec(name='yamllint', display_name=None, version='1.38.0', package=None, module=None, native_config_files=('.yamllint', '.yamllint.yaml', '.yamllint.yml'), config_flag='--config-file', native_format=<NativeFormat.YAML: 'yaml'>, default_config='yamllint.yaml', reads_pyproject=False, pyproject_table=None, default_flags=('--strict',), ci_flags=('--format', 'github'), default_args=('.',), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/adrienverge/yamllint', tag_pattern=None, config_docs_url='https://yamllint.readthedocs.io/en/stable/configuration.html', cli_docs_url='https://yamllint.readthedocs.io/en/stable/quickstart.html', docs_notes="**Try it:**\n\n```shell-session\n$ repomatic run yamllint -- .\n```\n\n**Minimal `[tool.yamllint]`:**\n\n```toml\n[tool.yamllint.rules.line-length]\nmax = 120\n```\n\nyamllint has no native `pyproject.toml` support, so repomatic bridges\n`[tool.yamllint]` to a temporary YAML config passed via `--config-file`.\nWith no project config it uses repomatic's strict bundled `yamllint.yaml`."), 'zizmor': ToolSpec(name='zizmor', display_name=None, version='1.30.1', package=None, module=None, native_config_files=('.github/zizmor.yml', '.github/zizmor.yaml', 'zizmor.yml', 'zizmor.yaml'), config_flag='--config', native_format=<NativeFormat.YAML: 'yaml'>, default_config='zizmor.yaml', reads_pyproject=False, pyproject_table=None, default_flags=('--offline',), ci_flags=('--format', 'github'), default_args=('.',), default_paths=None, per_file=False, with_packages=(), path_tools=(), needs_venv=False, install_into_venv=False, computed_params=None, prepare=None, config_after_subcommand=False, post_process=None, output_flag=None, problem_matcher=None, check_flags=(), rewrite_exit_code=None, binary=None, npm=None, source_url='https://github.com/zizmorcore/zizmor', tag_pattern=None, config_docs_url='https://docs.zizmor.sh/configuration/', cli_docs_url='https://docs.zizmor.sh/usage/', docs_notes='**Try it:**\n\n```shell-session\n$ repomatic run zizmor -- .\n```\n\nzizmor audits GitHub Actions workflows for security issues, offline by\ndefault. repomatic bridges `[tool.zizmor]` to a temporary YAML config\n(passed via `--config`); with none, it uses the bundled `zizmor.yaml`. See\nthe [configuration reference](https://docs.zizmor.sh/configuration/) for\navailable keys.')}

Tool name to the specification driving its install, config and invocation.

The single source of truth for repomatic run: every command, workflow job and documentation renderer reads the roster from here rather than hard-coding a tool name, a version or a flag.

Todo

Give the mdformat entry a config_flag="--config" once mdformat accepts an explicit config path, so it stops relying on discovery from the working directory: hukkin/mdformat#432 and hukkin/mdformat#562.

repomatic.tooling.registry.lookup_tool(name)[source]

Return the registry entry of the tool called name.

Raises:

ValueError – When no tool goes by name.

Return type:

ToolSpec

repomatic.tooling.registry.tool_summary()[source]

Render the summary table of all managed tools.

Return type:

str

repomatic.tooling.registry.tool_reference()[source]

Render the per-tool detail sections.

The metadata of each section (version, install, config, flags, links) is generated from the registry; the trailing free-form prose comes from the spec’s own docs_notes field, so hand-written examples and caveats live next to the spec they document.

Return type:

str