* fix(#2488): strip leading terminators so changeset bullets survive re-parse A fragment body beginning with a line terminator rendered as an empty `- ` bullet followed by an orphaned paragraph. `parseChangelog` treats a non-indented line as terminating a bullet, so `github-release-notes.cjs` silently dropped the entry when re-parsing CHANGELOG.md to build the GitHub Release body. Two independent causes, both in scripts/changeset/parse.cjs: 1. `extractDocsExempt` stripped trailing terminators but not leading ones. `DOCS_EXEMPT_RE` is `^...$` under /m, so removing a first-line `<!-- docs-exempt -->` marker left the `\n` that `$` does not consume. 2. `parseFragment` preserved the post-frontmatter body verbatim, so a blank line between the closing `---` and the first content line produced the same leading `\n` with no marker involved. 8 of 256 pending fragments were affected, split 4/4 across the two causes — including the OpenCode MCP binding, the pi extension, and the EoS adapters, all of which would have vanished from the v1.8.0 release notes. Regression tests cover both causes in LF and CRLF form, plus an end-to-end serializeChangelog -> parseChangelog round-trip that pins the user-visible defect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#2488): regenerate golden install fixtures for parse.cjs scripts/ ships in the npm package and the installer, so the golden install-parity fixtures record a content hash for every shipped file. Editing scripts/changeset/parse.cjs drifts that hash and fails all 18 per-runtime parity tests. Regenerated via `npm run gen:golden`. The diff is exactly one line per fixture — the scripts/changeset/parse.cjs hash — with no unrelated drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
changeset/ — release-notes tooling
This directory holds the scripts that turn per-PR fragments in .changeset/
and git history into the project's CHANGELOG.md and GitHub release notes.
The entry point is cli.cjs. It exposes three subcommands:
| Subcommand | Purpose |
|---|---|
render |
Render a single version's changelog section from consolidated data. |
github-release-notes |
Build GitHub release-notes body for a ref range. |
extract |
Pull existing CHANGELOG.md entries that fall in a version range. |
The rest of this document specifies the extract contract, because it is the
surface most likely to be called by external tooling (CI workflows, npm scripts,
release automation) that needs a stable exit-code and output guarantee to code
against.
cli.cjs extract
Extract the changelog entries for every release in a version range, reading from
an existing CHANGELOG.md. The range is --from exclusive, --to inclusive.
node scripts/changeset/cli.cjs extract --from VERSION --to VERSION \
[--changelog FILE] [--repo <dir>] [--json]
Flags
| Flag | Required | Description |
|---|---|---|
--from VERSION |
Yes | Lower bound, exclusive — entries equal to --from are not returned. |
--to VERSION |
Yes | Upper bound, inclusive — entries equal to --to are returned. |
--changelog FILE |
No | Path to the changelog to read. Defaults to <repo>/CHANGELOG.md. |
--repo <dir> |
No | Repo root used to locate CHANGELOG.md when --changelog is omitted. Defaults to the current working directory. |
--json |
No | Emit the structured report as JSON instead of rendered markdown. |
Version validation
Both --from and --to must be stable triplet semver — MAJOR.MINOR.PATCH,
digits only.
- A leading
vis accepted and stripped:v1.42.0is treated as1.42.0. - Pre-release and build suffixes are rejected:
1.42.0-rc.1,1.42.0+build, and partial versions like1.42.xall fail validation and exit1.
Strict validation is deliberate. Coercing a malformed bound such as 1.42.x to
1.42.0 would silently change which releases the range selects, so a malformed
bound is rejected early with a structured error rather than guessed at.
Changelog entries that are themselves pre-release or non-semver (and the
Unreleased section) are skipped during matching; a notice for each skipped
entry is written to stderr.
Exit codes
extract resolves to one of three exit codes. The output shape depends on
whether --json is passed.
| Exit | Meaning | Default stdout | --json stdout |
|---|---|---|---|
0 |
One or more releases fall in the range. | Rendered markdown for the matched releases. | { "releases": [ ... ], "from": "...", "to": "..." } |
1 |
Bad input: --from/--to is not stable semver, a required flag is missing, or the changelog file was not found. |
Nothing (a missing-flag error and usage go to stderr). | { "error": "<message>", "releases": [] } |
2 |
Bounds are valid but no release falls in the range. | A no releases found in range notice on stderr. |
{ "releases": [], "from": "...", "to": "..." } |
Notes for callers:
- Treat exit
2as "empty range", not "failure". For a well-formed invocation it means the request was understood and simply matched nothing — do not surface it as an error. (At the argument-parsing layer, malformed argv such as an unknown flag also exits2; pass well-formed arguments and this overlap does not arise.) - In default (text) mode, a failure is signalled by the exit code alone —
exit
1from invalid semver or a missing changelog writes nothing to stdout. Machine consumers should pass--jsonto receive theerrorfield.
Output shape
With --json, the report is pretty-printed JSON. The releases array contains
one object per matched release (version, date, and parsed sections); from and
to echo the normalized bounds. On exit 1, releases is empty and an error
string describes the failure.
Without --json, exit 0 prints the matched releases as markdown, ready to
paste into release notes:
## [1.42.0] - 2026-01-15
### Added
- New `--json` flag on the extract command (#3796)
### Fixed
- Trailing-slash handling in config paths (#3651)
Examples
Extract everything released after 1.41.0 up to and including 1.42.0:
node scripts/changeset/cli.cjs extract --from 1.41.0 --to 1.42.0
The same range as structured JSON, reading an explicit changelog file:
node scripts/changeset/cli.cjs extract \
--from v1.41.0 --to v1.42.0 \
--changelog ./CHANGELOG.md --json
Handle the three outcomes in a shell consumer:
if out=$(node scripts/changeset/cli.cjs extract --from "$FROM" --to "$TO" --json); then
echo "$out" # exit 0 — releases found
else
case $? in
2) echo "no releases in range — nothing to publish" ;; # not an error
*) echo "extract failed: $out" >&2; exit 1 ;; # exit 1 — bad input
esac
fi