changelog-writer
Turns the git log between two tags into a clean changelog.
| Size | Folder | Inside |
|---|---|---|
| 8.5 kB | scripts/ | 1 script |
| 3.1 kB | skills/ | 1 skill |
| 6 kB | (root) | 2 docs · 1 config file |
| Size | Path |
|---|---|
| 30 B | .gitignore |
| 1.1 kB | LICENSE |
| 4.3 kB | README.md |
| 538 B | package.json |
| 8.5 kB | scripts/changelog.mjs |
| 3.1 kB | skills/changelog.md |
Already bought this?
View your purchasesREADME
changelog-writer
Turn git log between two tags into a changelog draft, then edit it into release notes.
One script and one skill. The script reads Conventional Commits when a repo uses them and degrades to a clean flat list when it does not, because most repos do not and a tool that only runs on tidy history is a tool you cannot use today.
No dependencies. Node 18 or newer. It runs git log, git tag and git remote, and writes nothing to git.
Usage
node scripts/changelog.mjs v1.0.0 v1.1.0
Argument handling is the shape you would guess:
| Command | Range |
|---|---|
changelog | newest tag ..HEAD |
changelog v1.4.2 | v1.4.2..HEAD |
changelog v1.4.2 (when v1.4.2 is the current tip) | v1.4.1..v1.4.2 — "what went into this release" |
changelog v1.0.0 v1.1.0 | exactly that |
changelog abc123 HEAD | any revision works, not only tags |
Options
| Flag | Effect |
|---|---|
--repo <url> | Base URL for commit and compare links. Defaults to origin, converted from SSH to https. |
--out <file> | Prepend the block to a file. Newest release ends up at the top of CHANGELOG.md. |
--all | Include chore, ci, build and test commits. Off by default; the count is reported instead. |
--json | Emit the parsed commits instead of markdown, so you can group them yourself. |
--cwd <dir> | Run against another checkout. |
Examples
Release notes for a tag you just pushed, with links:
node scripts/changelog.mjs v1.1.0 --repo https://github.com/you/project
Keep CHANGELOG.md current as part of the release:
node scripts/changelog.mjs v1.0.0 v1.1.0 --out CHANGELOG.md
git add CHANGELOG.md && git commit -m "docs: changelog for v1.1.0"
Straight into a GitHub release:
node scripts/changelog.mjs v1.1.0 | gh release create v1.1.0 --notes-file -
Output
## v1.1.0 — 2026-08-30
7 commits since [`v1.0.0...v1.1.0`](https://github.com/you/project/compare/v1.0.0...v1.1.0).
### Breaking changes
- **client:** require an options object ([`9f2c1ab`](https://github.com/you/project/commit/9f2c1ab…))
`createClient()` no longer accepts a string. Pass `{ url }`.
### Added
- **auth:** device-code login for headless machines ([`3d81f04`](…))
### Fixed
- resume uploads after a dropped connection ([`c0a7e12`](…))
<sub>3 housekeeping commits omitted. Re-run with `--all` to include them.</sub>
Sections come out in reading order: Breaking changes, Added, Fixed, Performance, Changed, Reverted, Documentation, then the noisy ones behind --all. Empty sections are never printed.
How commits are classified
type(scope)!: summaryis parsed into a type, an optional scope and a summary. The scope becomes the bold lead-in on the bullet.- A commit is breaking if the subject has
!before the colon or the body has aBREAKING CHANGE:footer. Breaking commits appear in the breaking section and in their own type's section, so a reader scanning "Added" still sees them. - The
BREAKING CHANGE:footer text is printed under the bullet. That footer is usually the only place the migration instruction exists. - Anything that does not match falls into Other, unchanged.
- Merge commits are excluded (
--no-merges).
The skill
skills/changelog.md is the half a script cannot do. It covers the editing pass: rewriting commit subjects into user-facing entries, merging five commits about one bug into one line, putting an actual instruction in every breaking-change entry, and deleting the sections you have nothing real to say in. It also tells an agent not to invent an entry it cannot verify from the diff, which is the failure mode worth guarding against.
Drop it into wherever your agent reads skills from.
Limitations
- Tags are compared by
git describereachability, so a changelog across an unmerged branch may surprise you. Pass both ends explicitly when the history is not linear. - Reverts are listed, not resolved. A
revert:commit and the commit it reverts both appear; deciding they cancel out is an editing judgement. - Commit links assume the GitHub URL shape (
/commit/<sha>,/compare/a...b). Pass--repofor anything else, or drop the flag and get plain short SHAs.
Licence
MIT. See LICENSE.
Security scan report
Scanned by X402 Git, OSV (opens in a new tab) and Socket (opens in a new tab) — passed on all three. Findings from X402 Git and Socket are listed below.
- secrets
- none found
- X402 Git
- size limits
- within caps
- X402 Git
- known malware
- no dependency manifests to check
- OSV
- known vulnerabilities
- no dependency manifests to check
- OSV
- dependency behaviour
- nothing on a direct dependency · 1 in transitive packages
- Socket
- instruction smuggling
- none found
- X402 Git
- what it can do
- listed below
- X402 Git
What this can do
- Tools
- none
- Runs
- scripts/changelog.mjs
- Talks to
- github.com
- Reads
- FIELD
- RECORD
- At install
- nothing runs
- Links to
- api.example.com
- github.com
Findings (5)
- LOWscripts/changelog.mjs — Reads the environment variable FIELD
- LOWscripts/changelog.mjs — Reads the environment variable RECORD
- LOWscripts/changelog.mjs — Talks to github.com
- LOWSocket SBOM Resolver — Socket SBOM Resolver (transitive) — no lockfile, so installs are not reproducible
- LOWscripts/changelog.mjs — Ships the script scripts/changelog.mjs
Releases 2
- v1.0.12026-08-30
Re-cut with lines-of-code and README assets
- v1.0.02026-08-30
feat: initial release
Turn
git logbetween two tags into a changelog draft, then edit it into release notes.