Release day. You need a changelog. You open the git log, stare at 47 commits, and start manually sorting them into βFeatures,β βBug Fixes,β and βBreaking Changes.β This takes 30 minutes every release.
In this tutorial, weβll build changelog-ai β a CLI that reads your git history between two tags and generates a formatted changelog automatically.
What weβre building
$ changelog-ai v1.2.0 v1.3.0
# Changelog β v1.3.0 (2026-03-25)
## β¨ New Features
- Add rate limiting to all API endpoints (#142)
- Support dark mode in dashboard (#138)
- Add CSV export for reports (#135)
## π Bug Fixes
- Fix race condition in WebSocket reconnection (#141)
- Resolve null pointer when user profile is incomplete (#139)
- Fix timezone offset in scheduled reports (#136)
## π§ Improvements
- Reduce Docker image size from 1.2GB to 340MB (#140)
- Migrate from moment.js to date-fns (#137)
## β οΈ Breaking Changes
- API response format changed for /api/reports endpoint (#135)
- `data` field renamed to `results`
- Pagination now uses cursor instead of offset
## π₯ Contributors
@sarah, @jake, @alex
The code
#!/usr/bin/env node
// index.js
import Anthropic from '@anthropic-ai/sdk';
import { execSync } from 'child_process';
const [,, fromTag, toTag] = process.argv;
if (!fromTag || !toTag) {
console.log('Usage: changelog-ai <from-tag> <to-tag>');
console.log('Example: changelog-ai v1.2.0 v1.3.0');
process.exit(1);
}
// Get commits between tags
const log = execSync(
`git log ${fromTag}..${toTag} --pretty=format:"%h %s (%an)" --no-merges`,
{ encoding: 'utf-8' }
);
if (!log.trim()) {
console.log(`No commits between ${fromTag} and ${toTag}`);
process.exit(0);
}
// Get the diff stats for context
const stats = execSync(
`git diff ${fromTag}..${toTag} --stat`,
{ encoding: 'utf-8' }
);
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1500,
messages: [{
role: 'user',
content: `Generate a changelog from these git commits.
From: ${fromTag}
To: ${toTag}
Date: ${new Date().toISOString().split('T')[0]}
Commits:
${log}
File changes:
${stats.slice(0, 3000)}
Format as markdown with these sections:
# Changelog β ${toTag} (date)
## β¨ New Features
## π Bug Fixes
## π§ Improvements
## β οΈ Breaking Changes (if any, otherwise omit)
## π₯ Contributors
Rules:
- Group commits by category based on the commit message content
- Include PR/issue numbers if present in commit messages
- For breaking changes, explain what changed and how to migrate
- List unique contributors at the end
- Skip merge commits and version bumps
- Be concise β one line per change`
}],
});
console.log(response.content[0].text);
Setup
mkdir changelog-ai && cd changelog-ai
npm init -y && npm install @anthropic-ai/sdk
chmod +x index.js && npm link
Usage
# Between two tags
changelog-ai v1.2.0 v1.3.0
# Save to file
changelog-ai v1.2.0 v1.3.0 > CHANGELOG.md
# Between a tag and HEAD
changelog-ai v1.2.0 HEAD
Making it better
- Conventional commits: If your team uses conventional commits (
feat:,fix:,chore:), parse them directly instead of using AI for categorization - GitHub integration: Fetch PR descriptions for richer context using the GitHub API
- Append mode: Prepend to existing CHANGELOG.md instead of overwriting
- CI integration: Run automatically on tag push in GitHub Actions
CI integration (GitHub Actions)
Add this to .github/workflows/changelog.yml to auto-generate on every release:
name: Generate Changelog
on:
push:
tags: ['v*']
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm install -g changelog-ai
- run: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^)
changelog-ai $PREV_TAG ${{ github.ref_name }} > RELEASE_NOTES.md
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body_path: RELEASE_NOTES.md
Now every time you push a tag, a GitHub Release is created with AI-generated release notes.
Total build time: ~15 minutes. API cost: ~$0.01 per changelog.
Related: Git cheat sheet Β· Build a Git Diff Summarizer Β· Best AI Coding Tools 2026