This document preserves the upstream release mechanics and CHANGELOG format; code paths are relative to the repository root. It is a reference for changing the release process only and must not be executed directly as personal-release commands: the npm scope, download URLs, main-branch write-back, and the maintainer's working habits belong to upstream. Before a personal release, check your own goals against the maintenance process.
Released to npm and mirrored as GitHub Releases. CHANGELOG.md is the source of truth; GitHub Release notes are extracted from it.
Default: write entries under ## [Unreleased] — that's the section reserved for work landing between releases. Don't pre-create a ## [X.Y.Z] block for the next release: the Release workflow's first step is scripts/prepare-release.mjs, which automatically promotes everything under [Unreleased] into a new ## [X.Y.Z] - <YYYY-MM-DD> block at release time (or merges into a pre-existing [X.Y.Z] block if one exists — but you don't need one). Pre-staging is what caused the v0.9.5 sparse-release-notes incident: a sparse [0.9.5] block hand-added before the rest of the work landed got picked by the extractor over the much-larger [Unreleased] section above it. Don't do that.
Formatting rules for any entry (anywhere — [Unreleased] or otherwise):
- Write friendly, user-facing notes — not engineer-facing ones. Group under
### New Featuresand### Fixes(sentence-case). Surface### Breaking Changesand### Securityas their own sections only when the release has them; fold improvement-flavored changes into New Features. Omit empty sections. (This replaces the old Keep-a-ChangelogAdded/Changed/Fixed/Removed/Deprecatedgrouping: the GitHub Release page extracts each version block verbatim viascripts/extract-release-notes.mjs, and the old dense, implementation-focused entries rendered as an unreadable wall of text — so the whole CHANGELOG was rewritten to this format and every published release re-noted to match.) - One plain-language sentence per bullet: what changed and why it matters to a user. Lead with the capability, or with the symptom that's now fixed.
- Strip the internals. No internal file paths (
src/...), no internal symbol / function / class names, no benchmark numbers / percentages / node-or-edge counts. Keep: language & framework names (Go, Spring, NestJS, …), things a user types or sets (codegraph install,codegraph_explore, theCODEGRAPH_*env vars), agent / IDE names (Claude Code, Cursor, opencode, Kiro, …), and a briefThanks @userwhen a contributor is credited. - Issue / PR references in entries are by number (
(#403)etc.); the GitHub renderer auto-links them in the published release notes. - Don't add a
[X.Y.Z]: https://...link reference yourself —prepare-release.mjsappends it automatically when it promotes the version (idempotent: a re-run is a no-op if it already exists). - Every release opens with a
### Highlightsblock — the only part most people read. At most ~8 one-line bullets, in plain language for someone who doesn't read code, ordered by what a typical user notices first (new agent/IDE support and setup changes, then answer quality, then reliability), plus a one-sentence upgrade note when a re-index is needed. Write or refresh it in[Unreleased]when a release is being prepared — not per PR — and keep the detailed### New Features/### Fixesentries below it. When### Fixesgrows past ~15 entries, group them under####sub-headings (Better answers from codegraph_explore,Finding your project, live updates, and the CLI,Indexing reliability and disk usage,Language and framework accuracy) so a skimmer can find their area.
Multi-word headings like ### New Features are safe on the normal release path: prepare-release.mjs Case A moves the whole [Unreleased] body verbatim into [X.Y.Z]. (Only its rarely-used Case B merge splits sub-sections with a single-word ^### (\w+)$ regex that wouldn't match them — and Case B fires only if a [X.Y.Z] block was pre-created, which rule above already forbids.)
Releases are built and published by the GitHub Actions "Release" workflow
(.github/workflows/release.yml). It runs scripts/prepare-release.mjs to
promote [Unreleased] into [<version>] (and auto-commit + push that
CHANGELOG change back to main so on-disk truth matches the published
notes), then bundles a Node runtime per platform (scripts/build-bundle.sh)
and publishes both the GitHub Release and the npm thin-installer
(scripts/pack-npm.sh: a shim package + per-platform packages).
Publishing manually is wrong now — a plain npm publish ships the root
package (non-bundled), which breaks anyone on Node < 22.5.
Claude does NOT bump the version unless explicitly asked. The maintainer
typically does it themselves — often by editing package.json directly via
the GitHub web UI. Don't proactively commit a version bump as part of
unrelated work, and don't propose one when summarizing a PR.
When the maintainer DOES bump the version, the only edit strictly required is
to package.json — the workflow's "Sync package-lock.json" step detects a
mismatch between package.json and package-lock.json, runs
npm install --package-lock-only --ignore-scripts to rewrite the lock file's
version fields (top-level + packages.""), and auto-commits + pushes the
result back to main with [skip ci]. So a GitHub-web-UI single-file edit to
package.json is enough to kick off a clean release. (If they edit both files
locally, that's fine too — the sync step no-ops.)
Once package.json is at the target version on main, trigger
Actions → Release → Run workflow (on main). The workflow:
- Syncs
package-lock.jsontopackage.json's version if they've drifted; commits + pushes that change. - Runs
prepare-release.mjs <X.Y.Z>→ promotes[Unreleased]→[X.Y.Z] - <today>inCHANGELOG.md, appends the link reference, commits + pushes the move with[skip ci]. - Builds every platform bundle on one runner, generates
SHA256SUMS. - Creates the GitHub Release with notes from the freshly-promoted
[X.Y.Z]block. - Publishes the npm shim + per-platform packages. Requires the
NPM_TOKENrepo secret.
Commits, pushes, tags, and releases in the personal fork run only with user authorization; the upstream workflows must first be adapted to the personal release goals.