Tutorials & Guides
How to Build an Automated Changelog Generator from Git Diffs Using Claude 3.5 Sonnet and Bun
Tired of writing sloppy release notes? Use Claude 3.5 Sonnet's precise reasoning capabilities alongside Bun's lightning-fast shell runtime to generate structured, human-readable changelogs from raw git diffs.
Updated 10/14/2026
The Problem with Git Commits as Changelog Entries
We have all been there. You are ready to release a new version of your software, but your git history is a graveyard of "fix typo," "adjust styling," "debug test again," and "please work." Turning those raw commits into an elegant, human-readable CHANGELOG.md that your stakeholders, product managers, and users can actually understand is a painful manual chore.
While simpler models can handle basic text summaries, writing a proper changelog requires deep reasoning. You need an engine that can look at actual code differences (the raw git diff) and infer the functional impact of your changes without hallucinating features.
Claude 3.5 Sonnet is arguably the best model on the market for this type of precise, analytical code review. By pairing it with Bun—the blazing-fast JavaScript runtime that features a built-in shell execution engine—we can build a command-line changelog generator that takes a git diff of your workspace, groups changes by impact, and writes structured Markdown ready for production.
Prerequisites
Before diving in, make sure you have the following installed on your local development machine:
Bun*: If you don’t have it yet, install it via curl -fsSL https://bun.sh/install | bash.
Anthropic API Key*: Set up an account and grab your API key from the Anthropic console.
For deep dives into the Claude family and how it handles high-throughput token pipelines, check out our platform hub at /platforms/claude.
Step 1: Initialize Your Bun Utility
Bun handles TypeScript natively out of the box with zero configuration files, making it the perfect runtime for quick CLI scripts. Create a directory and install the official Anthropic SDK:
`bash
mkdir bun-changelog-generator
cd bun-changelog-generator
bun init -y
bun add @anthropic-ai/sdk
`
Create a .env file in the root directory to store your credentials:
`env
ANTHROPIC_API_KEY=your_anthropic_api_key_here
`
Step 2: Grabbing the Git Diff via Bun's Shell
We need to extract the raw changes from our codebase. Bun provides a highly optimized, native shell runner called $ that makes running system commands painless and safe from injection attacks. Learn more about concepts like secure environment runtimes in our developer terminology resource at /glossary.
Create a file named generator.ts and add the code to grab git changes compared to your main development branch (typically main or master):
`typescript
import { $ } from 'bun';
async function getGitDiff(targetBranch = 'main'): Promise<string> {
try {
// Ensure we are inside a git repo and retrieve the diff
const diffOutput = await $git diff ${targetBranch}...HEAD.text();
return diffOutput.trim();
} catch (error) {
console.error('Error fetching git diff. Is this a git repository?');
process.exit(1);
}
}
`
Using the triple dot ... syntax ensures we are comparing the common ancestor of your current branch and the target branch, focusing purely on the changes introduced in your feature branch.
Step 3: Architecting the Prompt for Claude 3.5 Sonnet
Because git diffs can contain hundreds of lines of code modifications, our prompt must instruct Claude to ignore boilerplate modifications (like library lockfile adjustments) and concentrate strictly on core business logic changes. We will also enforce a strict Markdown output structure.
Add this integration step using the official Anthropic SDK to generator.ts:
`typescript
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, });
async function generateChangelog(diff: string): Promise<string> { if (!diff) { return 'No changes detected between the current workspace and the target branch.'; }
// Diffs can get very large, so we slice them to stay within safe token budgets if needed const safeDiff = diff.length > 100000 ? diff.slice(0, 100000) + '\n\n[Diff truncated...]' : diff;
const systemPrompt = ` You are an expert technical writer and principal software engineer. Your job is to read a raw git diff and generate a clean, production-ready CHANGELOG in Markdown. Adhere strictly to these grouping rules: - Features: New user-facing capabilities. - Fixes: Bug resolutions and stability improvements. - Performance: Enhancements making the application faster. - Chores: Refactors, dependency updates, and test updates. Do not list individual commit hashes. Group changes logically based on the modified files and your analysis of the code. Be concise. If a change is minor (like updating a comment), ignore it. `;
try {
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1500,
temperature: 0.1, // Low temperature for high precision and analytical consistency
system: systemPrompt,
messages: [
{ role: 'user', content: Analyze this git diff and write the changelog:\n\n${safeDiff} }
],
});
// Claude returns content blocks; extract the text content
const textBlock = response.content.find(block => block.type === 'text');
return textBlock ? textBlock.text : 'Failed to generate changelog content.';
} catch (error) {
console.error('Error calling Anthropic API:', error);
process.exit(1);
}
}
`
Step 4: Writing and Executing the Script
To round off our script, we will pull everything together, execute the tasks sequentially, and output the generated changelog directly to a local Markdown file.
Append the following execution code to generator.ts:
`typescript
async function run() {
const targetBranch = process.argv[2] || 'main';
console.log(Comparing current branch against: "${targetBranch}"...);
const diff = await getGitDiff(targetBranch); if (!diff) { console.log('Your local branch is completely in sync with the target branch. Nothing to do!'); return; }
console.log('Sending diff to Claude 3.5 Sonnet for analysis...'); const changelog = await generateChangelog(diff);
const fileName = 'UNRELEASED_CHANGES.md';
await Bun.write(fileName, changelog);
console.log(\nSuccess! Your changelog has been compiled and saved to ${fileName}.);
console.log('----------------------------------------------------\n');
console.log(changelog);
}
run();
`
Running Your Generator
You can call this utility instantly using Bun's runner. To check your workspace's divergence from your local target branch, just run:
`bash
bun run generator.ts main
`
If you run into any rate limits or connection errors when handling massive repositories with heavy dependency file diffs, visit our troubleshooting guides at /platforms/claude/articles to learn how to parse down file targets using native Git excludes.
Keep going
Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.
Which AI should I use for this?
Tickd is independent and not affiliated with any AI company.