Publish a changelog for your app
Ship a CHANGELOG.md with your Pear app so users read release notes over peer-to-peer with the pear changelog command.
Any Pear application can ship a CHANGELOG.md. Stage it with the rest of your project and your users—or any third party—read your release notes straight from the app drive with pear changelog pear://<link>, semver-filtered and pretty-printed. It is the same command that prints the Pear platform's own changelog when you run pear changelog with no link (a known regression makes the no-link form error on some 3.0.x builds—pass an explicit pear://<link>, as this guide does throughout).
The changelog lives in the app's Hyperdrive, so it is versioned, replicated, and readable peer-to-peer with no server and no separate publishing step—it travels with every stage and provision.
This is your application's own changelog. It is distinct from the docs-team Release Overview, which is a curated view of changes across Pear and its modules. To publish release notes for your app, follow this guide.
How it works
pear changelog reads a single file at the root of your app drive: /CHANGELOG.md. Under the hood the pear-changelog module parses it into one entry per release, then the command filters by version and prints each entry's notes. The filename and drive-root location are the entire contract—there is no manifest field to set and nothing to register.
Write CHANGELOG.md
Create a CHANGELOG.md at your project root (next to package.json). The format is lightweight:
- A header block at the top (typically a
# Title)—everything before the first##heading is ignored by the parser. Don't start the file with a##heading: the parser only recognizes headings that follow a line, so the first release would be dropped. - One release per level-2 heading (
## <version>), newest first. - The first space-separated token of each heading must be a SemVer version (a leading
vis allowed and stripped). Everything under a heading until the next##is free-form Markdown.
# Acme Chat Changelog
## v1.2.0
### Features
- Group threads with @mentions.
### Fixes
- Reconnect automatically after the sidecar restarts.
## v1.1.0
### Features
- Full-text message search.
## v1.0.0
Initial release.Keep the version as the first word of the heading, followed by a space. Bracketed Keep a Changelog headings such as ## [1.2.0] - 2026-01-01 aren't valid versions—those releases drop out of the default view and of any --of range (only --full still shows them). Use ## v1.2.0 or ## 1.2.0 instead. You can still add a date after the version, for example, ## v1.2.0 — 2026-01-01.
Check the format locally
Before staging, confirm each release parses—with the same pear-changelog module the CLI uses. This checks that headings are recognized; it does not apply the SemVer filter, so keep the version-format rules above in mind too. Install it as a dev dependency:
npm install --save-dev pear-changelogCreate check-changelog.js next to the changelog:
const { readFileSync } = require('node:fs')
const { parse } = require('pear-changelog')
for (const [version] of parse(readFileSync('CHANGELOG.md'))) {
console.log('release:', version)
}Run it with Node:
node check-changelog.jsIt prints one line per release the parser found, newest first:
release: v1.2.0
release: v1.1.0
release: v1.0.0If a release is missing from the output, its heading didn't parse—pear changelog won't show it either. The reverse isn't guaranteed: a release can parse here yet still be filtered out by pear changelog when its version isn't valid SemVer (see the format warning above).
Stage and seed it with your app
A root CHANGELOG.md is staged like any other project file—no extra flags. Just make sure it is not excluded by a pear.stage.ignore entry or dropped by a pear.stage.only filter that omits it.
Deploying a desktop app? The deployment directory that pear build assembles contains only package.json and by-arch/—your project's CHANGELOG.md is not copied in. Add it before staging: cp CHANGELOG.md ../<name>-<version>/.
pear stage --dry-run pear://<link> # confirm CHANGELOG.md appears in the diff
pear stage pear://<link>Then pear seed the link so peers can fetch it. Once staged, the file is retrievable at pear://<link>/CHANGELOG.md. Bump CHANGELOG.md in the same commit as your version bump so each release carries its own notes—see Ship your app for the full stage → provision flow.
Read it back
Anyone with the link reads your changelog with:
pear changelog pear://<link>By default this prints up to the 10 newest releases within the current major version, each with its full notes, separated by a divider. Tune it with the flags:
| Flag | Effect |
|---|---|
--of <semver> | Filter to a version range, for example, --of 1.x.x or --of '>=1.1.0 <2.0.0'. Defaults to the latest major. |
--max, -m <n> | Cap the number of entries shown (default 10). |
--full | Show every release (overrides --max); without --of, it also widens the range to all majors. |
--json | Emit newline-delimited JSON—one tagged object per release plus a final status object. Filter on "tag": "changelog" when scripting. |
pear changelog pear://<link> --of 1.x.x # only the 1.x releases
pear changelog pear://<link> --full # the entire history
pear changelog pear://<link> --json # machine-readableUse it for release announcements
Because the changelog is queryable by version range, it doubles as your source of truth for release notes. When you cut a release, pull the exact entries for the range you shipped and paste them into your announcement:
pear changelog pear://<link> --of '>=1.1.0' # everything since 1.1.0Relationship to pear dump
pear changelog is the structured view of the same file that pear dump returns raw:
pear dump pear://<link>/CHANGELOG.md - # raw Markdown, unparsed, to stdoutUse pear dump when you want the exact file bytes; use pear changelog when you want it parsed, semver-filtered, and formatted.
See also
pear changelogreference—the command and its flags.pear-changelogmodule—the parser that defines the format, with adiff()API for comparing changelogs.- Ship your app—the stage and provision flow the changelog travels with.
- Deploy your application—the full operator release flow.
- Configuration—
pear.stageoptions that control what gets staged.