Changelog
Record individual changes and release versioned changelogs with a single command that guides you through each step.
Summary
The changelog command combines two workflows into one guided flow:
- Recording a change (what happened).
- Releasing accumulated changes (bump versions and write a
CHANGELOG.md).
In interactive mode, a menu lets you pick which action to take. In non-interactive mode, the --record and --release flags drive the same workflows from scripts and CI/CD pipelines.
Recorded entries are stored as individual .md files in a .changelog/ directory at the monorepo root. When released, the entries are consumed, versions are bumped in each affected package.json, and a CHANGELOG.md is written (or updated) in the workspace directory.
Why Use This Command?
- Combines the record-and-release lifecycle into one command instead of separate tools.
- Interactive prompts guide you through package selection, category, description, and SemVer bump type. No flags to memorize.
- The
--recordand--releaseflags enable the same workflow in CI/CD without prompts. - The dry run option lets you preview every change before any file is touched.
Use Cases
- Solo projects — Record a change and release it in the same session without learning a separate versioning tool.
- Team workflows — Each contributor records changes in their PR. A maintainer runs the release step when ready.
- CI/CD automation — Use
--recordin a pipeline step to capture changes, then--releasein a publish step to bump versions and write changelogs. - Migration from Changesets — Replace
changeset addwithnova utility changelog --recordandchangeset versionwithnova utility changelog --release.
Requirements
- Node.js runtime — Use any Node.js LTS release with either the installed
novaCLI ornpx. - Project root — Run the command from the directory containing the top-level
package.json(monorepos included). - Nova config — A valid
nova.config.jsonmust exist with at least one non-freezable workspace defined.
Usage
You can run this command in two ways:
Options
| Flag | Description |
|---|---|
--record | Record a change. Prompts for missing values unless all flags are provided. |
--release | Release accumulated changes. |
-p, --package <name> | Package name for recording. |
-c, --category <category> | Category: updated, fixed, added, removed. |
-b, --bump <type> | Bump type: major, minor, patch. |
-m, --message <message> | Change description. |
-d, --dry-run | Run without writing any files. |
How It Works
Record Flow
- Select package — Auto-selects if only one non-freezable workspace exists. Otherwise prompts.
- Select category — Choose from Updated, Fixed, Added, or Removed.
- Enter description — Free-text description of the change.
- Select bump type — Pre-selected based on category (Added/Updated = minor, Fixed = patch, Removed = major). Override if needed.
- Save — Writes an entry file to
.changelog/with a random adjective-noun-verb filename.
Release Flow
Steps 4–6 run once per releasing package in a loop. Step 7 runs once after all packages are processed.
- Read entries — Collects all
.mdfiles from.changelog/. - Show summary — Lists entries grouped by package with the computed version bump (highest bump wins).
- Confirm — Interactive only. Non-interactive mode (
--release) skips this step. - Bump version — Updates the
versionfield in each affectedpackage.json. - Write CHANGELOG.md — Prepends a new version section. Creates the file if it does not exist.
- Stamp sentinels — Replaces every
@since UNRELEASEDand@deprecated UNRELEASEDtag in the package'ssrc/tree (.tsand.tsxfiles) with the new version. Skipped when the new version is a prerelease (contains a hyphen). Fails the release with a non-zero exit code if any sentinel survives after stamping. - Clean up — Deletes consumed entry files from
.changelog/.
Non-Zero Exit Code
The command exits with code 1 when validation fails (invalid flags, missing config, unknown package). CI pipelines that use this command will fail the step on errors.
UNRELEASED Sentinel Convention
When you add or change a public API member, tag it with the literal sentinel @since UNRELEASED (or @deprecated UNRELEASED) rather than a hand-written version number. The release step stamps the real version at release time, so you never have to guess or update the tag manually.
- Write
@since UNRELEASEDfor new members and@deprecated UNRELEASEDfor members being deprecated. - Never write a concrete version (e.g.,
@since 1.3.0) for unreleased code — it will be wrong if the bump type changes before release. - Concrete versions are only appropriate for members that have already shipped in a published release.
// Before release (what you write):
/**
* Returns the current config version.
*
* @since UNRELEASED
*/
export function getConfigVersion(): string { ... }
// After `nova utility changelog --release` (what gets stamped):
/**
* Returns the current config version.
*
* @since 1.4.0
*/
export function getConfigVersion(): string { ... }
Entry File Format
Each recorded change is stored as an individual .md file in .changelog/:
---
package: "@cbnventures/nova"
category: added
bump: minor
---
New ESLint rule for switch case blocks.
CHANGELOG.md Format
Released entries are written in the following format. Headings are ordered as UPDATED, FIXED, ADDED, REMOVED.
Empty headings are omitted.
# @cbnventures/nova
## 1.3.0 - 2026-02-25
### FIXED
- Crash in initialize command.
### ADDED
- New ESLint rule for switch case blocks.
Examples
Migration from Changesets
For users migrating from the Changesets workflow, Nova provides equivalent flags under nova utility changelog:
| Command | Equivalent | What It Does |
|---|---|---|
nova utility changelog --record | changeset add | Record a change with -p, -c, -b, -m flags. |
nova utility changelog --release | changeset version | Release accumulated changes, bump versions, and stamp @since/@deprecated UNRELEASED tags in source. |
Shorten long commands with scripts
Nova bundles many tools in one package, so commands can be verbose. Shorten them by adding aliases to the "scripts" field in your root package.json (the one at the monorepo root):
{
"scripts": {
"changelog:record": "nova utility changelog --record",
"changelog:release": "nova utility changelog --release"
}
}
Then run npm run changelog:record or npm run changelog:release.
Troubleshooting
- "No eligible (non-freezable) workspaces found" — Confirm that
nova.config.jsonhas at least one workspace with a trackable or distributable workspace policy. - "Cannot use --record and --release together" — These flags are mutually exclusive. Run them as separate commands.
- "Non-interactive record requires --package, --category, --bump, and --message" — All four flags are required when using
--record. Omit--recordfor interactive mode. - "No changelog entries found" — The
.changelog/directory is empty or does not exist. Record at least one change before releasing. - "Package not found in nova.config.json" — The
--packagevalue must match thenamefield of a non-freezable workspace innova.config.json. - "UNRELEASED sentinel survived stamping" — The release step found one or more
@since UNRELEASEDor@deprecated UNRELEASEDtags insrc/that were not replaced. This means a file was added or moved after the stamp pass searched it, or the file extension is not.tsor.tsx. Locate the surviving tag withgrep -r "UNRELEASED" src/and update it manually to the released version, then re-run any post-release steps.