changelog-gen

Generates structured changelogs from git history using conventional commit parsing. Reads commits since the last git tag, categorizes them into Added, Fixed, Changed, Breaking Changes, and Internal sections, then prepends a formatted entry to CHANGELOG.md. Falls back to AI-based categorization when commits do not follow conventional format. Triggered when the user asks to generate a changelog, update CHANGELOG, prepare release notes, or review what changed since the last release.

changelog-gen

Generate a structured CHANGELOG entry from git commit history using conventional commit conventions.


When to Trigger

Activate this skill when the user:

  • Asks to generate a changelog or update CHANGELOG.md
  • Wants to prepare release notes
  • Asks "what changed since last release" or "summarize recent commits"
  • Is invoked by the npm-publish skill during the release pipeline

Commit Parsing

Fetch Commits Since Last Tag

LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null)

If a tag exists:

git log ${LAST_TAG}..HEAD --oneline --no-merges

If no tags exist (first release), use all commits:

git log --oneline --no-merges

Categorize by Conventional Commit Prefix

PrefixChangelog Section
feat: / feat(scope):Added
fix: / fix(scope):Fixed
refactor: / perf:Changed
docs:Documentation
BREAKING CHANGE: or !: in subjectBreaking Changes
chore: / ci: / build: / test:Internal

Rules:

  • Strip the prefix and scope from the description (e.g., feat(auth): add OAuth flow becomes Add OAuth flow)
  • Capitalize the first letter of each entry
  • Append the short commit hash in parentheses: (a1b2c3d)
  • If a commit body contains BREAKING CHANGE:, promote it to the Breaking Changes section regardless of prefix

Non-Conventional Fallback

If fewer than 50% of commits follow conventional format, switch to AI categorization:

  1. For each commit, read the diff: git show --stat <hash>
  2. Categorize based on the files changed and diff content:
    • New files added to src/ -> Added
    • Modified test files -> Internal
    • Modified docs -> Documentation
    • Bug-related keywords in message (fix, resolve, patch, correct) -> Fixed
    • Everything else -> Changed
  3. Flag in the output that AI categorization was used

Output Format

## [X.Y.Z] --- YYYY-MM-DD

### Breaking Changes
- Description of breaking change and migration steps (hash)

### Added
- New feature description (hash)

### Fixed
- Bug fix description (hash)

### Changed
- Refactor or performance improvement description (hash)

### Documentation
- Documentation update description (hash)

### Internal
- Chore, CI, build, or test change description (hash)

Rules:

  • Omit any section that has zero entries
  • Use the version from package.json if available, otherwise prompt the user
  • Date is always today in YYYY-MM-DD format
  • Use --- as the date separator (em dash style)

Actions

1. Generate Entry

Build the changelog entry following the output format above. Display it to the user for review before writing.

2. Prepend to CHANGELOG.md

If CHANGELOG.md exists, insert the new entry immediately after the # Changelog header line (preserving any preamble text between the header and the first version entry).

If CHANGELOG.md does not exist, create it:

# 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.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [X.Y.Z] --- YYYY-MM-DD
...

3. Write Temp File for GitHub Release

Write just the current version's entry (without the ## [version] header) to /tmp/CHANGELOG_ENTRY.md for use by the npm-publish skill or gh release create.


Edge Cases

ScenarioBehavior
No git tags existUse all commits from repo inception
No commits since last tagReport "No changes since vX.Y.Z" and exit
No conventional commitsUse AI categorization with a warning
Merge commitsSkip by default (--no-merges)
Single commit since last tagStill generate a full entry
Monorepo with multiple packagesFilter commits by path if user specifies a package directory

Integration

This skill is designed to be invoked standalone or as part of the npm-publish pipeline. When called from npm-publish, the version number is already determined and should be passed in rather than read from package.json.