Changelog and Versioning
@katforge/imp treats the changelog and the version as things it keeps
correct for you, not paperwork you maintain by hand. This page is the reference
for what it does, so you can trust the output without re-reading the diff.
The git tag is the version
The canonical version of a repo is its highest semver tag. Everything else —
the CHANGELOG.md heading, the version fields in package manifests — is a copy
@katforge/imp rewrites to match the tag, so the two can never drift.
imp version # show each manifest against the tag (exit 1 on drift)
imp version --sync # rewrite drifted manifests to match the tag
The plain form exits non-zero on drift, so it gates CI. --sync leaves the
change uncommitted for the next commit or release to carry.
One entry engine, two speeds
Every changelog entry comes from one generator, run at one of two speeds:
- Diff-aware (default for humans). It reads the actual per-version diffs and asks the model to describe them from a user's perspective, sorting each change into Added, Changed, Fixed, or Removed.
- Deterministic (
--fast, and non-interactive runs). It reads the commit subjects and maps each Conventional Commit type to a category. The same commits always produce the same entry, and nothing waits on a model.
| Commit type | Deterministic category |
|---|---|
feat: | ### Added |
fix: | ### Fixed |
| anything else | ### Changed |
The diff-aware path adds a ### Removed category the deterministic path does not
produce. Either way, the commit types in
changelog:skip — chore, release, merge by
default — are dropped so release and merge noise never reaches an entry.
Which speed runs where:
| Command | Speed |
|---|---|
imp release (interactive) | diff-aware |
imp release --yes, imp ship, imp tag | deterministic (cheap, CI-safe) |
imp changelog | diff-aware |
imp changelog --fast | deterministic |
Non-interactive releases stay on the deterministic entry on purpose: a CI or agent release is reproducible and never blocks on a model. Interactive releases get the richer diff-aware entry, and you review it before it lands.
Writing the file with imp changelog
imp changelog maintains CHANGELOG.md directly, without cutting a release.
imp changelog # top up [Unreleased] from commits since the last tag
imp changelog --fast # same, deterministic entry
imp changelog --rebuild # regenerate the whole file from history
imp changelog --rebuild --apply # also create or move the git tags it infers
imp changelog --since 2026 # limit the range to a date, tag, or commit
By default it is incremental: it generates an entry for the commits since the
last tag and updates the [Unreleased] section in place, leaving released
history and any hand edits alone. Running it twice never stacks a second
[Unreleased] heading.
--rebuild regenerates the entire file from git history. It maps existing tags
to version boundaries and, where a stretch of commits has no tags, infers where
the releases should fall from the subjects. --rebuild --apply also creates or
moves the git tags it inferred. Untagged trailing commits collect under
[Unreleased].
Both paths write Keep-a-Changelog format from one shared header:
# Changelog
All notable changes to this project will be documented in this file.
## [1.2.0] - 2026-07-17
...
Manifests move with the tag
At release,
@katforge/imp rewrites the version field in the package files it
recognizes so a publish ships the released version, not a stale one. It handles
package.json, composer.json, pyproject.toml, and cli/pyproject.toml;
manifests with a dynamic (VCS-derived) version are left alone. A project can name
its manifests explicitly in .imp instead of
relying on detection.
Pre-release versions are translated to each ecosystem's native spelling — see Package manifests for the full table.
Release rolls back on failure
A release touches several things in sequence: the changelog, the manifests, a
squash, a tag, a push. If any step fails,
@katforge/imp restores what it
changed rather than leaving a half-cut release:
- The new tag is deleted.
HEADhard-resets to the pre-release commit, if the release commit was made.CHANGELOG.mdis restored to its exact prior content, or removed if it did not exist before.- Every rewritten manifest is restored from its captured original.
The one case
@katforge/imp does not unwind is a push that landed the
commits but failed to push the tag: the work is already public, so
@katforge/imp leaves it and tells you the exact command to finish the tag
push. Nothing is silently half-done.
Keeping it honest
The categories, the reproducible deterministic entry, and the version-manifest lockstep are what make the changelog reviewable at a glance. The same engine that reads a diff for the changelog also keeps the prose documentation as honest — that is Documentation sync.