You just finished a feature branch with 47 changed files. Your team lead asks for a summary of what changed. You could spend 20 minutes writing it up β or you could run one command and let AI do it.
In this tutorial, weβll build git-summary, a CLI tool that reads your git diff and generates a clean, human-readable changelog. It takes about 15 minutes to build and youβll actually use it daily.
What weβre building
$ git-summary
## Changes Summary
### New Features
- Added user authentication with JWT tokens
- Created /api/users endpoint with CRUD operations
### Bug Fixes
- Fixed race condition in WebSocket connection handler
- Resolved null pointer when user profile is incomplete
### Refactoring
- Extracted database queries into repository pattern
- Moved validation logic to middleware
**Files changed:** 12 | **Insertions:** 847 | **Deletions:** 234
Prerequisites
- Node.js 20+
- An Anthropic API key (get one at console.anthropic.com)
- A git repository to test with
Step 1: Set up the project
mkdir git-summary && cd git-summary
npm init -y
npm install @anthropic-ai/sdk
Add to package.json:
{
"type": "module",
"bin": { "git-summary": "./index.js" }
}
Step 2: Build the CLI
Create index.js:
#!/usr/bin/env node
import Anthropic from '@anthropic-ai/sdk';
import { execSync } from 'child_process';
// Get the git diff
function getGitDiff() {
try {
// Try staged changes first, then unstaged, then last commit
let diff = execSync('git diff --cached --stat -p', { encoding: 'utf-8' });
if (!diff.trim()) {
diff = execSync('git diff --stat -p', { encoding: 'utf-8' });
}
if (!diff.trim()) {
diff = execSync('git diff HEAD~1 --stat -p', { encoding: 'utf-8' });
}
return diff;
} catch {
console.error('Not a git repository or no changes found.');
process.exit(1);
}
}
// Truncate diff if too long (API has token limits)
function truncateDiff(diff, maxChars = 80000) {
if (diff.length <= maxChars) return diff;
return diff.slice(0, maxChars) + '\n\n... (diff truncated for length)';
}
async function summarize() {
const diff = getGitDiff();
if (!diff.trim()) {
console.log('No changes to summarize.');
return;
}
const client = new Anthropic();
const message = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{
role: 'user',
content: `Summarize this git diff into a clean changelog. Group changes into categories like "New Features", "Bug Fixes", "Refactoring", "Configuration", etc. Only include categories that have changes. Use bullet points. Be concise but specific. At the end, include a stats line with files changed, insertions, and deletions.
\`\`\`diff
${truncateDiff(diff)}
\`\`\``
}],
});
console.log(message.content[0].text);
}
summarize();
Step 3: Set your API key
export ANTHROPIC_API_KEY=your-key-here
# Add to your shell profile for persistence
echo 'export ANTHROPIC_API_KEY=your-key-here' >> ~/.zshrc
Step 4: Test it
# Make some changes in a git repo, then:
node index.js
# Or link it globally:
npm link
git-summary
Making it smarter
Compare specific branches
Add branch comparison support by accepting arguments:
const args = process.argv.slice(2);
const target = args[0] || 'HEAD~1';
const diff = execSync(`git diff ${target} --stat -p`, { encoding: 'utf-8' });
Now you can run:
git-summary main # compare current branch to main
git-summary HEAD~5 # last 5 commits
git-summary v1.2.0 # since a tag
Add a commit message generator
Extend the tool to also suggest a commit message:
async function generateCommitMessage() {
const diff = execSync('git diff --cached --stat -p', { encoding: 'utf-8' });
if (!diff.trim()) {
console.log('No staged changes. Run `git add` first.');
return;
}
const client = new Anthropic();
const message = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 256,
messages: [{
role: 'user',
content: `Write a concise conventional commit message for this diff. Format: type(scope): description. Keep it under 72 characters. Only output the commit message, nothing else.
\`\`\`diff
${truncateDiff(diff)}
\`\`\``
}],
});
console.log(message.content[0].text);
}
// Route based on flag
if (process.argv.includes('--commit')) {
generateCommitMessage();
} else {
summarize();
}
git-summary --commit
# Output: feat(auth): add JWT authentication with refresh token rotation
Save summaries to a file
import { writeFileSync } from 'fs';
if (process.argv.includes('--save')) {
const date = new Date().toISOString().split('T')[0];
writeFileSync(`changelog-${date}.md`, summary);
console.log(`Saved to changelog-${date}.md`);
}
Cost
Each summary costs roughly $0.003-0.01 depending on diff size. You could run this 1,000 times for under $10. For most developers, the monthly cost is negligible.
What you learned
- How to build a CLI tool with Node.js and the
binfield in package.json - How to use the Anthropic SDK to call Claudeβs API
- How to pipe git output into an AI prompt
- How to structure prompts for consistent, formatted output
The full code is about 60 lines. You can extend it with PR description generation, release notes, or even automatic CHANGELOG.md updates. The pattern is always the same: get the diff, send it to the API with a clear prompt, format the output.
Related resources
- Git cheat sheet β all the git commands you need
- Git complete guide β from basics to advanced workflows
- npm complete guide β publishing your CLI tool to npm