A Claude Code workflow that turns a git tag range into three things:
- a categorized changelog section (Keep a Changelog format)
- a draft GitHub release body
- a short announcement post you can actually paste somewhere
It reads the commits you already wrote. It does not invent features, and if a range has nothing user-facing in it, it tells you so instead of producing filler.
Cutting a release is the chore everyone skips or does badly. You either dump a
raw git log into the release notes, or you write nothing, or you spend twenty
minutes wording an announcement that ends up sounding like every other
announcement. We ship a lot of small repos and needed the same release to come
out the same way every time, from one command, built from the real commits.
So the mechanical parts — figuring out the commit range, parsing the log, sorting commits into Added / Fixed / Removed / etc. — are plain Node scripts with tests. Claude only does the part it's actually good at: turning a sorted list into a readable sentence. And it's handed the commit list as its only source, so it can't drift off into things that didn't happen.
release-radar/
├─ release-radar.workflow.js # the workflow (export const meta + body)
├─ workflow.md # what each phase does
├─ commands/
│ └─ release-radar.md # the /release-radar slash command
├─ scripts/
│ ├─ commit-range.js # zero-dep git-log parser (range resolver + parser)
│ ├─ collect-commits.js # stable alias for the parser
│ ├─ classify-commits.js # commit -> Keep-a-Changelog section classifier
│ ├─ categorize.js # stable alias for the classifier
│ └─ update-changelog.js # renderers + CHANGELOG.md prepend
├─ references/
│ ├─ keep-a-changelog.md # the format the scripts write
│ └─ announcement-voice.md # the voice the announcement is held to
├─ examples/
│ └─ sample-run.md # a real end-to-end run with actual output
└─ test/
├─ categorize.test.js # classifier + parser tests
└─ classify-commits.test.js # renderer + integration tests
No dependencies. Node 20 or newer.
git clone https://github.com/VelkinaStudio/release-radar
cd release-radar
node --test # confirm it works (21 tests)To use the slash command in a project, copy commands/release-radar.md into
that project's .claude/commands/ directory, and copy the scripts/ and
references/ folders somewhere the command can reach them (or keep the whole
repo as a submodule / sibling and adjust the paths in the command).
From inside a git repository:
/release-radar 1.4.0
/release-radar 1.4.0 v1.3.0..HEAD
The first form uses <last-tag>..HEAD. The second takes an explicit range. The
command (defined in commands/release-radar.md) walks Claude through: collect
the commits, categorize them, write the highlights, render all three artifacts,
prepend CHANGELOG.md, and show you the gh command to publish a draft release.
The workflow file itself (release-radar.workflow.js) uses the Claude Code
Workflow format — export const meta = {...} plus a default-exported body that
calls phase() for each step and agent() for the two synthesis steps. The
phase breakdown is documented in workflow.md. If your runner supports it:
claude workflow run release-radar.workflow.js -- 1.4.0 v1.3.0..HEADThe mechanical core works on its own — no harness, no API key. This is the honest part: the changelog, the categorized release body, and a count-only announcement are all produced by plain Node. Only the studio-voice highlights and announcement wording need Claude.
# Inside any git repo:
# Just see the categorized commits since the last tag:
node /path/to/release-radar/scripts/categorize.js
# Explicit range, machine-readable:
node /path/to/release-radar/scripts/collect-commits.js v1.3.0..HEAD
# Produce all three artifacts (templated highlights, real counts):
node /path/to/release-radar/release-radar.workflow.js 1.4.0
# Prepend a changelog section to CHANGELOG.md:
node /path/to/release-radar/scripts/update-changelog.js 1.4.0 --writeSee examples/sample-run.md for a full run with real output.
| What the commit looks like | Where it lands |
|---|---|
feat: ... or "Add / introduce / implement ..." |
Added |
fix: ... or "Fix / resolve / patch ..." |
Fixed |
| "Remove / delete / drop ..." | Removed |
| "Deprecate ..." | Deprecated |
| security signal (CVE, XSS, sanitize, ...) | Security |
perf: ... or "Speed up / optimize ..." |
Changed (labeled Performance) |
docs: ... |
Changed (labeled Documentation) |
chore / ci / test / style, version bumps, merges |
dropped |
Conventional-commit prefixes are used first; subjects without a prefix fall back
to keyword heuristics. The full rules are in references/keep-a-changelog.md.
If no commit supports a line, the line doesn't appear. If the whole range is internal churn, you get told there's nothing to release rather than a hollow set of notes. A short honest changelog beats an invented one.
node --testThe deterministic pieces — range resolution, log parsing, classification, and
the renderers — are covered. The two LLM synthesis steps are not unit-tested
(they need the harness), and that's stated plainly in workflow.md.
MIT. See LICENSE.
Built by Velkina — https://velkina.com