| name | tdk-bump |
| description | Generate Keep-a-Changelog entries for .specify/, .claude/, .github/ config changes. Use when updating commands, scripts, templates, or governance files. |
| metadata | {"version":"1.4.0"} |
tdk-bump
Generate changelog entries for project configuration changes.
Definition of Done
This skill completes SUCCESS only when ALL:
- New version entry in
.specify/CHANGELOG.md
marketplace.json.metadata.version matches the bumped version
- All affected plugin.json bumped across every existing manifest format (
.claude-plugin/, .codex-plugin/, .cursor-plugin/). Missing Codex/Cursor mirrors are auto-scaffolded by plugin-bump.
- All changed skill/agent/hook/command components bumped — "changed" = any file in component directory M/A/D (not just SKILL.md). Removed components NOT bumped, listed under
### Removed in CHANGELOG.
- Step 14
verify.ts and release manifest check both exit 0 — includes per-format checks on claude/codex/cursor plugin.json and .specify/release-manifest.json freshness.
ANY fail → report FAILED, DO NOT print success summary.
Prerequisite: plugin-bump
This skill delegates per-plugin version cascade (3-format plugin.json sync, component bumps, per-plugin CHANGELOG) to the plugin-bump skill from plugins-toolkit. Before running, ensure plugin-bump is installed and reachable:
PLUGIN_BUMP_RUN="$HOME/.claude/plugins/marketplaces/agent-plugins-marketplace/plugins/plugins-toolkit/skills/plugin-bump/scripts/run.ts"
test -f "$PLUGIN_BUMP_RUN" || { echo "plugin-bump missing — install plugins-toolkit"; exit 2; }
If plugin-bump is unavailable, fail fast in Step 11 with an actionable hint — do NOT silently fall back to single-format Claude-only bump (that would leave Codex/Cursor mirrors stale).
Usage
/tdk-bump [--since <ref>] [--auto] [--bump patch|minor|major]
- No args: changelog from staged changes (
git diff --cached)
--since <ref>: changelog from <ref>..HEAD (e.g., --since v1.0.0, --since HEAD~5)
--auto: skip all confirmations, use sensible defaults (no AskUserQuestion calls)
--bump <type>: force version bump type in auto mode (default: patch). Ignored unless --auto is set.
Tooling Rules
Prefer TypeScript scripts over Python oneliners or jq. Existing utilities:
bun .specify/scripts/ts/src/utils/json-field.ts get <file> <dot.path>
bun .specify/scripts/ts/src/utils/json-field.ts get .claude-plugin/marketplace.json metadata.version
bun .specify/scripts/ts/src/utils/json-field.ts get .claude-plugin/marketplace.json metadata --json
bun .specify/scripts/ts/src/utils/json-field.ts set .claude-plugin/marketplace.json metadata.version 1.34.0
If a task requires something the existing TS scripts don't cover, ask the user to create a new utility script rather than writing an ad-hoc Python or shell oneliner.
The version field is already returned by Step 1's collect-diff-data.ts output — do not re-read marketplace.json separately.
Workflow
Auto mode progress: Print [N/14] <step name>... ✓ for each step. Missing printouts = visible skip.
Step 1: Collect diff data
Run the helper script to get structured change data:
bun .claude/skills/tdk-bump/scripts/collect-diff-data.ts [--since <ref>]
Output is JSON: { "version": "x.y.z"|null, "changes": [{status, path, group, old_path?}] }
Version is read from marketplace.json → metadata.version. Changelog exclude patterns are read from .specify/.specify.json → changelog.exclude.
Step 2: Handle missing version
If version from Step 1 is null (marketplace.json has no version or file is missing):
Auto mode (--auto): Set version to 0.1.0 automatically. Print: "No version found — defaulting to 0.1.0".
Interactive mode: Use AskUserQuestion:
- Question: "
.specify/plugins/.claude-plugin/marketplace.json has no version. Set one now?"
- Options:
- "Set to 0.1.0" (Recommended)
- "Let me specify version"
Then:
- Read
marketplace.json, add/update metadata.version field with chosen value
- Set
version variable for subsequent steps
Step 3: Handle empty results
If changes array is empty, inform user: "No config changes detected in .specify/, .claude/, .github/. Try git add first or use --since <ref>." Then stop.
Step 4: Warn on large changesets
If 50+ files in changes:
Auto mode (--auto): Proceed without confirmation. Print a warning: "Warning: large changeset (N files) — proceeding in auto mode."
Interactive mode: Use AskUserQuestion to confirm proceeding or suggest --since with narrower range.
Step 5: Read actual diffs
Run git diff to get file contents for semantic analysis:
git diff --cached -- <file1> <file2> ...
git diff <ref>..HEAD -- <file1> <file2> ...
Read the diffs to understand WHAT changed, not just which files.
Step 6: Discover affected components
Run the manifest script to detect which files and components have changed per plugin:
bun .specify/scripts/ts/src/commands/manifest/compute.ts --project-root <project-root>
Parse JSON output. Structure: { "<plugin-name>": { new_files, changed_files, removed_files, unchanged_files, new_components, changed_components, unchanged_components, removed_components } }
This step is discovery only — you do NOT compute new versions here. Versions are owned by plugin-bump (Step 11) which max-wins semver from per-plugin git diff and writes new versions into definition files (SKILL.md frontmatter, agent version, hooks.json version). Then manifest compute --write in Step 12 reads those versions back as source-of-truth.
What to extract from this output for downstream steps:
- Affected plugins: any plugin with non-empty
new_files, changed_files, or removed_files → goes to Step 11 for plugin-bump delegation.
- Components per category (for changelog drafting in Step 7):
new_components.{type} → "Added" bullets
changed_components.{type} → "Changed" bullets
removed_components.{type} → "Removed" bullets (render as - skill-foo (was 1.2.3) using the version from the previous manifest entry — that record disappears after plugin-bump runs, so capture it now)
Keep this discovery data in memory for Step 7 (categorize) and Step 11 (build --added/--changed/--removed flags per plugin).
Step 7: Categorize and draft entries
Map each change to Keep-a-Changelog categories:
| Git Status | Default Category |
|---|
A (added) | Added |
M (modified) | Changed |
R (renamed) | Changed |
D (deleted) | Removed |
Group entries by component (from script output: Scripts, Commands, Templates, Embedded Skills, Claude Agent Config, GitHub Config, Memory, Configurations, Guides, Setup, Docs, General).
Nested grouping rule: When a component has 2+ entries in the same category (Added/Changed/Removed), nest them under one parent line instead of repeating the component tag:
This avoids repetitive **[tdk-core]** tags across many lines. The parent line MAY include a brief summary when one naturally fits; otherwise just the component tag is sufficient.
Draft human-readable descriptions by reading the actual diffs. Be specific about what changed, not just "updated file X".
Step 8: Ask version bump type
Auto mode (--auto):
- If
--bump is explicitly provided, use that value.
- Otherwise, infer from the changeset:
major: any D (deleted) entries in critical files (skills, commands, templates, configurations) — or breaking schema/contract changes detected in diffs
minor: any A (added) entries exist (new skills, commands, templates, etc.)
patch: only M (modified) or R (renamed) entries — tweaks/fixes to existing files
- Print: "Version bump: {type} (auto-inferred)" or "Version bump: {type} (--bump override)".
Interactive mode: Use AskUserQuestion:
- Question: "What type of version bump?" with AI suggestion based on change nature
- Options: patch (default, bug fixes/tweaks), minor (new features/capabilities), major (breaking changes)
Step 9: Preview and confirm
Auto mode (--auto): Skip preview. Print drafted entries to stdout, then proceed directly to Step 10.
Interactive mode: Use AskUserQuestion to show numbered draft entries. User can:
- Approve as-is
- Reclassify entries (e.g., "move #3 to Fixed", "move #5 to Security")
- Edit descriptions
- Add entries to: Fixed, Security, Deprecated categories
Step 10: Write changelog
If .specify/CHANGELOG.md exists:
- Read first 10 lines, check for
## [ pattern (Keep-a-Changelog format)
- If non-standard format detected:
- Auto mode (
--auto): Overwrite with standard format automatically. Print: "Non-standard CHANGELOG.md detected — overwriting with Keep-a-Changelog format."
- Interactive mode:
AskUserQuestion: overwrite with standard format, or abort
- Prepend new version section after the header block (after the blank line following format/versioning links)
If .specify/CHANGELOG.md does not exist:
- Create with standard header:
# Changelog
All notable changes to the project configuration (.specify/, .claude/, .github/)
will be documented in this file.
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
Version section format:
## [x.y.z] - YYYY-MM-DD
### Added
- **[Component]** Description of single addition
- **[Component-B]** Summary of multiple additions
- First addition detail
- Second addition detail
### Changed
- **[Component]** Description of single change
- **[Component-B]** Summary of multiple changes
- First change detail
- Second change detail
### Removed
- **[Component]** Description of removal
Grouping rule: When a component has 2+ entries in the same category, always use the nested sub-bullet format. Never repeat the same **[Component]** tag on consecutive flat lines. Only include category sections that have entries. Use today's date (from date +%Y-%m-%d).
Step 11: Delegate per-plugin cascade to plugin-bump
For each affected plugin (had new/changed files or components in Step 6), shell out to plugin-bump. This is the single source of truth for per-plugin manifest updates — do NOT hand-edit plugin.json files here.
What plugin-bump handles automatically:
- All three manifest formats:
.claude-plugin/plugin.json (anchor), .codex-plugin/plugin.json, .cursor-plugin/plugin.json
- Auto-scaffolds missing
.codex-plugin/ and .cursor-plugin/ directories by copying the Claude anchor (only version is overwritten on first creation — Codex interface block and Cursor-specific fields are preserved on subsequent runs)
- Cascade bumps to component frontmatter (
SKILL.md metadata.version, agent version)
- Per-plugin
<plugin-dir>/CHANGELOG.md (sibling to the workspace-level .specify/CHANGELOG.md written in Step 10 — they intentionally coexist as rollup + detail)
Invocation per affected plugin:
bun "$PLUGIN_BUMP_RUN" \
--target=<absolute-path-to-plugin-dir> \
--auto \
--added="<bullet>" --added="<bullet>" \
--changed="<bullet>" --changed="<bullet>" \
--removed="<bullet>"
Build --added/--changed/--removed bullets from the Step 7 categorized entries filtered to this plugin only. Each bullet is one logical change (not one file). Reuse the same prose as the workspace CHANGELOG entries.
Semver alignment: plugin-bump auto-derives its own semver per plugin (max-wins from git diff: D=major, A=minor, M/R/C=patch). The workspace bump type from Step 8 may differ — that is intentional. Component bumps inside a plugin follow plugin-bump's decision; marketplace-level bump in Step 12 follows Step 8.
On non-zero exit from plugin-bump:
- Exit 2 (precondition): dirty tree without
--auto, empty diff, invalid ref → report to user verbatim, do NOT proceed to Step 12.
- Exit 4 (verify): plugin-bump's internal 4-check DoD failed → read its failure output, apply the fix, re-run for that plugin.
- Exit 99: unexpected error → bubble up to user.
Run all affected plugins sequentially (or in parallel via background tasks if changes are independent). Wait for ALL to exit 0 before Step 12.
Step 12: Workspace-level finalization
After every affected plugin has been bumped by plugin-bump, tdk-bump owns ONLY the workspace-scope files that plugin-bump deliberately ignores. Three generated writes total:
-
Bump marketplace.json — set .claude-plugin/marketplace.json → metadata.version to the new workspace version (from Step 8).
Codex reads this same file as a legacy-compatible marketplace catalog — no separate .codex-plugin/marketplace.json needed.
-
Refresh manifest.json — run:
bun run manifest --project-root <project-root> --write
manifest compute reads component versions directly from each definition file (SKILL.md metadata.version, agent version, hooks.json version) — the source-of-truth files plugin-bump just wrote. No explicit version propagation needed; the new versions are picked up automatically.
-
Refresh .specify/release-manifest.json — run:
bun .claude/skills/tdk-bump/scripts/generate-release-manifest.ts --project-root <project-root> --write
The release manifest records the shippable .specify/ payload from distribute.json. It is maintainer-only tooling, but the generated JSON is shipped to consumers.
Version consistency rule: After Step 12, the version string for each plugin MUST match across:
manifest.json → plugins.{plugin}.version
.claude-plugin/plugin.json → version
.codex-plugin/plugin.json → version (if exists)
.cursor-plugin/plugin.json → version (if exists)
And each component version MUST match across manifest.json and its definition file frontmatter — enforced by manifest compute reading from frontmatter as the primary source.
Step 13: Summary
Print summary: version bumped to X.Y.Z, N entries written across M categories, per-plugin cascade ran for: [list of affected plugins] (manifest formats touched per plugin), SKILL.md versions synced for: [list of affected component names].
Step 14: Post-flight verification (MANDATORY)
Before invoking verify.ts, ensure --expected-version is known. If the target version is not already established by earlier steps: propose a candidate (from the diff + current marketplace.json.metadata.version) and ask the user via AskUserQuestion to confirm before calling verify.ts. The script WILL exit 1 if --expected-version is missing.
Run the deterministic post-flight check:
bun .specify/scripts/ts/src/commands/changelog/verify.ts \
--expected-version=<confirmed-version> \
--plugins=<affected-plugins-csv> \
--skills=<affected-skills-csv>
Check 3 now reports per format — failure lines look like plugin.json vs manifest [tdk-core/codex] and the fix: hint suggests re-running plugin-bump --target=plugins/<plugin> to resync.
Expected output on success: ALL CHECKS PASSED (exit 0).
On non-zero exit:
- Read the printed failures — each line includes
expected, actual, and an actionable fix: hint with file path.
- Apply the fixes exactly as the hints describe — do NOT invent alternate repairs.
- Re-run
verify.ts with the same flags until it exits 0.
- Only after exit 0 may you print the Step 13 success summary.
Then run the release manifest freshness check:
bun .claude/skills/tdk-bump/scripts/generate-release-manifest.ts --project-root <project-root> --check
On non-zero release manifest check:
- Run the same script with
--write.
- Inspect the
.specify/release-manifest.json diff.
- Re-run
--check until it exits 0.
Never report SUCCESS after a non-zero verify or release manifest check exit. The Definition of Done pins the success contract to both checks exiting 0 — no exception.