feat(ce-pr-description): focused skill for PR description generation (#561)
This commit is contained in:
@@ -59,32 +59,32 @@ Ask the user: "Update the PR description for this branch?" If declined, stop.
|
||||
|
||||
### DU-2: Find the PR
|
||||
|
||||
Use the current branch and existing PR check from context. If the current branch is empty (detached HEAD), report no branch and stop. If the PR check returned `state: OPEN`, proceed to DU-3. Otherwise, report no open PR and stop.
|
||||
Use the current branch and existing PR check from context. If the current branch is empty (detached HEAD), report no branch and stop. If the PR check returned `state: OPEN`, note the PR `url` from the context block — this is the unambiguous reference to pass downstream — and proceed to DU-3. Otherwise, report no open PR and stop.
|
||||
|
||||
### DU-3: Write and apply the updated description
|
||||
|
||||
Read the current PR description:
|
||||
Read the current PR description to drive the compare-and-confirm step later:
|
||||
|
||||
```bash
|
||||
gh pr view --json body --jq '.body'
|
||||
```
|
||||
|
||||
Build the updated description:
|
||||
**Generate the updated title and body** — load the `ce-pr-description` skill with the PR URL from DU-2 (e.g., `https://github.com/owner/repo/pull/123`). The URL preserves repo/PR identity even when invoked from a worktree or subdirectory where the current repo is ambiguous. If the user provided a focus (e.g., "include the benchmarking results"), append it as free-text steering after the URL. The skill returns a `{title, body}` block without applying or prompting.
|
||||
|
||||
1. **Get the full branch diff** -- follow "Detect the base branch and remote" and "Gather the branch scope" in Step 6. Use the PR found in DU-2 for base branch detection.
|
||||
2. **Classify commits** -- follow "Classify commits before writing" in Step 6.
|
||||
3. **Decide on evidence** -- if the current description already has a `## Demo` or `## Screenshots` section with image embeds, preserve it unless the user's focus asks to refresh or remove it. If no evidence exists, follow "Evidence for PR descriptions" in Step 6.
|
||||
4. **Rewrite the description from scratch** -- follow the writing principles in Step 6, driven by feature commits, final diff, and evidence decision. Do not layer changes onto the old description or document what changed since the last version. Write as if describing the PR for the first time.
|
||||
- If the user provided a focus, incorporate it alongside the branch diff context.
|
||||
5. **Compare and confirm** -- briefly explain what the new description covers differently from the old one. This helps the user decide whether to apply; the description itself does not narrate these differences.
|
||||
- If the user provided a focus, confirm it was addressed.
|
||||
- Ask the user to confirm before applying.
|
||||
If `ce-pr-description` returns a "not open" or other graceful-exit message instead of a `{title, body}` pair, report that message and stop.
|
||||
|
||||
If confirmed, apply:
|
||||
**Evidence decision:** `ce-pr-description` preserves any existing `## Demo` or `## Screenshots` block from the current body by default. If the user's focus asks to refresh or remove evidence, pass that intent as steering text — the skill will honor it. If no evidence block exists and one would benefit the reader, invoke `ce-demo-reel` separately to capture, then re-invoke `ce-pr-description` with updated steering that references the captured evidence.
|
||||
|
||||
**Compare and confirm** — briefly explain what the new description covers differently from the old one. This helps the user decide whether to apply; the description itself does not narrate these differences.
|
||||
|
||||
- If the user provided a focus, confirm it was addressed.
|
||||
- Ask the user to confirm before applying.
|
||||
|
||||
If confirmed, apply with the returned title and body:
|
||||
|
||||
```bash
|
||||
gh pr edit --body "$(cat <<'EOF'
|
||||
Updated description here
|
||||
gh pr edit --title "<returned title>" --body "$(cat <<'EOF'
|
||||
<returned body>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
@@ -137,7 +137,7 @@ Priority order for commit messages and PR titles:
|
||||
|
||||
Use the current branch and existing PR check from context. If the branch is empty, report detached HEAD and stop.
|
||||
|
||||
If the PR check returned `state: OPEN`, note the URL -- continue to Step 4 and 5, then skip to Step 7 (existing PR flow). Otherwise, continue through Step 8.
|
||||
If the PR check returned `state: OPEN`, note the URL -- this is the existing-PR flow. Continue to Step 4 and 5 (commit any pending work and push), then go to Step 7 to ask whether to rewrite the description. Only run Step 6 (which generates a new description via `ce-pr-description`) if the user confirms the rewrite; Step 7's existing-PR sub-path consumes the `{title, body}` that Step 6 produces. Otherwise (no open PR), continue through Steps 6, 7, and 8 in order.
|
||||
|
||||
### Step 4: Branch, stage, and commit
|
||||
|
||||
@@ -157,13 +157,11 @@ If the PR check returned `state: OPEN`, note the URL -- continue to Step 4 and 5
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
### Step 6: Write the PR description
|
||||
### Step 6: Generate the PR title and body
|
||||
|
||||
The working-tree diff from Step 1 only shows uncommitted changes at invocation time. The PR description must cover **all commits** in the PR.
|
||||
|
||||
#### Detect the base branch and remote
|
||||
|
||||
Resolve both the base branch and the remote (fork-based PRs may use a remote other than `origin`). Stop at the first that succeeds:
|
||||
**Detect the base branch and remote.** Resolve both the base branch and the remote (fork-based PRs may use a remote other than `origin`). Stop at the first that succeeds:
|
||||
|
||||
1. **PR metadata** (if existing PR found in Step 3):
|
||||
```bash
|
||||
@@ -184,193 +182,62 @@ Resolve both the base branch and the remote (fork-based PRs may use a remote oth
|
||||
|
||||
If none resolve, ask the user to specify the target branch.
|
||||
|
||||
#### Gather the branch scope
|
||||
|
||||
Verify the remote-tracking ref exists and fetch if needed:
|
||||
**Gather the full branch diff (before evidence decision).** The working-tree diff from Step 1 only reflects uncommitted changes at invocation time — on the common "feature branch, all pushed, open PR" path, Step 1 skips the commit/push steps and the working-tree diff is empty. The evidence decision below needs the real branch diff to judge whether behavior is observable, so compute it explicitly against the base resolved above. Only fetch when the local ref isn't available — if `<base-remote>/<base-branch>` already resolves locally, run the diff from local state so offline / restricted-network / expired-auth environments don't hard-fail:
|
||||
|
||||
```bash
|
||||
git rev-parse --verify <base-remote>/<base-branch> 2>/dev/null || git fetch --no-tags <base-remote> <base-branch>
|
||||
git rev-parse --verify <base-remote>/<base-branch> >/dev/null 2>&1 \
|
||||
|| git fetch --no-tags <base-remote> <base-branch>
|
||||
git diff <base-remote>/<base-branch>...HEAD
|
||||
```
|
||||
|
||||
Gather merge base, commit list, and full diff in a single call:
|
||||
Use this branch diff (not the working-tree diff) for the evidence decision. If the branch diff is empty (e.g., HEAD is already merged into the base or the branch has no unique commits), skip the evidence prompt and continue to delegation.
|
||||
|
||||
```bash
|
||||
MERGE_BASE=$(git merge-base <base-remote>/<base-branch> HEAD) && echo "MERGE_BASE=$MERGE_BASE" && echo '=== COMMITS ===' && git log --oneline $MERGE_BASE..HEAD && echo '=== DIFF ===' && git diff $MERGE_BASE...HEAD
|
||||
```
|
||||
**Evidence decision (before delegation).** If the branch diff changes observable behavior (UI, CLI output, API behavior with runnable code, generated artifacts, workflow output) and evidence is not otherwise blocked (unavailable credentials, paid services, deploy-only infrastructure, hardware), ask: "This PR has observable behavior. Capture evidence for the PR description?"
|
||||
|
||||
Use the full branch diff and commit list as the basis for the description.
|
||||
- **Capture now** -- load the `ce-demo-reel` skill with a target description inferred from the branch diff. ce-demo-reel returns `Tier`, `Description`, and `URL`. Note the captured evidence so it can be passed as free-text steering to `ce-pr-description` (e.g., "include the captured demo: <URL> as a `## Demo` section") or spliced into the returned body before apply. If capture returns `Tier: skipped` or `URL: "none"`, proceed with no evidence.
|
||||
- **Use existing evidence** -- ask for the URL or markdown embed, then pass it as free-text steering to `ce-pr-description` or splice in before apply.
|
||||
- **Skip** -- proceed with no evidence section.
|
||||
|
||||
#### Evidence for PR descriptions
|
||||
When evidence is not possible (docs-only, markdown-only, changelog-only, release metadata, CI/config-only, test-only, or pure internal refactors), skip without asking.
|
||||
|
||||
Decide whether evidence capture is possible from the full branch diff.
|
||||
**Delegate title and body generation to `ce-pr-description`.** Load the `ce-pr-description` skill:
|
||||
|
||||
**Evidence is possible** when the diff changes observable behavior demonstrable from the workspace: UI, CLI output, API behavior with runnable code, generated artifacts, or workflow output.
|
||||
- **For a new PR** (no existing PR found in Step 3): invoke with `base:<base-remote>/<base-branch>` using the already-resolved base from earlier in this step, so `ce-pr-description` describes the correct commit range even when the branch targets a non-default base (e.g., `develop`, `release/*`). Append any captured-evidence context or user focus as free-text steering (e.g., "include the captured demo: <URL> as a `## Demo` section").
|
||||
- **For an existing PR** (found in Step 3): invoke with the full PR URL from the Step 3 context (e.g., `https://github.com/owner/repo/pull/123`). The URL preserves repo/PR identity even when invoked from a worktree or subdirectory; the skill reads the PR's own `baseRefName` so no `base:` override is needed. Append any focus steering as free text after the URL.
|
||||
|
||||
**Evidence is not possible** for:
|
||||
- Docs-only, markdown-only, changelog-only, release metadata, CI/config-only, test-only, or pure internal refactors
|
||||
- Behavior requiring unavailable credentials, paid/cloud services, bot tokens, deploy-only infrastructure, or hardware not provided
|
||||
`ce-pr-description` returns a `{title, body}` block. It applies the value-first writing principles, commit classification, sizing, narrative framing, writing voice, visual communication, numbering rules, and the Compound Engineering badge footer internally. Use the returned values verbatim in Step 7; do not layer manual edits onto them unless a focused adjustment is required (e.g., splicing an evidence block captured in this step that was not passed as steering text).
|
||||
|
||||
When not possible, skip without asking. When possible, ask: "This PR has observable behavior. Capture evidence for the PR description?"
|
||||
|
||||
1. **Capture now** -- load the `ce-demo-reel` skill with a target description inferred from the branch diff. ce-demo-reel returns `Tier`, `Description`, and `URL`. Build a `## Demo` or `## Screenshots` section (browser-reel/terminal-recording/screenshot-reel use "Demo", static uses "Screenshots").
|
||||
2. **Use existing evidence** -- ask for the URL or markdown embed.
|
||||
3. **Skip** -- no evidence section.
|
||||
|
||||
If capture returns `Tier: skipped` or `URL: "none"`, do not add a placeholder. Summarize in the final report.
|
||||
|
||||
Place evidence before the Compound Engineering badge. Do not label test output as "Demo" or "Screenshots".
|
||||
|
||||
#### Classify commits before writing
|
||||
|
||||
Scan the commit list and classify each commit:
|
||||
|
||||
- **Feature commits** -- implement the PR's purpose (new functionality, intentional refactors, design changes). These drive the description.
|
||||
- **Fix-up commits** -- iteration work (code review fixes, lint fixes, test fixes, rebase resolutions, style cleanups). Invisible to the reader.
|
||||
|
||||
When sizing the description, mentally subtract fix-up commits: a branch with 12 commits but 9 fix-ups is a 3-commit PR.
|
||||
|
||||
#### Frame the narrative before sizing
|
||||
|
||||
Articulate the PR's narrative frame:
|
||||
|
||||
1. **Before**: What was broken, limited, or impossible? (One sentence.)
|
||||
2. **After**: What's now possible or improved? (One sentence.)
|
||||
3. **Scope rationale** (only if 2+ separable-looking concerns): Why do these ship together? (One sentence.)
|
||||
|
||||
This frame becomes the opening. For small+simple PRs, the "after" sentence alone may be the entire description.
|
||||
|
||||
#### Sizing the change
|
||||
|
||||
Assess size (files, diff volume) and complexity (design decisions, trade-offs, cross-cutting concerns) to select description depth:
|
||||
|
||||
| Change profile | Description approach |
|
||||
|---|---|
|
||||
| Small + simple (typo, config, dep bump) | 1-2 sentences, no headers. Under ~300 characters. |
|
||||
| Small + non-trivial (bugfix, behavioral change) | Short narrative, ~3-5 sentences. No headers unless two distinct concerns. |
|
||||
| Medium feature or refactor | Narrative frame (before/after/scope), then what changed and why. Call out design decisions. |
|
||||
| Large or architecturally significant | Full narrative: problem context, approach (and why), key decisions, migration/rollback if relevant. |
|
||||
| Performance improvement | Include before/after measurements if available. Markdown table works well. |
|
||||
|
||||
When in doubt, shorter is better. Match description weight to change weight.
|
||||
|
||||
#### Writing voice
|
||||
|
||||
If the user has documented style preferences, follow those. Otherwise:
|
||||
|
||||
- Active voice. No em dashes or `--` substitutes; use periods, commas, colons, or parentheses.
|
||||
- Vary sentence length. Never three similar-length sentences in a row.
|
||||
- Do not make a claim and immediately explain it. Trust the reader.
|
||||
- Plain English. Technical jargon fine; business jargon never.
|
||||
- No filler: "it's worth noting", "importantly", "essentially", "in order to", "leverage", "utilize."
|
||||
- Digits for numbers ("3 files"), not words ("three files").
|
||||
|
||||
#### Writing principles
|
||||
|
||||
- **Lead with value**: Open with what's now possible or fixed, not what was moved around. The subtler failure is leading with the mechanism ("Replace the hardcoded capture block with a tiered skill") instead of the outcome ("Evidence capture now works for CLI tools and libraries, not just web apps").
|
||||
- **No orphaned opening paragraphs**: If the description uses `##` headings anywhere, the opening must also be under a heading (e.g., `## Summary`). For short descriptions with no sections, a bare paragraph is fine.
|
||||
- **Describe the net result, not the journey**: The description covers the end state, not how you got there. No iteration history, debugging steps, intermediate failures, or bugs found and fixed during development. This applies equally to description updates: rewrite from the current state, not as a log of what changed since the last version. Exception: process details critical to understand a design choice.
|
||||
- **When commits conflict, trust the final diff**: The commit list is supporting context, not the source of truth. If commits describe intermediate steps later revised or reverted, describe the end state from the full branch diff.
|
||||
- **Explain the non-obvious**: If the diff is self-explanatory, don't narrate it. Spend space on things the diff doesn't show: why this approach, what was rejected, what the reviewer should watch.
|
||||
- **Use structure when it earns its keep**: Headers, bullets, and tables aid comprehension, not mandatory template sections.
|
||||
- **Markdown tables for data**: Before/after comparisons, performance numbers, or option trade-offs communicate well as tables.
|
||||
- **No empty sections**: If a section doesn't apply, omit it. No "N/A" or "None."
|
||||
- **Test plan -- only when non-obvious**: Include when testing requires edge cases the reviewer wouldn't think of, hard-to-verify behavior, or specific setup. Omit when "run the tests" is the only useful guidance. When the branch adds test files, name them with what they cover.
|
||||
|
||||
#### Visual communication
|
||||
|
||||
Include a visual aid only when the change is structurally complex enough that a reviewer would struggle to reconstruct the mental model from prose alone.
|
||||
|
||||
**When to include:**
|
||||
|
||||
| PR changes... | Visual aid |
|
||||
|---|---|
|
||||
| Architecture touching 3+ interacting components | Mermaid component or interaction diagram |
|
||||
| Multi-step workflow or data flow with non-obvious sequencing | Mermaid flow diagram |
|
||||
| 3+ behavioral modes, states, or variants | Markdown comparison table |
|
||||
| Before/after performance or behavioral data | Markdown table |
|
||||
| Data model changes with 3+ related entities | Mermaid ERD |
|
||||
|
||||
**When to skip:**
|
||||
- Sizing routes to "1-2 sentences"
|
||||
- Prose already communicates clearly
|
||||
- The diagram would just restate the diff visually
|
||||
- Mechanical changes (renames, dep bumps, config, formatting)
|
||||
|
||||
**Format:**
|
||||
- **Mermaid** (default) for flows, interactions, dependencies. 5-10 nodes typical, up to 15 for genuinely complex changes. Use `TB` direction. Source should be readable as fallback.
|
||||
- **ASCII diagrams** for annotated flows needing rich in-box content. 80-column max.
|
||||
- **Markdown tables** for comparisons and decision matrices.
|
||||
- Place inline at point of relevance, not in a separate section.
|
||||
- Prose is authoritative when it conflicts with a visual.
|
||||
|
||||
Verify generated diagrams against the change before including.
|
||||
|
||||
#### Numbering and references
|
||||
|
||||
Never prefix list items with `#` in PR descriptions -- GitHub interprets `#1`, `#2` as issue references and auto-links them.
|
||||
|
||||
When referencing actual GitHub issues or PRs, use `org/repo#123` or the full URL. Never use bare `#123` unless verified.
|
||||
|
||||
#### Compound Engineering badge
|
||||
|
||||
Append a badge footer separated by a `---` rule. Skip if a badge already exists.
|
||||
|
||||
**Badge:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
|
||||
[](https://github.com/EveryInc/compound-engineering-plugin)
|
||||

|
||||
```
|
||||
|
||||
**Harness lookup:**
|
||||
|
||||
| Harness | `LOGO` | `COLOR` |
|
||||
|---------|--------|---------|
|
||||
| Claude Code | `claude` | `D97757` |
|
||||
| Codex | (omit logo param) | `000000` |
|
||||
| Gemini CLI | `googlegemini` | `4285F4` |
|
||||
|
||||
**Model slug:** Replace spaces with underscores. Append context window and thinking level in parentheses if known. Examples: `Opus_4.6_(1M,_Extended_Thinking)`, `Sonnet_4.6_(200K)`, `Gemini_3.1_Pro`.
|
||||
If `ce-pr-description` returns a graceful-exit message instead of `{title, body}` (e.g., closed PR, no commits to describe, base ref unresolved), report the message and stop — do not create or edit the PR.
|
||||
|
||||
### Step 7: Create or update the PR
|
||||
|
||||
#### New PR (no existing PR from Step 3)
|
||||
|
||||
Using the `{title, body}` returned by `ce-pr-description`:
|
||||
|
||||
```bash
|
||||
gh pr create --title "the pr title" --body "$(cat <<'EOF'
|
||||
PR description here
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/EveryInc/compound-engineering-plugin)
|
||||
-D97757?logo=claude&logoColor=white)
|
||||
gh pr create --title "<returned title>" --body "$(cat <<'EOF'
|
||||
<returned body>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
Use the badge from the Compound Engineering badge section. Replace harness and model values from the lookup tables. Keep the PR title under 72 characters, following Step 2 conventions.
|
||||
Keep the title under 72 characters; `ce-pr-description` already emits a conventional-commit title in that range.
|
||||
|
||||
#### Existing PR (found in Step 3)
|
||||
|
||||
The new commits are already on the PR from Step 5. Report the PR URL, then ask whether to rewrite the description.
|
||||
|
||||
- If **yes**:
|
||||
1. Classify commits -- new commits since last push are often fix-up work and should not appear as distinct items
|
||||
2. Size the full PR (not just new commits) using the sizing table
|
||||
3. **Rewrite from scratch** to describe the PR's net result as of now, following Step 6's writing principles. Do not append, amend, or layer onto the old description. The description is not a changelog.
|
||||
4. Include the Compound Engineering badge unless already present
|
||||
5. Apply:
|
||||
- If **yes**, run Step 6 now to generate `{title, body}` via `ce-pr-description` (passing the existing PR URL as `pr:`), then apply the returned title and body:
|
||||
|
||||
```bash
|
||||
gh pr edit --body "$(cat <<'EOF'
|
||||
Updated description here
|
||||
gh pr edit --title "<returned title>" --body "$(cat <<'EOF'
|
||||
<returned body>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
- If **no** -- done.
|
||||
- If **no** -- skip Step 6 entirely and finish. Do not run delegation or evidence capture when the user declined the rewrite.
|
||||
|
||||
### Step 8: Report
|
||||
|
||||
|
||||
Reference in New Issue
Block a user