(ywc) Use when working on a release PR (e.g., develop→main) and the user wants to generate the merged PR list grouped by author. Triggers: "release PR list", "릴리즈 PR 정리", "릴리스 PR 리스트", "release pr 정리", "リリースPR一覧", "ywc-release-pr-list", or any request to build a release-PR description from merged PR numbers. Do not use for normal feature PR creation (use ywc-create-pr), handling PR review comments (use ywc-handle-pr-reviews), or CHANGELOG / release-note generation from git history or merged P...
Scanned 9/2/2026
Install to Claude Code
npx -y skills add yongwoon/ywc-agent-toolkit --skill ywc-release-pr-list --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ywc Release Pr List?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/yongwoon-ywc-release-pr-list)More formats (shields.io, HTML) on the badges page.
---
name: ywc-release-pr-list
version: 1.0.0
description: (ywc) Use when working on a release PR (e.g., develop→main) and the user wants to generate the merged PR list grouped by author. Triggers: "release PR list", "릴리즈 PR 정리", "릴리스 PR 리스트", "release pr 정리", "リリースPR一覧", "ywc-release-pr-list", or any request to build a release-PR description from merged PR numbers. Do not use for normal feature PR creation (use ywc-create-pr), handling PR review comments (use ywc-handle-pr-reviews), or CHANGELOG / release-note generation from git history or merged PRs (use ywc-changelog-release-notes).
category: release
phase: release
requires: []
advisor_budget: 0
allowed tools: Bash, Read, Glob, Grep
---
# Release PR List
**Announce at start:** "I'm using the ywc-release-pr-list skill to compile the merged PR list for the release PR."
Generate the PR list for a release PR and update the PR description.
## Rationalization Defense
When tempted to skip a step, check this table first:
| Excuse | Reality |
|---|---|
| "Some commit headlines lack PR numbers, skip those" | Stop and report missing PR numbers. They likely indicate squashed-without-link or direct pushes that need attention. |
| "Author resolution failed for one PR, leave it blank" | Always retry author lookup via `gh pr view <num> --json author`. Blank author breaks grouping. |
| "Author summaries make the list verbose, skip them" | Summary inclusion is interactively asked at the start. Honor that user choice exactly. |
| "Updating the target PR description requires force, skip on conflict" | Use `gh pr edit --body-file -`. If concurrent edit detected, fetch latest and re-apply, do not overwrite. |
| "Group by date instead of author for variety" | Author grouping is the spec. Date grouping defeats the goal of attribution. |
| "Release PR has 100+ PRs, truncate with '... and more'" | Never truncate. Long lists are fine; missing entries break audit trail. |
**Violating the letter of these rules is violating the spirit.** Release PR descriptions are an audit artifact.
## Context
- Current branch: !`git branch --show-current`
- Recent commits: !`git log --oneline -5`
## Inputs
- `$ARGUMENTS` must contain the target release PR number
- Example: `301`
## Task
Follow the steps below.
### 1. Identify the target PR
- Read the PR number from `$ARGUMENTS`
- Validate that the PR number is a positive integer
- If the PR number is not valid, stop and report the invalid input
- If no PR number is provided, stop and report the missing input
### 2. Ask whether to include a per-PR summary
Before doing any GitHub API work, ask the user exactly one question and wait for a response:
> Would you like to include a short summary of what each PR applied alongside the author? (yes / no)
- On `yes`, `y`, or equivalent affirmative: set `summary_mode = true`
- On `no`, `n`, or equivalent negative: set `summary_mode = false`
- On ambiguous response: ask once more. If still unclear, default to `summary_mode = false` and explicitly note the default in the final report.
Persist `summary_mode` in memory for the remaining steps. Do not re-prompt later.
### 3. Collect commit headlines from the release PR
Run:
```bash
gh pr view <PR_NUMBER> --json commits --jq '.commits[].messageHeadline'
```
### 4. Extract merged PR numbers
- Extract PR numbers from commit headlines using these reliable patterns:
- `Merge pull request #(\d+)` — standard merge commit format
- Trailing `(#\d+)` — squash merge format (e.g., `feat: add login (#123)`)
- Do NOT extract `#<number>` from arbitrary positions (e.g., `fix #123` is an Issue reference, `see #456` is a cross-reference — these are not merged PRs)
- After extraction, validate each number with `gh pr view <number> --json state --jq .state` to confirm it is a merged PR. Skip any that return `OPEN`, `CLOSED` (not merged), or error
- Remove duplicates
- Sort PR numbers in ascending order
- Exclude the release PR number itself if it appears
### 5. Resolve author (and summary when enabled) for each PR
Fetch author, title, summary, and MERGED state for all extracted PR numbers in one script call:
```bash
bash claude-code/skills/ywc-release-pr-list/scripts/fetch-pr-metadata.sh \
<pr-number-1> <pr-number-2> ...
# exit 0 → NDJSON on stdout (one JSON object per line); exit 2 → usage error
```
Each output line is one of:
- `{"number":123,"author":"alice","title":"feat: add login","summary":"Add login flow","skipped":false}` — merged PR; use `author` for grouping and `summary` for summary mode
- `{"number":124,"skipped":true,"reason":"not_merged: OPEN"}` — PR is not merged; skip it
- `{"number":125,"skipped":true,"reason":"fetch_error"}` — could not fetch; report in final report
The script applies the summary derivation rules (strip conventional commit prefixes, truncate at 80 chars, fall back to first non-empty body line for vague titles) regardless of `summary_mode`. In classic mode, ignore the `summary` field and use only `author`.
Build entries in this format:
- Classic mode: `- #<PR_NUMBER> @<AUTHOR>`
- Summary mode: `- #<PR_NUMBER> @<AUTHOR> — <SUMMARY>`
### 6. Group and sort the list
- Group entries by author login
- Sort author groups alphabetically
- Sort PR numbers ascending within each author group
### 7. Update the release PR description
First, save the current PR description and the new PR list to temporary files:
```bash
gh pr view <PR_NUMBER> --json body --jq '.body' > /tmp/pr_body_original.txt
```
Write the new PR LIST content (entries only, no heading) to `/tmp/pr_list_new.txt`.
Classic mode example (`summary_mode = false`):
```markdown
- #123 @alice
- #145 @alice
- #130 @bob
```
Summary mode example (`summary_mode = true`):
```markdown
- #123 @alice — Add OAuth login flow with Google provider
- #145 @alice — Fix pagination off-by-one on user list
- #130 @bob — Refactor database connection pool for reuse
```
Then run this script to produce the updated body. This script replaces ONLY the `## PR LIST` section and preserves everything else byte-for-byte:
```bash
python3 - /tmp/pr_body_original.txt /tmp/pr_list_new.txt /tmp/pr_body_updated.txt <<'PYEOF'
import sys, re
original = open(sys.argv[1], "r").read()
new_list = open(sys.argv[2], "r").read().strip()
section = f"## PR LIST\n\n{new_list}\n"
# Pattern: match from "## PR LIST" to the next "## " heading or end of string
pattern = r"(## PR LIST)\s*\n[\s\S]*?(?=\n## |\Z)"
if re.search(pattern, original):
updated = re.sub(pattern, section, original, count=1)
else:
# No existing PR LIST section — append at end
updated = original.rstrip() + "\n\n" + section + "\n"
open(sys.argv[3], "w").write(updated)
PYEOF
```
Finally, apply the update — but **re-fetch and compare first**. Between the `gh pr view` above and
this write, someone else may have edited the description; writing `/tmp/pr_body_updated.txt` blindly
would silently discard their change.
```bash
gh pr view <PR_NUMBER> --json body --jq '.body' > /tmp/pr_body_recheck.txt
if diff -q /tmp/pr_body_original.txt /tmp/pr_body_recheck.txt >/dev/null; then
gh pr edit <PR_NUMBER> --body-file /tmp/pr_body_updated.txt
else
echo "CONCURRENT EDIT DETECTED — not overwriting" >&2
fi
```
On `CONCURRENT EDIT DETECTED`, do **not** force the write. Re-apply instead:
1. Copy `/tmp/pr_body_recheck.txt` over `/tmp/pr_body_original.txt` — the other party's version is now
the base, so their edit survives.
2. Re-run the section-replacement script above against that new base. It rewrites only `## PR LIST`,
so their changes to other sections are preserved byte-for-byte.
3. Repeat the re-fetch-and-compare once more.
If the description changes again on that second attempt, **stop and report** rather than looping.
Two consecutive collisions mean something else is actively writing, and a third attempt is as likely
to lose data as to succeed.
> This is a compare-and-swap by convention, not by guarantee. `gh` cannot condition a PR-body write
> on the version it read, so a change landing between the re-fetch and the `gh pr edit` is still
> lost. The guard closes the wide window (the whole build step), not the narrow one. Do not describe
> it as atomic.
**Important**: Do NOT construct the full body string manually. Always use the Python script above to ensure only the `## PR LIST` section is modified.
## Decision Rules
- Resolve `{owner}/{repo}` dynamically: `gh repo view --json nameWithOwner --jq .nameWithOwner`
- If `gh` is unavailable or unauthenticated, stop and report the blocker.
- If no merged PR numbers can be extracted, stop before editing and report what was found in the commit headlines.
- **Preserve existing description**: Never modify, reorder, or delete any content outside the `## PR LIST` section. Maintain the original section order.
- **Update only PR LIST**: If a `## PR LIST` section already exists, replace only that section with the new PR list. Leave everything else intact.
- Do not guess authors. If a PR cannot be resolved, report it clearly instead of writing incomplete data.
- **Summary mode is opt-in**: Always ask the user once at step 2 before performing the work. Never enable `summary_mode` silently and never re-prompt mid-run.
- **Do not fabricate summaries**: A summary must be derived from the PR title or body. If neither yields a usable line, fall back to classic format for that single entry and list the affected PR numbers in the final report.
## Example Invocations
- `Use $release-pr-list 301 to update the release PR description.`
- `Use $release-pr-list 301 and keep the existing sections untouched except for PR LIST.`
- `Use $release-pr-list 301 — I'll choose whether to add per-PR summaries when prompted.`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!