Molt
Changeset-driven versioning for Python

Intent-based versioning, changelogs, and publishing for Python packages and monorepos. A Python-native port of changesets, built on PEP 440.

🐍

PEP 440 native

Version and specifier math built on packaging, not SemVer. Prereleases (a/b/rc/.dev), epochs, local versions, and PEP 503 name normalization are handled the way pip actually resolves them.

🔗

Real monorepo propagation

A fixpoint release-plan engine bumps dependents across your workspace and rewrites their constraints. This is the moat, the part two prior Python ports never reached.

uv-first, ecosystem-agnostic

First-class uv workspaces with lockfile updates on every version bump. A backend seam covers Poetry, Hatch, PDM, and setuptools instead of hard-wiring one tool.

📦

Single package or monorepo

Single-package repos are first-class, not a degenerate special case. Most Python projects are one package, so molt treats that path as the default, not an afterthought.

📋

A plan on every command

add, version, publish, build, and git-tag each produce a machine-readable plan. Pass --dry-run to print exactly what would happen and write nothing to disk.

🚦

Prerelease is a flag

molt version --pre rc. No pre.json branch state to leak, forget, or merge-conflict. Prerelease is an invocation, and it maps cleanly onto PEP 440 spellings.

♻️

molt yank

PyPI has no unpublish. molt makes PEP 592 yank a first-class verb: it checks the version, says whether it is already yanked, and prints the exact steps. PyPI has no yank API, so the final click is yours.

🪟

Windows-correct from day one

Tested on Windows from the first commit. No cp1252 surprises in output, no path bugs. Your release tool has to run in the CI you already have.