Compare commits

...

3 Commits

Author SHA1 Message Date
vanzue
380669dd81 update exlude pattern for commit filter if start and end are from different branch 2026-01-07 14:23:01 +08:00
vanzue
d24569d49c Fix the sub agent for contributor 2026-01-07 14:04:09 +08:00
vanzue
94cbea91a0 Skill set for release generation 2026-01-07 13:58:35 +08:00
8 changed files with 848 additions and 0 deletions

View File

@@ -93,6 +93,7 @@
\.xz$
\.zip$
^\.github/actions/spell-check/
^\.github/skills/
^\.github/workflows/spelling\d*\.yml$
^\.gitmodules$
^\Q.pipelines/ESRPSigning_core.json\E$

View File

@@ -0,0 +1,152 @@
---
name: changelog-generator
description: Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
---
# Changelog Generator
This skill transforms technical git commits into polished, user-friendly changelogs that your customers and users will actually understand and appreciate.
## When to Use This Skill
- Preparing release notes for a new version
- Generating changelog between two tags/commits
- Creating draft release notes from recent commits
## Quick Start
```
Create a changelog from commits since v0.96.1
```
```
Create release notes for version 0.97.0 starting from tag v0.96.1
```
---
## Workflow Overview
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1. Fetch │───▶│ 2. Filter │───▶│ 3. Categorize│───▶│ 4. Generate │
│ Commits │ │ & Dedupe │ │ by Module │ │ Descriptions │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│ │ │ │
▼ ▼ ▼ ▼
📄 github-api 📄 commit- 📄 module- 📄 user-facing-
reference.md filtering.md mapping.md description.md
```
## Sub-Skills Reference
This skill is composed of the following sub-skills.
**⚠️ IMPORTANT: Do NOT read all sub-skills at once!**
- Only read a sub-skill when you reach that step in the workflow
- This saves context window and improves accuracy
- Use `read_file` to load a sub-skill only when needed
| Step | Sub-Skill | When to Read |
|------|-----------|--------------|
| Fetch data | [github-api-reference.md](sub-skills/github-api-reference.md) | When fetching commits/PRs |
| Filter commits | [commit-filtering.md](sub-skills/commit-filtering.md) | When checking if commit should be skipped |
| Categorize | [module-mapping.md](sub-skills/module-mapping.md) | When determining which module a PR belongs to |
| Generate text | [user-facing-description.md](sub-skills/user-facing-description.md) | When writing the changelog entry text |
| Attribution | [contributor-attribution.md](sub-skills/contributor-attribution.md) | When checking if author needs thanks |
| Large releases | [progress-tracking.md](sub-skills/progress-tracking.md) | Only if processing 50+ commits |
---
## Step-by-Step Summary
### Step 1: Get Commit Range
```powershell
# Count commits between tags
gh api repos/microsoft/PowerToys/compare/v0.96.0...v0.96.1 --jq '.commits | length'
```
👉 Details: [github-api-reference.md](sub-skills/github-api-reference.md)
### Step 2: Filter Commits
- Skip commits already in start tag
- Skip cherry-picks and backports
- Deduplicate by PR number
👉 Details: [commit-filtering.md](sub-skills/commit-filtering.md)
### Step 3: For Each PR, Generate Entry
1. Get PR title, body, files, labels
2. Determine module from file paths or labels
3. Check if user-facing (skip internal changes)
4. Transform to user-friendly description
5. Add contributor attribution if needed
👉 Details: [user-facing-description.md](sub-skills/user-facing-description.md), [module-mapping.md](sub-skills/module-mapping.md), [contributor-attribution.md](sub-skills/contributor-attribution.md)
### Step 4: Checkpoint (if 50+ commits)
- Save progress after every 15-20 commits
- Track processed PRs for deduplication
- Enable resume from interruption
👉 Details: [progress-tracking.md](sub-skills/progress-tracking.md)
### Step 5: Format Output
```markdown
## ✨ What's new
**Version X.XX (Month Year)**
**✨ Highlights**
- [Most impactful change 1]
- [Most impactful change 2]
### [Module Name - Alphabetical]
- [Description]. Thanks [@contributor](https://github.com/contributor)!
### Development
- [Internal changes]
```
---
## Example Output
```markdown
## ✨ What's new
**Version 0.96.1 (December 2025)**
**✨ Highlights**
- Advanced Paste now supports multiple AI providers.
- PowerRename can extract photo metadata for renaming.
### Advanced Paste
- Added support for Azure OpenAI, Google Gemini, Mistral, and more.
### Awake
- The countdown timer now stays accurate over long periods. Thanks [@daverayment](https://github.com/daverayment)!
### Development
- Resolved build warnings in Command Palette projects.
```
---
## Tips
1. **Propose highlights** after all entries are generated
2. **Check PR body** when title is unclear
3. **Thank external contributors** - see [contributor-attribution.md](sub-skills/contributor-attribution.md)
4. **Use progress tracking** for large releases - see [progress-tracking.md](sub-skills/progress-tracking.md)
5. **Save output** to `release-change-note-draft.md`
---
## Troubleshooting
| Problem | Solution |
|---------|----------|
| Lost progress mid-generation | Read `release-notes-progress.md` to resume |
| Duplicate entries | Check processed PR list, dedupe by PR number |
| Commit already released | Use `git merge-base --is-ancestor` to verify |
| API rate limited | Check `gh api rate_limit`, wait or use token |
👉 See sub-skills for detailed troubleshooting.

View File

@@ -0,0 +1,107 @@
# Commit Filtering Rules
This sub-skill defines rules for filtering commits to include in the changelog.
## Understanding the Branch Model
PowerToys uses a release branch model where fixes are cherry-picked from main:
```
main: A---B---C---D---E---F---G---H (HEAD)
\
release: X---Y---Z (v0.96.1 tag)
(X, Y are cherry-picks of C, E from main)
```
**Key insight:** When comparing `v0.96.1...main`:
- The release tag (v0.96.1) is on a **different branch** than main
- GitHub compare finds the merge-base and returns commits on main after that point
- Commits C and E appear in the results even though they were cherry-picked to release as X and Y
- **The SHAs are different**, so SHA-based filtering won't work!
## ⚠️ CRITICAL: Filter by PR Number, Not SHA
Since cherry-picks have different SHAs, you **MUST** check by PR number:
```powershell
# Extract PR number from commit message and check if it exists in the release tag
$prNumber = "43785"
$startTag = "v0.96.1"
# Search the release branch for this PR number in commit messages
$cherryPicked = git log $startTag --oneline --grep="#$prNumber"
if ($cherryPicked) {
Write-Host "SKIP: PR #$prNumber was cherry-picked to $startTag"
} else {
Write-Host "INCLUDE: PR #$prNumber is new since $startTag"
}
```
## Complete Filtering Workflow
```powershell
$startTag = "v0.96.1" # Release tag (on release branch)
$endRef = "main" # Target (main branch)
# Step 1: Get all commits from main since the merge-base with release tag
$commits = gh api "repos/microsoft/PowerToys/compare/$startTag...$endRef" `
--jq '.commits[] | {sha: .sha, message: .commit.message}' | ConvertFrom-Json
# Step 2: Build list of PR numbers already in the release tag
$releasePRs = git log $startTag --oneline | Select-String -Pattern '#(\d+)' -AllMatches |
ForEach-Object { $_.Matches.Groups[1].Value } | Sort-Object -Unique
Write-Host "PRs already in $startTag : $($releasePRs.Count)"
# Step 3: Filter commits - skip if PR was cherry-picked to release
$newCommits = @()
foreach ($commit in $commits) {
if ($commit.message -match '#(\d+)') {
$prNumber = $matches[1]
if ($releasePRs -contains $prNumber) {
Write-Host "SKIP: PR #$prNumber already in $startTag (cherry-picked)"
continue
}
}
$newCommits += $commit
}
Write-Host "New commits to process: $($newCommits.Count)"
```
## Why SHA-Based Methods Don't Work Here
| Method | Works for same branch? | Works for cross-branch (cherry-picks)? |
|--------|------------------------|----------------------------------------|
| `git merge-base --is-ancestor` | ✅ Yes | ❌ No - different SHAs |
| `git tag --contains` | ✅ Yes | ❌ No - tag is on different branch |
| GitHub Compare API | ✅ Yes | ❌ No - returns commits by SHA |
| **PR number matching** | ✅ Yes | ✅ **Yes** |
## Skip Rules Summary
| Priority | Condition | Action |
|----------|-----------|--------|
| 1 | PR number found in `git log $startTag --grep="#$prNumber"` | **SKIP** - cherry-picked |
| 2 | Same PR number already processed in this run | **SKIP** - duplicate |
| 3 | Bot author (dependabot, etc.) | **SKIP** - unless user-visible |
| 4 | Internal-only change (CI, tests, refactor) | Move to **Development** section |
## User-Facing vs Non-User-Facing
**Include in changelog:**
- New features and capabilities
- Bug fixes that affect users
- UI/UX improvements
- Performance improvements users would notice
- Breaking changes or behavior modifications
- Security fixes
**Exclude from changelog (put in Development section):**
- Internal refactoring
- CI/CD changes
- Code style fixes
- Test additions/modifications
- Documentation-only changes
- Dependency updates (unless user-visible impact)

View File

@@ -0,0 +1,110 @@
# Contributor Attribution Rules
This sub-skill defines when and how to credit contributors in changelog entries.
## Attribution Rules
- Check [COMMUNITY.md](../../../COMMUNITY.md) for the "PowerToys core team" section
- If author is **NOT** in core team → Add `Thanks [@username](https://github.com/username)!`
- If author **IS** in core team → No attribution needed
- Microsoft employees working on PowerToys as their job → No attribution needed
## Core Team Members
Current core team members (from COMMUNITY.md) - do NOT thank these:
```
@craigloewen-msft, @niels9001, @dhowett, @yeelam-gordon, @jamrobot,
@lei9444, @shuaiyuanxx, @moooyo, @haoliuu, @chenmy77, @chemwolf6922,
@yaqingmi, @zhaoqpcn, @urnotdfs, @zhaopy536, @wang563681252, @vanzue,
@zadjii-msft, @khmyznikov, @chatasweetie, @MichaelJolley, @Jaylyn-Barbee,
@zateutsch, @crutkas
```
## Check Author Script
```powershell
$coreTeam = @(
'craigloewen-msft', 'niels9001', 'dhowett', 'yeelam-gordon', 'jamrobot',
'lei9444', 'shuaiyuanxx', 'moooyo', 'haoliuu', 'chenmy77', 'chemwolf6922',
'yaqingmi', 'zhaoqpcn', 'urnotdfs', 'zhaopy536', 'wang563681252', 'vanzue',
'zadjii-msft', 'khmyznikov', 'chatasweetie', 'MichaelJolley', 'Jaylyn-Barbee',
'zateutsch', 'crutkas'
)
function Get-Attribution {
param([string]$author)
if ($coreTeam -contains $author) {
return $null # No attribution needed
}
return "Thanks [@$author](https://github.com/$author)!"
}
# Usage
$author = gh pr view 12345 --repo microsoft/PowerToys --json author --jq '.author.login'
$attribution = Get-Attribution $author
if ($attribution) {
Write-Host "Add: $attribution"
} else {
Write-Host "No attribution needed (core team member)"
}
```
## Attribution Format
**With attribution:**
```markdown
- The Awake countdown timer now stays accurate over long periods. Thanks [@daverayment](https://github.com/daverayment)!
```
**Without attribution (core team):**
```markdown
- Added new feature to Command Palette for opening settings.
```
## Special Cases
1. **Co-authored commits**: Credit the primary author (first in list)
2. **Bot accounts** (dependabot, etc.): No attribution
3. **Former core team members**: Check if they were core team at time of PR
4. **Multiple PRs by same external contributor**: Thank them on each entry
## High-Impact Community Members
These contributors have made significant ongoing contributions and are recognized in COMMUNITY.md.
**ALWAYS thank these contributors** - they are NOT core team and deserve recognition:
```
@davidegiacometti, @htcfreek, @daverayment, @jiripolasek
```
Check COMMUNITY.md for the full up-to-date list under "High impact community members" section.
## Updated Check Author Script
```powershell
$coreTeam = @(
'craigloewen-msft', 'niels9001', 'dhowett', 'yeelam-gordon', 'jamrobot',
'lei9444', 'shuaiyuanxx', 'moooyo', 'haoliuu', 'chenmy77', 'chemwolf6922',
'yaqingmi', 'zhaoqpcn', 'urnotdfs', 'zhaopy536', 'wang563681252', 'vanzue',
'zadjii-msft', 'khmyznikov', 'chatasweetie', 'MichaelJolley', 'Jaylyn-Barbee',
'zateutsch', 'crutkas'
)
# High-impact community members - ALWAYS thank these!
$highImpactCommunity = @(
'davidegiacometti', 'htcfreek', 'daverayment', 'jiripolasek'
)
function Get-Attribution {
param([string]$author)
# Core team and bots don't need thanks
if ($coreTeam -contains $author -or $author -match '\[bot\]$') {
return $null
}
# Everyone else (including high-impact community) gets thanked
return "Thanks [@$author](https://github.com/$author)!"
}
```

View File

@@ -0,0 +1,101 @@
# GitHub API Reference for Changelog Generation
This sub-skill provides GitHub API commands and scripts for fetching commit and PR data.
## Authentication
```powershell
# Ensure gh CLI is authenticated
gh auth status
# Or use personal access token
$headers = @{ Authorization = "token $env:GITHUB_TOKEN" }
```
## Useful API Endpoints
```powershell
# Compare two refs (tags, branches, commits)
gh api repos/microsoft/PowerToys/compare/v0.96.0...v0.96.1
# List commits with pagination
gh api "repos/microsoft/PowerToys/commits?per_page=100&page=1"
# Get PR associated with a commit
gh api repos/microsoft/PowerToys/commits/{sha}/pulls
# Get PR details
gh api repos/microsoft/PowerToys/pulls/{number}
# Get files changed in a PR
gh api repos/microsoft/PowerToys/pulls/{number}/files
# Search PRs merged in date range
gh api "search/issues?q=repo:microsoft/PowerToys+is:pr+is:merged+merged:2025-01-01..2025-01-31"
```
## Fetch Commits in Range
```powershell
$owner = "microsoft"
$repo = "PowerToys"
$startTag = "v0.96.0"
$endTag = "v0.96.1"
# Get all commits between two tags
gh api repos/$owner/$repo/compare/$startTag...$endTag --jq '.commits[] | {sha: .sha, message: .commit.message, author: .author.login, date: .commit.author.date}'
```
## Get PR Details for a Commit
```powershell
# Get PR associated with a commit SHA
gh api "repos/$owner/$repo/commits/{sha}/pulls" --jq '.[0] | {number: .number, title: .title, body: .body, user: .user.login, labels: [.labels[].name]}'
# Get full PR details
gh pr view 1234 --repo microsoft/PowerToys --json title,body,author,files,labels,mergedAt
```
## Batch Processing Script
```powershell
$startTag = "v0.96.0"
$endTag = "v0.96.1"
$commits = gh api repos/microsoft/PowerToys/compare/$startTag...$endTag --jq '.commits[].sha' | ForEach-Object {
$sha = $_
$prInfo = gh api "repos/microsoft/PowerToys/commits/$sha/pulls" 2>$null | ConvertFrom-Json
if ($prInfo) {
[PSCustomObject]@{
SHA = $sha
PRNumber = $prInfo[0].number
Title = $prInfo[0].title
Author = $prInfo[0].user.login
Labels = $prInfo[0].labels.name -join ", "
}
}
}
$commits | Format-Table
```
## Rate Limiting
```powershell
# Check remaining rate limit
gh api rate_limit --jq '.rate'
# Authenticated: 5000 requests/hour
# Unauthenticated: 60 requests/hour
```
## Pagination for Large Results
```powershell
# GitHub API returns max 100 items per page
$page = 1
$allCommits = @()
do {
$commits = gh api "repos/$owner/$repo/commits?per_page=100&page=$page" | ConvertFrom-Json
$allCommits += $commits
$page++
} while ($commits.Count -eq 100)
```

View File

@@ -0,0 +1,137 @@
# PowerToys Module Path Mapping
This sub-skill maps file paths to PowerToys module names for categorization.
## Module Path Mapping Table
| Module | Path Pattern |
|--------|--------------|
| Advanced Paste | `src/modules/AdvancedPaste/**` |
| Always On Top | `src/modules/alwaysontop/**` |
| Awake | `src/modules/Awake/**` |
| Color Picker | `src/modules/colorPicker/**` |
| Command Palette | `src/modules/cmdpal/**` |
| Crop And Lock | `src/modules/CropAndLock/**` |
| Environment Variables | `src/modules/EnvironmentVariables/**` |
| FancyZones | `src/modules/fancyzones/**` |
| File Explorer Add-ons | `src/modules/previewpane/**`, `src/modules/FileExplorerPreview/**` |
| File Locksmith | `src/modules/FileLocksmith/**` |
| Find My Mouse | `src/modules/MouseUtils/FindMyMouse/**` |
| Hosts File Editor | `src/modules/Hosts/**` |
| Image Resizer | `src/modules/imageresizer/**` |
| Keyboard Manager | `src/modules/keyboardmanager/**` |
| Light Switch | `src/modules/LightSwitch/**` |
| Mouse Highlighter | `src/modules/MouseUtils/MouseHighlighter/**` |
| Mouse Jump | `src/modules/MouseUtils/MouseJump/**` |
| Mouse Pointer Crosshairs | `src/modules/MouseUtils/MousePointerCrosshairs/**` |
| Mouse Without Borders | `src/modules/MouseWithoutBorders/**` |
| New+ | `src/modules/NewPlus/**` |
| Paste As Plain Text | `src/modules/PastePlain/**` |
| Peek | `src/modules/Peek/**` |
| PowerRename | `src/modules/powerrename/**` |
| PowerToys Run | `src/modules/launcher/**` |
| Quick Accent | `src/modules/QuickAccent/**` |
| Registry Preview | `src/modules/RegistryPreview/**` |
| Screen Ruler | `src/modules/MeasureTool/**` |
| Shortcut Guide | `src/modules/ShortcutGuide/**` |
| Text Extractor | `src/modules/TextExtractor/**` |
| Video Conference Mute | `src/modules/videoconference/**` |
| Workspaces | `src/modules/Workspaces/**` |
| ZoomIt | `src/modules/ZoomIt/**` |
| Settings | `src/settings-ui/**` |
| Runner | `src/runner/**` |
| Installer | `installer/**` |
| General / Infrastructure | `src/common/**`, `.github/**`, `tools/**` |
## Categorization by PR Labels
Common PowerToys PR labels for modules:
- `Product-FancyZones`
- `Product-PowerToys Run`
- `Product-Awake`
- `Product-ColorPicker`
- `Product-Keyboard Manager`
- etc.
## Auto-Categorization Script
```powershell
function Get-ModuleFromPath {
param([string]$filePath)
$moduleMap = @{
'src/modules/AdvancedPaste/' = 'Advanced Paste'
'src/modules/alwaysontop/' = 'Always On Top'
'src/modules/Awake/' = 'Awake'
'src/modules/colorPicker/' = 'Color Picker'
'src/modules/cmdpal/' = 'Command Palette'
'src/modules/CropAndLock/' = 'Crop And Lock'
'src/modules/EnvironmentVariables/' = 'Environment Variables'
'src/modules/fancyzones/' = 'FancyZones'
'src/modules/previewpane/' = 'File Explorer Add-ons'
'src/modules/FileExplorerPreview/' = 'File Explorer Add-ons'
'src/modules/FileLocksmith/' = 'File Locksmith'
'src/modules/MouseUtils/FindMyMouse/' = 'Find My Mouse'
'src/modules/Hosts/' = 'Hosts File Editor'
'src/modules/imageresizer/' = 'Image Resizer'
'src/modules/keyboardmanager/' = 'Keyboard Manager'
'src/modules/LightSwitch/' = 'Light Switch'
'src/modules/MouseUtils/MouseHighlighter/' = 'Mouse Highlighter'
'src/modules/MouseUtils/MouseJump/' = 'Mouse Jump'
'src/modules/MouseUtils/MousePointerCrosshairs/' = 'Mouse Pointer Crosshairs'
'src/modules/MouseWithoutBorders/' = 'Mouse Without Borders'
'src/modules/NewPlus/' = 'New+'
'src/modules/PastePlain/' = 'Paste As Plain Text'
'src/modules/Peek/' = 'Peek'
'src/modules/powerrename/' = 'PowerRename'
'src/modules/launcher/' = 'PowerToys Run'
'src/modules/QuickAccent/' = 'Quick Accent'
'src/modules/RegistryPreview/' = 'Registry Preview'
'src/modules/MeasureTool/' = 'Screen Ruler'
'src/modules/ShortcutGuide/' = 'Shortcut Guide'
'src/modules/TextExtractor/' = 'Text Extractor'
'src/modules/videoconference/' = 'Video Conference Mute'
'src/modules/Workspaces/' = 'Workspaces'
'src/modules/ZoomIt/' = 'ZoomIt'
'src/settings-ui/' = 'Settings'
'src/runner/' = 'Runner'
'installer/' = 'Installer'
'src/common/' = 'General'
'.github/' = 'Development'
'tools/' = 'Development'
}
foreach ($pattern in $moduleMap.Keys) {
if ($filePath -like "*$pattern*") {
return $moduleMap[$pattern]
}
}
return 'General'
}
# Usage: categorize a PR by its changed files
$files = gh pr view 12345 --repo microsoft/PowerToys --json files --jq '.files[].path'
$modules = $files | ForEach-Object { Get-ModuleFromPath $_ } | Sort-Object -Unique
Write-Host "PR affects modules: $($modules -join ', ')"
```
## Output Organization
Modules should be listed in **alphabetical order** in the changelog:
```markdown
### Advanced Paste
- ...
### Awake
- ...
### Command Palette
- ...
### General
- ...
### Development
- ...
```

View File

@@ -0,0 +1,121 @@
# Progress Tracking for Large Changelogs
This sub-skill provides the checkpoint mechanism for processing many commits without losing progress.
## When to Use Progress Tracking
- Processing 50+ commits
- Long-running changelog generation
- Risk of context overflow in AI conversations
## Checkpoint Mechanism
1. **Before starting**: Create a progress tracking file `release-notes-progress.md`
2. **After each batch**: Append processed results to `release-change-note-draft.md`
3. **Track position**: Record the last processed commit SHA in `release-notes-progress.md`
## Progress File Template
Create `release-notes-progress.md`:
```markdown
# Release Notes Generation Progress
## Configuration
- Start Tag: v0.96.0
- End Tag: v0.96.1
- Total Commits: 127
- Batch Size: 20
## Progress Tracker
| Batch | Status | Last SHA | PRs Processed |
|-------|--------|----------|---------------|
| 1 (1-20) | ✅ Done | abc1234 | #1001, #1002, #1003... |
| 2 (21-40) | ✅ Done | def5678 | #1004, #1005... |
| 3 (41-60) | 🔄 In Progress | ghi9012 | #1006... |
| 4 (61-80) | ⏳ Pending | - | - |
| 5 (81-100) | ⏳ Pending | - | - |
| 6 (101-120) | ⏳ Pending | - | - |
| 7 (121-127) | ⏳ Pending | - | - |
## Processed PRs (deduplication list)
#1001, #1002, #1003, #1004, #1005, #1006
## Last Checkpoint
- Timestamp: 2025-01-07 10:30:00
- Last processed commit: ghi9012
- Next commit to process: jkl3456
```
## Batch Processing Workflow
```
┌─────────────────────────────────────────────────────────────┐
│ 1. Get total commit count and create progress file │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. Filter: Skip commits already in start tag │
│ - Check if commit is ancestor of start tag │
│ - Skip cherry-picks or backports already released │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. Process batch of 15-20 commits │
│ - Fetch commit details │
│ - Get associated PRs │
│ - Generate changelog entries │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. CHECKPOINT: Save progress │
│ - Append entries to release-change-note-draft.md │
│ - Update release-notes-progress.md with last SHA │
│ - Record processed PR numbers │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. Check: More commits remaining? │
│ YES → Go to step 3 with next batch │
│ NO → Go to step 6 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. Final merge and formatting │
│ - Combine all batches │
│ - Deduplicate by PR number │
│ - Sort by module alphabetically │
│ - Add highlights section │
└─────────────────────────────────────────────────────────────┘
```
## Resuming from Checkpoint
If interrupted, read `release-notes-progress.md` to find:
1. Which batch was last completed
2. The SHA of the last processed commit
3. Which PRs have already been processed (for deduplication)
Then continue from the next unprocessed commit:
```powershell
# Find where you left off
$lastSha = "abc1234" # from progress file
$remainingShas = gh api repos/microsoft/PowerToys/compare/$lastSha...main --jq '.commits[].sha'
```
## Batch Size Recommendations
| Total Commits | Recommended Batch Size |
|---------------|------------------------|
| < 30 | Process all at once |
| 30-100 | 15-20 per batch |
| 100-300 | 20 per batch |
| 300+ | 25 per batch + parallel processing |
## Deduplication
Track processed PR numbers to avoid duplicates:
- Same PR can appear multiple times (multiple commits)
- Cherry-picks may reference same PR
- Always check `Processed PRs` list before generating entry

View File

@@ -0,0 +1,119 @@
# Generating User-Facing Descriptions
This sub-skill explains how to transform technical PR/commit info into user-friendly changelog entries.
## Information Sources (Priority Order)
1. **PR Title** - Often the best summary, already user-facing
2. **PR Description/Body** - Look for "What does this PR do?" or summary sections
3. **Commit Message** - First line is usually descriptive
4. **Changed Files** - Infer the impact from what was modified
5. **PR Labels** - Indicate type (bug, feature, enhancement)
## Data Collection Command
```powershell
# Get all relevant info for a PR
$prNumber = 12345
$prData = gh pr view $prNumber --repo microsoft/PowerToys --json title,body,author,files,labels,mergedAt
# Parse the data
$pr = $prData | ConvertFrom-Json
Write-Host "Title: $($pr.title)"
Write-Host "Author: $($pr.author.login)"
Write-Host "Labels: $($pr.labels.name -join ', ')"
Write-Host "Files changed: $($pr.files.Count)"
Write-Host "Body preview: $($pr.body.Substring(0, [Math]::Min(500, $pr.body.Length)))"
```
## Transformation Rules
| Source Info | Transformation | Example Output |
|-------------|----------------|----------------|
| PR Title: "Fix null reference in FancyZones editor" | Describe the fix from user perspective | "Fixed a crash that could occur when editing zone layouts." |
| PR Title: "Add support for XYZ format" | State the new capability | "Added support for XYZ format in File Explorer preview." |
| PR Title: "[FancyZones] Refactor grid logic" | Check if user-visible; if not → Development section | (Development) "Refactored FancyZones grid logic for improved maintainability." |
| PR with label "bug" | Frame as a fix | "Fixed an issue where..." |
| PR with label "enhancement" | Frame as improvement | "Improved..." or "Enhanced..." |
## Description Generation Process
```
┌────────────────────────────────────────────────────────────────┐
│ INPUT: PR #12345 │
│ Title: "Fix Awake timer drift after system sleep" │
│ Body: "The timer was resetting incorrectly when the system │
│ resumed from sleep, causing the countdown to be wrong." │
│ Author: @daverayment │
│ Labels: [bug, Product-Awake] │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ ANALYSIS: │
│ 1. Is it user-facing? YES (timer behavior affects users) │
│ 2. Category: Bug fix │
│ 3. Module: Awake (from label + file paths) │
│ 4. Author in core team? NO → needs attribution │
└────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ OUTPUT: │
│ "The Awake countdown timer now stays accurate over long │
│ periods. Thanks [@daverayment](https://github.com/daverayment)!" │
└────────────────────────────────────────────────────────────────┘
```
## Writing Style Guidelines
**DO:**
- Start with what changed or what users can now do
- Use active voice: "Added...", "Fixed...", "Improved..."
- Be specific about the benefit to users
- Keep it concise (1-2 sentences max)
- Include link to documentation if it's a new feature
**DON'T:**
- Use technical jargon users won't understand
- Reference internal code names, file names, or class names
- Say "Fixed bug" without explaining what was wrong
- Include PR numbers in the description (they're tracked separately)
## Example Transformations
| Technical PR Title | User-Facing Description |
|--------------------|------------------------|
| "Fix NRE in FZEditor when zones is null" | "Fixed a crash that could occur when opening the FancyZones editor with no saved layouts." |
| "Add ExifTool integration for PowerRename" | "PowerRename can now extract and use photo metadata (EXIF, XMP) in renaming patterns like `%Camera`, `%Lens`, and `%ExposureTime`." |
| "Perf: Reduce memory allocation in PT Run" | "Improved PowerToys Run startup performance and reduced memory usage." |
| "Update Newtonsoft.Json to 13.0.3" | (Development) "Updated Newtonsoft.Json dependency." OR skip if no user impact |
| "Add unit tests for color picker" | SKIP - not user-facing |
| "Fix typo in settings UI" | "Fixed a typo in the Settings interface." (only if visible to users) |
## Context-Aware Description
Sometimes you need to read the PR body or changed files to understand the impact:
```powershell
# If PR title is unclear, check the body for context
$prBody = gh pr view 12345 --repo microsoft/PowerToys --json body --jq '.body'
# Look for common patterns in PR descriptions:
# - "## Summary" or "## Description"
# - "This PR fixes/adds/improves..."
# - "Before/After" comparisons
# - Screenshots (indicate UI changes)
# Check what files changed to understand scope
$files = gh pr view 12345 --repo microsoft/PowerToys --json files --jq '.files[].path'
# If mostly .xaml files → UI change
# If mostly .cs/.cpp in one module → module-specific change
# If in src/common/ → potentially affects multiple modules
```
## Handling Ambiguous PRs
If a PR's impact is unclear:
1. **Check the linked issue** - often has user-reported symptoms
2. **Look at file changes** - understand what was modified
3. **Check PR comments** - may have discussion about user impact
4. **When in doubt** - put in Development section or ask for clarification