Add auto-generated CHANGELOG from GitHub releases

New Features:
- Created scripts/generate-changelog.js to auto-generate CHANGELOG.md
- Fetches all GitHub releases and formats into Keep a Changelog format
- Added npm run changelog:generate command

Version-Bump Skill Updates:
- Added Step 10: Generate CHANGELOG to workflow
- Updated verification checklist to include CHANGELOG generation
- Updated skill description and critical rules
- Single source of truth: GitHub releases

Technical Details:
- Script fetches releases via gh CLI
- Parses release bodies and formats to markdown
- Removes duplicate headers and Claude Code footers
- Sorts releases by date (newest first)
- Generates clean, consistent changelog

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2025-11-11 16:26:10 -05:00
parent fe0902b48f
commit ecb8b39f6d
5 changed files with 1480 additions and 399 deletions
+4 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: version-bump name: version-bump
description: Manage semantic version updates for claude-mem project. Handles patch, minor, and major version increments following semantic versioning. Updates package.json, marketplace.json, plugin.json, and CLAUDE.md version number (NOT version history). Creates git tags. description: Manage semantic version updates for claude-mem project. Handles patch, minor, and major version increments following semantic versioning. Updates package.json, marketplace.json, plugin.json, and CLAUDE.md version number (NOT version history). Creates git tags and GitHub releases. Auto-generates CHANGELOG.md from releases.
--- ---
# Version Bump Skill # Version Bump Skill
@@ -42,6 +42,7 @@ See [operations/workflow.md](operations/workflow.md) for detailed step-by-step p
6. Build and test 6. Build and test
7. Commit and create git tag 7. Commit and create git tag
8. Push and create GitHub release 8. Push and create GitHub release
9. Generate CHANGELOG.md from releases and commit
## Common Scenarios ## Common Scenarios
@@ -56,6 +57,7 @@ See [operations/scenarios.md](operations/scenarios.md) for examples:
- Update ALL FOUR files with matching version numbers - Update ALL FOUR files with matching version numbers
- Create git tag with format `vX.Y.Z` - Create git tag with format `vX.Y.Z`
- Create GitHub release from the tag - Create GitHub release from the tag
- Generate CHANGELOG.md from releases after creating release
- Ask user if version type is unclear - Ask user if version type is unclear
**NEVER:** **NEVER:**
@@ -73,6 +75,7 @@ Before considering the task complete:
- [ ] Git tag created (format: vX.Y.Z) - [ ] Git tag created (format: vX.Y.Z)
- [ ] Commit and tags pushed to remote - [ ] Commit and tags pushed to remote
- [ ] GitHub release created from the tag - [ ] GitHub release created from the tag
- [ ] CHANGELOG.md generated and committed
- [ ] CLAUDE.md: ONLY line 9 updated (version number), NOT version history - [ ] CLAUDE.md: ONLY line 9 updated (version number), NOT version history
## Reference Commands ## Reference Commands
@@ -187,6 +187,29 @@ gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
**IMPORTANT:** Always create the GitHub release immediately after pushing the tag. This makes the release discoverable to users and triggers any automated workflows. **IMPORTANT:** Always create the GitHub release immediately after pushing the tag. This makes the release discoverable to users and triggers any automated workflows.
## Step 10: Generate CHANGELOG
After creating the GitHub release, regenerate CHANGELOG.md from all releases:
```bash
# Generate CHANGELOG.md from all GitHub releases
npm run changelog:generate
# Review the generated changelog
git diff CHANGELOG.md
# Commit and push the updated changelog
git add CHANGELOG.md
git commit -m "Update CHANGELOG.md for vX.Y.Z release"
git push
```
**Why this step:**
- CHANGELOG.md is auto-generated from GitHub releases
- Keeps the changelog in sync with release notes
- No manual editing required
- Single source of truth: GitHub releases
## Verification ## Verification
After completing all steps, verify: After completing all steps, verify:
+1341 -396
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -40,6 +40,7 @@
"worker:stop": "pm2 stop claude-mem-worker", "worker:stop": "pm2 stop claude-mem-worker",
"worker:restart": "pm2 restart claude-mem-worker", "worker:restart": "pm2 restart claude-mem-worker",
"worker:logs": "pm2 logs claude-mem-worker", "worker:logs": "pm2 logs claude-mem-worker",
"changelog:generate": "node scripts/generate-changelog.js",
"usage:analyze": "node scripts/analyze-usage.js", "usage:analyze": "node scripts/analyze-usage.js",
"usage:today": "node scripts/analyze-usage.js $(date +%Y-%m-%d)" "usage:today": "node scripts/analyze-usage.js $(date +%Y-%m-%d)"
}, },
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env node
/**
* Generate CHANGELOG.md from GitHub releases
*
* Fetches all releases from GitHub and formats them into Keep a Changelog format.
*/
import { execSync } from 'child_process';
import { writeFileSync } from 'fs';
function exec(command) {
try {
return execSync(command, { encoding: 'utf-8' });
} catch (error) {
console.error(`Error executing command: ${command}`);
console.error(error.message);
process.exit(1);
}
}
function getReleases() {
console.log('📋 Fetching releases from GitHub...');
const releasesJson = exec('gh release list --limit 1000 --json tagName,publishedAt,name');
const releases = JSON.parse(releasesJson);
// Fetch body for each release
console.log(`📥 Fetching details for ${releases.length} releases...`);
for (const release of releases) {
const body = exec(`gh release view ${release.tagName} --json body --jq '.body'`).trim();
release.body = body;
}
return releases;
}
function formatDate(isoDate) {
const date = new Date(isoDate);
return date.toISOString().split('T')[0]; // YYYY-MM-DD
}
function cleanReleaseBody(body) {
// Remove the "Generated with Claude Code" footer
return body
.replace(/🤖 Generated with \[Claude Code\].*$/s, '')
.replace(/---\n*$/s, '')
.trim();
}
function extractVersion(tagName) {
// Remove 'v' prefix from tag name
return tagName.replace(/^v/, '');
}
function generateChangelog(releases) {
console.log(`📝 Generating CHANGELOG.md from ${releases.length} releases...`);
const lines = [
'# Changelog',
'',
'All notable changes to this project will be documented in this file.',
'',
'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).',
'',
];
// Sort releases by date (newest first)
releases.sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt));
for (const release of releases) {
const version = extractVersion(release.tagName);
const date = formatDate(release.publishedAt);
const body = cleanReleaseBody(release.body);
// Add version header
lines.push(`## [${version}] - ${date}`);
lines.push('');
// Add release body
if (body) {
// Remove the initial markdown heading if it exists (e.g., "## v5.5.0 (2025-11-11)")
const bodyWithoutHeader = body.replace(/^##?\s+v?[\d.]+.*?\n\n?/m, '');
lines.push(bodyWithoutHeader);
lines.push('');
}
}
return lines.join('\n');
}
function main() {
console.log('🔧 Generating CHANGELOG.md from GitHub releases...\n');
const releases = getReleases();
if (releases.length === 0) {
console.log('⚠️ No releases found');
return;
}
const changelog = generateChangelog(releases);
writeFileSync('CHANGELOG.md', changelog, 'utf-8');
console.log('\n✅ CHANGELOG.md generated successfully!');
console.log(` ${releases.length} releases processed`);
}
main();