name: maintain-changelog
description: Use when the user says "update the CHANGELOG" / "changelog from PRs since {version}" / "draft the changelog entries" — fetches merged PRs + linked issues since the given version tag via the connected code host, filters for user-facing changes, groups them Keep-A-Changelog style (Added / Changed / Deprecated / Removed / Fixed / Security), and writes a DRAFT snippet to changelog/{version}.md. Hard no: never auto-commits the canonical CHANGELOG.md — user copies the snippet in themselves.
Maintain Changelog
When to use
- User: "update the CHANGELOG from merged PRs since {version}" / "draft changelog entries for {version}" / "what's new since {version-tag}".
- Implicit trigger: the user mentions a version bump and references the last tagged release.
Steps
-
Read engineering context. Open
../head-of-engineering/engineering-context.md. If missing or empty, stop and tell the user:"I need the engineering context doc to judge what's user-facing. Run the Head of Engineering's
define-engineering-contextfirst." -
Read config.
config/changelog-format.md(style reference — if it's an existing CHANGELOG.md, mirror its headings and tone). If missing or "none", default to Keep-A-Changelog (keepachangelog.com) with these headings:Added,Changed,Deprecated,Removed,Fixed,Security. -
Resolve inputs.
- Version source: the git tag the user named (e.g.
v1.4.0). - Version target: what the user is about to ship (e.g.
v1.5.0orUnreleased). If unstated, ask ONE question:"What's the next version? (e.g.
v1.5.0, or justUnreleasedfor now.) I'll put the draft under that heading." - Repo: resolve via
config/repo.json; if missing, ask OR runcomposio search code-hostingfor a connected host and list accessible repos.
- Version source: the git tag the user named (e.g.
-
Fetch merged PRs since the tag. Run
composio search code-hostingto find the GitHub (or GitLab / Bitbucket / Gitea) tool. Fetch merged PRs whose merge commit is after the tag, with:- Title
- Body (first 500 chars — scan for "Closes #N" linked issues)
- Author
- Number
- Labels
- Merge date
- Linked issue titles (fetch the issues that are referenced
in-body with
Closes #/Fixes #/Resolves #).
If no connected code host, tell the user which category to link and stop.
-
Filter for user-facing changes.
- Include: new features, behavior changes, bug fixes that affect user-visible output, security fixes, breaking changes, deprecations, new configuration options.
- Skip: internal refactors with no user effect, CI-only changes, dependency bumps (unless they change behavior), tests only, docs-only (unless major), formatting / lint only.
- Heuristic: if the PR title starts with
refactor:,chore:,test:,ci:, skip by default — but scan the body for "user-facing" mentions and include if in doubt. Err toward including; the user will cut.
-
Classify each kept PR under Keep-A-Changelog headings:
Added— new features, new endpoints, new config options.Changed— behavior changes to existing features (non- breaking).Deprecated— features scheduled for removal.Removed— features now gone (breaking).Fixed— bug fixes.Security— vulnerability fixes / auth changes.
-
Rewrite each PR title into user-facing language:
- From the user's perspective. "Fixed race condition in widget saves" > "Refactor widget save mutex".
- Active voice, one line. Lead with the verb (Add / Change / Fix / Remove).
- Link back with
(#{PR-number})at the end — links go in a footer section the user can strip if they don't want PR references. - Breaking changes prefixed with
**BREAKING:**— this is critical, never omit.
-
Compose the draft snippet. Exact structure (the user will paste this under the target version in their canonical CHANGELOG.md):
## [{target-version}] — {YYYY-MM-DD} ### Added - Add `/v1/widgets/batch` endpoint for bulk creation. (#142) - Add `WIDGET_RATE_LIMIT` env var to cap API writes. (#148) ### Changed - **BREAKING:** Rename `widget.price` to `widget.price_cents`. Clients on < v1.4 must migrate. (#151) - Log format is now JSON by default (was plaintext). Set `LOG_FORMAT=text` to revert. (#155) ### Fixed - Fix race condition when two clients save the same widget concurrently. (#144) - Fix silent failure when `price_cents` was negative (now returns 400). (#147) ### Security - Rotate JWT signing key on startup if `JWT_SECRET` is the default dev value. (#160) --- ### PR references - (#142) [Title] — @author, YYYY-MM-DD - (#144) [Title] — @author, YYYY-MM-DD - ... -
Write to
changelog/{version}.mdatomically (*.tmp→ rename). Where{version}is the target version slug (e.g.v1.5.0.mdorunreleased.md). -
Append to
outputs.json(type: "changelog",title: "Changelog draft — {target-version}",summary: "{N} entries: {added} added, {changed} changed, {fixed} fixed, {security} security. {M} PRs considered, {K} skipped as non-user-facing.",path: "changelog/{version}.md",status: "draft", timestamps). -
Summarize to user — one paragraph: counts per Keep-A- Changelog heading, the top 1-2 breaking changes to call out, any PRs where you were unsure (ask the user to confirm), and the path. End with: "This is a draft snippet. Copy the block above into your canonical CHANGELOG.md at the repo root, review, and commit. I never auto-commit the CHANGELOG."
Hard nos
- Never auto-commit the canonical CHANGELOG.md — this is the
load-bearing hard no for this skill. Output is a draft snippet at
changelog/{version}.mdonly; the user pastes intoCHANGELOG.mdat their repo root and commits. - Never invent PRs / issues — every entry cites a real PR number.
- Never omit
**BREAKING:**prefix on breaking changes. - Never write without reading
engineering-context.mdfirst.
Outputs
changelog/{version}.md— draft snippet for ONE release.- Appends to
outputs.jsonwith{ id, type: "changelog", title, summary, path, status: "draft", createdAt, updatedAt }.