Conventional Changelog logo

Conventional Changelog

Community
mizchi
conventional-changelog

Reference for Conventional Commits and automatic CHANGELOG generation. Compares release-please / changesets / git-cliff / towncrier and covers commit format, Keep a Changelog, and semver tag practices. Use when setting up or unifying a release flow.

Overview

Publishermizchi
Repositoryskills
Skill nameconventional-changelog
Stars
333
Forks
4
Bundled files
1
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by mizchi on GitHub. Read the source before you install it.

Installation

Install the Conventional Changelog AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/mizchi/skills.git /tmp/skills
mkdir -p .claude/skills
cp -r /tmp/skills/conventional-changelog .claude/skills/conventional-changelog
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Conventional Changelog in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Conventional Changelog on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Conventional Changelog is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Conventional Changelog

A guide to writing commit messages following the Conventional Commits spec and mechanically generating CHANGELOGs and semver tags from them. Pick tools across npm / cargo / python / rust / go and avoid writing CHANGELOGs by hand.

When to use

  • Setting up a release flow on a new repo
  • Commit messages on an existing repo are inconsistent and you want to introduce CHANGELOG generation
  • You tried release-please but it did not fit / you want to evaluate alternatives
  • Looking for a changelog tool suited to the language (Rust / Python / Go, etc.)
  • Tags and CHANGELOGs keep breaking under manual operation, and you want to automate them

Conventional Commits format

Form: <type>[optional scope]: <subject>

feat(api): add rate-limit middleware
fix(auth): reject expired tokens before DB lookup
docs: clarify OAuth flow diagram
chore: bump deps

List of types and semver impact

typesemver bumpIncluded in CHANGELOG
featminor✓ (Added / Features)
fixpatch✓ (Fixed)
perfpatch✓ (Changed)
refactornone× (default)
docsnone×
stylenone×
testnone×
buildnone×
cinone×
chorenone×
revertdepends on the reverted commit

Breaking change (major bump)

Two ways: put BREAKING CHANGE: in the footer, or a ! after the type:

feat(api)!: replace REST endpoint with GraphQL

BREAKING CHANGE: /api/v1/users is removed. Use GraphQL Query.user instead.

Writing both is polite (obvious from the title + detail in the footer).

scope

Optional, but including it makes the CHANGELOG easier to read. In a monorepo, use the package name as the scope:

feat(@pkg/auth): add oauth2 flow
feat(@pkg/billing): stripe migration

release-please also auto-detects the "target package of the change" from the scope (in manifest mode).

subject

  • Start lowercase (no trailing period)
  • Imperative mood ("add X" / "fix Y"; avoid "added" or "fixes")
  • Aim for 50 characters or fewer

body / footer

Body after a blank line, then another blank line before the footer (Closes #123, Co-authored-by: ..., BREAKING CHANGE: ...).

CHANGELOG.md format (Keep a Changelog)

Follows keepachangelog.com. Versions in descending order, semver, and a fixed set of 6 sections:

markdown
# Changelog

All notable changes to this project are documented here.

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).

## [Unreleased]

### Added

- foo bar

## [1.2.0] - 2026-04-19

### Added

- New `--json` output flag for `foo scan` ([#42](https://github.com/owner/repo/pull/42))

### Changed

- `parse_config` now accepts YAML in addition to TOML

### Fixed

- Crash when config file is empty ([#40](https://github.com/owner/repo/issues/40))

### Deprecated

- `--legacy-mode` flag. Use `--mode=legacy` instead.

### Removed

- `foo bar --old-flag` (deprecated in 1.0.0)

### Security

- Upgrade `openssl` to 3.2.0 to address CVE-2025-XXXXX

## [1.1.0] - 2026-03-01

...

[Unreleased]: https://github.com/owner/repo/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/owner/repo/compare/v1.1.0...v1.2.0

The 6 sections:

  • Added: new features
  • Changed: changes to existing functionality
  • Deprecated: scheduled for removal (still present)
  • Removed: already removed
  • Fixed: bug fixes
  • Security: vulnerability fixes

semver tag practices

  • Tags in v<MAJOR>.<MINOR>.<PATCH> form (v1.2.3). The v prefix is the norm
  • Pre-releases: v1.0.0-beta.1 / v1.0.0-rc.1
  • git tag -a v1.2.3 -m "Release v1.2.3" (annotated tag) → git push origin v1.2.3
  • Wire up tag creation and deployment via GitHub Release + Actions with on: release

Tool comparison

ToolSupported languagesStyleCHANGELOG generationversion bumpMain strengthsWeaknesses
release-pleasenpm / python / rust / go / java / php / rubyAuto-creates a Release PR✓ (tag on PR merge)Monorepo support, stable Google-maintained projectPR-based with a learning curve
changesetsnpm onlyManual entries via changeset addMonorepo + flexibility (npm workspaces)Not commit-driven
conventional-changelog-cliLanguage-agnostic (command)One-shot generation from commits×Lightweight, appends to existing CHANGELOGTags / bumps handled separately by hand
standard-version (deprecated)npmcommit → bump + CHANGELOGSimpleMaintenance halted; migration to release-please recommended
git-cliffLanguage-agnostic (written in Rust)Highly customizable via TOML config×Fast, flexible templates, popular in the Rust ecosystemTags / bumps separate
towncrierPythonWrite a news fragment per PR×Fragments avoid merge conflictsIdiosyncratic workflow
auto (intuit/auto)MultipleLabel-based + CI integrationDriven by GitHub labelsCustom label conventions

One-line recommendations

  • Want to automate a single npm package → release-please
  • npm monorepo → release-please (manifest mode) or changesets (for fine-grained control)
  • Rust → git-cliff
  • Python → towncrier (or release-please)
  • Go / generic → git-cliff or release-please
  • Just want to generate a CHANGELOG (manual tag management) → conventional-changelog-cli or git-cliff

release-please setup (most recommended path)

Fresh setup on an npm project:

json
// release-please-config.json
{
  "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
  "release-type": "node",
  "packages": {
    ".": { "package-name": "my-pkg", "changelog-path": "CHANGELOG.md" }
  },
  "include-v-in-tag": true,
  "bump-minor-pre-major": true
}
json
// .release-please-manifest.json
{ ".": "0.0.0" }
yaml
# .github/workflows/release-please.yml
name: release-please
on:
  push:
    branches: [main]
permissions:
  contents: write
  pull-requests: write
jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - uses: googleapis/release-please-action@v5
        with:
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json

Write commits following the Conventional Commits spec and push to main; a "Release PR" is created automatically. Merging it triggers the tag + GitHub Release + CHANGELOG update.

Two forms of publish-linked workflow:

FormtriggerStrengthsWeaknesses
Job separation within the same workflow (needs: release-please + if: outputs.release_created)push: mainSelf-contained in one file, directly references release-please outputs, easy to graspIf the release-please job fails, publish is dragged down with it; responsibilities get mixed
Separate workflow (on: release)release: { types: [published] }Separation of concerns, publish runs even for manual releases, re-runs are independentsecrets / permissions must be declared in two places; release-please outputs are unavailable

Selection guidance: For a single package with a single publish target (npm only), a single workflow is concise. If there are multiple publish targets (npm + GitHub Container Registry etc.) or manual releases are also allowed, use a separate on: release workflow. When using OIDC Trusted Publishing, attaching id-token: write only to the publish job is sufficient, and either form works.

See the publish flow in the npm-release skill (locally managed) for details.

Pre-release (beta / rc) staged releases

When you want to ship a v2.0.0 with breaking changes in the order beta → rc → stable. Use release-please's prerelease / prerelease-type flags:

json
// release-please-config.json
{
  "packages": {
    ".": {
      "package-name": "my-pkg",
      "prerelease": true,
      "prerelease-type": "beta"
    }
  }
}

Flow:

  1. Stack commits with prerelease-type: "beta"v2.0.0-beta.1 is cut
  2. More commits stack up as v2.0.0-beta.2 / beta.3 ...
  3. Promote to RC: rewrite config to "prerelease-type": "rc"v2.0.0-rc.1
  4. Stable release: "prerelease": false (or remove the field) → v2.0.0

How to write the CHANGELOG: release-please finalizes an independent section for each beta/rc (it is not the Keep a Changelog style of accumulating into [Unreleased]). [2.0.0-beta.1] / [2.0.0-beta.2] / [2.0.0-rc.1] / [2.0.0] all remain. The 2.0.0 stable section aggregates every feat! / BREAKING CHANGE from the period.

Monorepo + workspace:* interaction

In a pnpm workspace with @org/core, @org/ui, @org/cli where @org/ui and @org/cli depend on @org/core:

json
// release-please-config.json
{
  "plugins": [
    { "type": "node-workspace", "updatePeerDependencies": true }
  ],
  "packages": {
    "packages/core": {
      "release-type": "node",
      "package-name": "@org/core",
      "include-component-in-tag": true
    },
    "packages/ui": {
      "release-type": "node",
      "package-name": "@org/ui",
      "include-component-in-tag": true
    },
    "packages/cli": {
      "release-type": "node",
      "package-name": "@org/cli",
      "include-component-in-tag": true
    }
  },
  "separate-pull-requests": false,
  "include-v-in-tag": true
}
  • include-component-in-tag: true → tag names include the component name, like @org/core-v2.0.0 (avoids monorepo collisions)
  • node-workspace plugin → detects a bump on @org/core, rewrites dependencies."@org/core" in @org/ui / @org/cli, and adds both as patch bumps to the PR
  • updatePeerDependencies: true → rewrites peer dependencies the same way (needed on the library side)
  • separate-pull-requests: false → consolidates bumps for all packages into a single Release PR

Include the package name in the commit scope:

feat(@org/core): add streaming parser
fix(@org/ui): button focus ring on safari
feat(@org/cli)!: rename --config to --profile

The scope automatically determines the target of the change. An entry is written only into the CHANGELOG of the relevant package.

How workspace:* substitution works: Even if you write "@org/core": "workspace:*" in @org/ui/package.json, pnpm automatically substitutes it with ^<actual version> at publish time. release-please leaves the source as workspace:* during bumps without touching it. This assumption lines up perfectly (on the pnpm side, workspace:^^same-major, workspace:~~same-minor).

git-cliff setup (Rust / generic)

bash
cargo install git-cliff
# or
brew install git-cliff
toml
# cliff.toml
[changelog]
header = "# Changelog\n"
body = """
{% if version %}## [{{ version }}] - {{ timestamp | date(format="%Y-%m-%d") }}{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }} ({{ commit.id | truncate(length=7, end="") }})
{% endfor %}
{% endfor %}
"""

[git]
conventional_commits = true
filter_unconventional = true       # Exclude existing non-conforming commits from the CHANGELOG
protect_breaking_commits = true
commit_parsers = [
  { message = "^feat",     group = "Added" },
  { message = "^fix",      group = "Fixed" },
  { message = "^perf",     group = "Changed" },
  { message = "^refactor", group = "Changed" },
  { message = "^deprecat", group = "Deprecated" },
  { message = "^remove",   group = "Removed" },
  { message = "^security", group = "Security" },
  { message = "^revert",   group = "Reverted" },
  { message = "^(docs|style|test|build|ci|chore)", skip = true },
  { message = ".*",        skip = true },   # Bulk-exclude non-conforming commits (do not rewrite history)
]
tag_pattern = "v[0-9]*"
sort_commits = "newest"

For Rust projects used alongside cargo-release, cargo-release handles the version bump in Cargo.toml and tagging, while git-cliff focuses solely on CHANGELOG generation (loosely coupled). This is also why you would not layer a PR-based automatic bump like release-please on top.

bash
git-cliff --output CHANGELOG.md            # Regenerate from full history
git-cliff --unreleased --prepend CHANGELOG.md  # Prepend only the unreleased section
git-cliff --tag v1.2.0 --output CHANGELOG.md   # Generate for a specific tag

Wiring with cargo-release (concrete example)

Put hooks into [package.metadata.release] in Cargo.toml. Running cargo release <level> runs bump → CHANGELOG generation → commit → tag → push in sequence:

toml
# Cargo.toml
[package.metadata.release]
# After the version bump and before the commit, update CHANGELOG with git-cliff
pre-release-hook = ["git", "cliff", "--tag", "v{{version}}", "--unreleased", "--prepend", "CHANGELOG.md"]
# The tag itself is created by cargo-release
tag-name = "v{{version}}"
# Include CHANGELOG.md in the release commit
pre-release-commit-message = "chore(release): v{{version}}"

Order:

  1. cargo release minor --execute → version bump in Cargo.toml
  2. pre-release-hook runs git cliff to finalize the next version's [Unreleased] as [v0.2.0] - 2026-04-19 and update CHANGELOG.md
  3. cargo-release bundles Cargo.toml + CHANGELOG.md into the release commit
  4. tag-name creates v0.2.0 as an annotated tag
  5. cargo publish + git push --tags

Note: The [changelog.bump] section on the git-cliff side (features_always_bump_minor etc.) is a git-cliff-specific feature where git-cliff itself decides the bump; do not use it when combined with cargo-release (the bump decision belongs to cargo-release). <level> (patch / minor / major) is chosen by a human.

conventional-changelog-cli (lightweight, language-agnostic)

bash
npm install -g conventional-changelog-cli
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
# -p: preset (angular / atom / ember / eslint / jquery / jshint)
# -i: input, -s: same file, -r 0: full history

The main preset is angular (the origin of Conventional Commits). Well-suited to appending to an existing CHANGELOG.

Validating commit messages with pre-commit

Reject commits that violate the Conventional Commits spec at commit time:

commitlint

bash
npm install -D @commitlint/cli @commitlint/config-conventional
js
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
yaml
# .pre-commit-config.yaml (pre-commit framework)
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
  rev: v9.16.0
  hooks:
    - id: commitlint
      stages: [commit-msg]
      additional_dependencies: ['@commitlint/config-conventional']

Pitfall: Without additional_dependencies, the hook cannot resolve config-conventional at run time and fails with Cannot find module. Just extends in commitlint.config.js is not enough; you must also declare it explicitly in the hook's isolated environment.

In the mizchi-default pkfire ecosystem, commitlint is wired into a Taskfile.pkl task instead — see the chezmoi-management and pkfire skills.

commitizen (interactive commit creation)

bash
npm install -D commitizen cz-conventional-changelog
# package.json
"config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } }
# usage
git cz

Common failures

  • Introducing the commit convention partway through: existing history stays in the broken format. release-please ignores "non-conforming commits" during its scan. No retroactive rewrite is needed; just align from now on
  • Using chore: for everything so no release PR opens: writing chore: for a bug fix omits it from the CHANGELOG / does not bump the version. Pick the type by "kind of change", not by importance (fix is a user-visible fix, chore is internal housekeeping)
  • Writing BREAKING CHANGE in the subject and omitting the footer: release-please looks at the BREAKING CHANGE: in the footer or the ! on the type. Writing "breaking: ..." in the subject will not trigger a major bump
  • Mixing manual CHANGELOG edits with the tool: manually editing a CHANGELOG managed by release-please causes drift on the next generation. Add entries only via PRs (use release-please's "Edit" feature)
  • Creating a tag first and then opening a release-please PR: release-please creates the tag on PR merge. A manual tag conflicts with it. For manual operation, standardize on conventional-changelog-cli / git-cliff + manual tagging

Red flags

Thought that comes upReality
"Writing the CHANGELOG by hand is more polished"Generating from commits misses nothing. Handwriting inevitably produces omissions and inconsistent wording
"Create the tag first, then write the changelog"The order is backwards. Stick to commit → tool generates CHANGELOG → tag
"Breaking changes are clear if written in the subject"Tools mechanically read the footer / !. Natural-language phrasing in the subject is ignored
"Let's add project-specific types"You can express things within feat / fix / chore. Custom types are not supported by the tools' presets
"Let's fix non-conforming commits via rebase"Retroactive rewrites pollute history. Just align from now on; the tool will ignore the rest

Related

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Conventional Changelog AI skill do?

Reference for Conventional Commits and automatic CHANGELOG generation. Compares release-please / changesets / git-cliff / towncrier and covers commit format, Keep a Changelog, and semver tag practices. Use when setting up or unifying a release flow.

Why use Conventional Changelog on TypingMind?

Because you install it once and use it with any model. Conventional Changelog is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Conventional Changelog in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/mizchi/skills/tree/main/conventional-changelog. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Conventional Changelog?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Conventional Changelog?

As many as you like. As long as a model supports skills, you can use Conventional Changelog with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Conventional Changelog AI skill free?

It is published on GitHub by mizchi. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇