|
| 1 | +--- |
| 2 | +description: Generate CHANGES entries from branch commits and PR context |
| 3 | +argument-hint: "[optional additional context about the changes]" |
| 4 | +allowed-tools: Bash(git log:*), Bash(git branch:*), Bash(git symbolic-ref:*), Bash(gh pr view:*), Bash(gh pr list:*), Read, Grep, Glob, Edit |
| 5 | +--- |
| 6 | + |
| 7 | +# Changelog Entry Generator |
| 8 | + |
| 9 | +Generate well-formatted CHANGES entries from the current branch's commits and PR context. This command analyzes commits, categorizes them, and inserts entries into the CHANGES file after user review. |
| 10 | + |
| 11 | +Additional context from user: $ARGUMENTS |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Phase 1: Gather Context |
| 16 | + |
| 17 | +**Goal**: Collect all information needed to generate changelog entries. |
| 18 | + |
| 19 | +**Actions**: |
| 20 | + |
| 21 | +1. **Detect project name** from `pyproject.toml`: |
| 22 | + - Read `pyproject.toml` and extract the `name` field under `[project]` |
| 23 | + - This is used for matching the CHANGES heading format (`## <project> vX.Y.Z`) |
| 24 | + |
| 25 | +2. **Detect trunk branch**: |
| 26 | + ``` |
| 27 | + git symbolic-ref refs/remotes/origin/HEAD |
| 28 | + ``` |
| 29 | + - Strip `refs/remotes/origin/` prefix to get branch name |
| 30 | + - Fall back to `master` if the above fails |
| 31 | + |
| 32 | +3. **Verify not on trunk**: |
| 33 | + - Check current branch: `git branch --show-current` |
| 34 | + - If on trunk, report "Already on trunk branch — nothing to generate" and stop |
| 35 | + |
| 36 | +4. **Read CHANGES file**: |
| 37 | + - Find the CHANGES file (usually `CHANGES` with no extension at project root) |
| 38 | + - Identify the unreleased section heading (e.g., `## vcspull v1.51.x (unreleased)`) |
| 39 | + - Locate the `<!-- END PLACEHOLDER` marker line — this is where new entries are inserted |
| 40 | + - Note any existing entries between `<!-- END PLACEHOLDER -->` and the next `## ` release heading |
| 41 | + - Record which section headings (e.g., `### Bug fixes`, `### Features`) already exist in the unreleased block |
| 42 | + |
| 43 | +5. **Check for PR**: |
| 44 | + ``` |
| 45 | + gh pr view --json number,title,body,labels 2>/dev/null |
| 46 | + ``` |
| 47 | + - If a PR exists, extract the number, title, body, and labels |
| 48 | + - If no PR exists, note that `(#???)` placeholders will be used |
| 49 | + |
| 50 | +6. **Get commits**: |
| 51 | + ``` |
| 52 | + git log <trunk>..HEAD --oneline |
| 53 | + ``` |
| 54 | + - Also get full commit details for body parsing: |
| 55 | + ``` |
| 56 | + git log <trunk>..HEAD --format='%H %s%n%b---' |
| 57 | + ``` |
| 58 | + - If no commits ahead of trunk, report "No commits ahead of trunk" and stop |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## Phase 2: Categorize Commits |
| 63 | + |
| 64 | +**Goal**: Parse commits into changelog categories and group related ones. |
| 65 | + |
| 66 | +### Commit type mapping |
| 67 | + |
| 68 | +Parse the commit type from the `Component(type[sub])` convention in commit subjects: |
| 69 | + |
| 70 | +| Commit type | CHANGES section | Notes | |
| 71 | +|---|---|---| |
| 72 | +| `feat` | Features / New features | New functionality | |
| 73 | +| `fix` | Bug fixes | Bug fixes | |
| 74 | +| `docs` | Documentation | Doc changes | |
| 75 | +| `test` | Tests | Test additions/changes | |
| 76 | +| `refactor` | (only if user-visible) | Skip internal-only refactors | |
| 77 | +| `chore`, `deps` | Development | Maintenance, dependency bumps | |
| 78 | +| `style` | (skip) | Formatting-only changes | |
| 79 | + |
| 80 | +### Grouping rules |
| 81 | + |
| 82 | +- **TDD workflow sequences**: An xfail commit + a fix commit + an xfail-removal commit should collapse into a **single** bug fix entry. The fix commit's message is the primary source. |
| 83 | +- **Dependency bumps**: A `pyproject` deps commit + a CHANGES doc commit = 1 entry under "Breaking changes" (if it's a minimum version bump) or "Development" |
| 84 | +- **Multi-commit features**: Sequential `feat` commits on the same component collapse into one entry |
| 85 | +- **Skip entirely**: merge commits, `commands(feat[...])` commits (adding claude commands), lock-only changes, internal-only refactors |
| 86 | + |
| 87 | +### Output of this phase |
| 88 | + |
| 89 | +A structured list of entries grouped by section, each with: |
| 90 | +- Section name (e.g., "Bug fixes") |
| 91 | +- Entry text (formatted markdown) |
| 92 | +- Source commit(s) for traceability |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +## Phase 3: Generate Entries |
| 97 | + |
| 98 | +**Goal**: Write the exact markdown to be inserted into CHANGES. |
| 99 | + |
| 100 | +### Format rules (derived from existing CHANGES files) |
| 101 | + |
| 102 | +1. **Section headings**: Use `### Section Name` (e.g., `### Bug fixes`, `### Features`) |
| 103 | + |
| 104 | +2. **Section order** (only include sections that have entries): |
| 105 | + - Breaking changes |
| 106 | + - Features / New features |
| 107 | + - Bug fixes |
| 108 | + - Documentation |
| 109 | + - Tests |
| 110 | + - Development |
| 111 | + |
| 112 | +3. **Simple entries** — single bullet: |
| 113 | + ```markdown |
| 114 | + - Brief description of the change (#123) |
| 115 | + ``` |
| 116 | + |
| 117 | +4. **Detailed entries** — sub-heading with description: |
| 118 | + ```markdown |
| 119 | + #### Component: Brief description (#123) |
| 120 | + |
| 121 | + Explanatory paragraph about what changed and why. |
| 122 | + |
| 123 | + - Bullet point with specific detail |
| 124 | + - Another detail |
| 125 | + ``` |
| 126 | + |
| 127 | +5. **PR references**: |
| 128 | + - If PR number is known: `(#512)` |
| 129 | + - If no PR exists: `(#???)` |
| 130 | + |
| 131 | +6. **Match existing style**: |
| 132 | + - Check whether the project uses "Bug fixes" or "Bug Fixes" (match existing capitalization) |
| 133 | + - Check whether "Features" or "New features" is used |
| 134 | + - Preserve the project's conventions |
| 135 | + |
| 136 | +### Entry writing guidelines |
| 137 | + |
| 138 | +- Write from the user's perspective — what changed for them, not internal implementation details |
| 139 | +- Lead with the *what*, not the *why* (the description paragraph handles *why*) |
| 140 | +- Use present tense for the entry title ("Add support for..." not "Added support for...") |
| 141 | +- Don't repeat the section heading in the entry text |
| 142 | +- Keep bullet entries to 1-2 lines; use the sub-heading format for anything needing more explanation |
| 143 | + |
| 144 | +--- |
| 145 | + |
| 146 | +## Phase 4: Present for Review |
| 147 | + |
| 148 | +**CRITICAL**: This is a mandatory confirmation gate. Never skip to Phase 5 without explicit user approval. |
| 149 | + |
| 150 | +**Present to the user**: |
| 151 | + |
| 152 | +1. **Summary line**: |
| 153 | + ``` |
| 154 | + Branch: <branch-name> | Commits: <count> | PR: #<number> (or "none") |
| 155 | + ``` |
| 156 | + |
| 157 | +2. **Proposed entries** in a fenced code block showing the exact markdown: |
| 158 | + ```` |
| 159 | + ```markdown |
| 160 | + ### Bug fixes |
| 161 | +
|
| 162 | + - Fix phantom "None" message when syncing path-based patterns (#512) |
| 163 | +
|
| 164 | + #### cli/sync: Report errored git syncs in summary (#512) |
| 165 | +
|
| 166 | + `update_repo()` now detects and reports git sync failures instead of |
| 167 | + silently succeeding. The sync summary shows errored repositories |
| 168 | + alongside successful and failed counts. |
| 169 | + ``` |
| 170 | + ```` |
| 171 | + |
| 172 | +3. **Insertion point**: Describe where these entries will go: |
| 173 | + ``` |
| 174 | + Insert after: <!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE --> |
| 175 | + Before: (next release heading or existing unreleased entries) |
| 176 | + ``` |
| 177 | + |
| 178 | +4. **Ask the user**: "Insert these entries into CHANGES? You can also ask me to modify them first." |
| 179 | + |
| 180 | +**Wait for user response.** Do not proceed until they confirm. |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +## Phase 5: Insert into CHANGES |
| 185 | + |
| 186 | +**Goal**: Insert the approved entries into the CHANGES file. |
| 187 | + |
| 188 | +**Only execute after explicit user approval in Phase 4.** |
| 189 | + |
| 190 | +### Insertion logic |
| 191 | + |
| 192 | +1. **Find the insertion point**: Locate the `<!-- END PLACEHOLDER` line in the CHANGES file |
| 193 | + |
| 194 | +2. **Check for existing unreleased section headings**: |
| 195 | + - If the CHANGES file already has a `### Bug fixes` section in the unreleased block, and the new entries also have bug fixes, **append** to the existing section rather than creating a duplicate heading |
| 196 | + - If the section doesn't exist yet, insert the full section with heading |
| 197 | + |
| 198 | +3. **Insert the entries**: |
| 199 | + - Use the Edit tool to insert after the `<!-- END PLACEHOLDER -->` line |
| 200 | + - Ensure exactly one blank line between the placeholder comment and the first section heading |
| 201 | + - Ensure exactly one blank line between sections |
| 202 | + |
| 203 | +4. **Show the result**: |
| 204 | + - After editing, read the modified region of the CHANGES file and display it so the user can verify |
| 205 | + - Note: this command does NOT commit — the user decides when to stage and commit the CHANGES update |
| 206 | + |
| 207 | +### Edge case: merging with existing entries |
| 208 | + |
| 209 | +If there are already entries below the placeholder in the unreleased section: |
| 210 | + |
| 211 | +- New entries for **existing sections** are appended at the end of that section (before the next `###` heading or the next `## ` release heading) |
| 212 | +- New entries for **new sections** follow the section order defined in Phase 3 — insert the new section in the correct position relative to existing sections |
| 213 | +- Never duplicate a `###` section heading |
0 commit comments