From DevFlow
Generates or updates a structured PR description by analyzing branch changes against the base branch. Merges new content with human-added content when an existing PR is found.
How this skill is triggered — by the user, by Claude, or both
Slash command
/devflow:pr-description <issue-number><issue-number>The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate a structured PR description by analyzing the current branch's changes against the base branch. When an existing PR is found, merges new content with human-added content instead of replacing it.
Generate a structured PR description by analyzing the current branch's changes against the base branch. When an existing PR is found, merges new content with human-added content instead of replacing it.
Input: $ARGUMENTS is an optional GitHub issue number. If provided, include a "Resolves #N" link.
Portable helper anchor (single-statement). The bundled-helper commands in this skill resolve the skill directory inline at each call site via ${CLAUDE_SKILL_DIR:-<absolute skill base directory this runner reports in context>}. When $CLAUDE_SKILL_DIR is set and non-empty (Claude Code), run each command exactly as written. On a runner where it is unset or empty, replace the placeholder with the skill base directory the runner reports in context (e.g. a Base directory for this skill: line) before running the command; if that reported path is Windows-form (C:\...), first convert it to this shell's POSIX form with one standalone wslpath -u '<path>' (WSL) or cygpath -u '<path>' (Git Bash/MSYS2) command and substitute the printed result only if the command succeeds and prints a non-empty path — otherwise fall through to the drive-letter rules exactly as if the tool were absent, the same success-and-non-empty acceptance the platform's path-normalization rules apply (if neither tool exists: lowercase the drive letter, map C:\ to /mnt/c on WSL or /c on MSYS2, and turn backslashes into /; if the environment is neither WSL nor MSYS2, use the path unchanged and report that it could not be normalized — the same arm the platform's path-normalization rules take). Resolve the anchor inline at every call site — never capture it into a shell variable that a later statement reads, because some runners' inline-bash marshaling drops such variables (observed on Copilot CLI). If neither $CLAUDE_SKILL_DIR nor a runner-reported base directory is available, stop and report that the helper anchor could not be resolved rather than running a command with a broken path.
Consumer prompt extension (load first). Before doing this skill's work, load any consumer-supplied prompt extension for this skill and honor it. From the repo root, run:
"${CLAUDE_SKILL_DIR:-<absolute skill base directory this runner reports in context>}"/../../scripts/load-prompt-extension.sh pr-description
If the invocation fails because the helper path does not exist (No such file, exit 127, or the platform equivalent), that is the anchor-resolution failure described in the Portable helper anchor note above — fix the anchor, don't report a missing extension. Otherwise, if the helper exits non-zero, a consumer extension exists but could not be loaded — surface its stderr message and do not silently proceed as if none existed. If it exits 0 and prints text, treat that text as additional instructions appended to the end of this skill's own prompt for this run — it is upgrade-safe, consumer-owned customization committed under .devflow/prompt-extensions/. If it exits 0 and prints nothing, proceed unchanged.
Run these commands to understand what changed:
git fetch origin main
git log origin/main...HEAD --oneline
git diff origin/main...HEAD --stat
Read the diff details for any files that need deeper understanding:
git diff origin/main...HEAD
If an issue number was provided, fetch the issue for context:
gh issue view $ARGUMENTS --json title,body,labels
Check for an existing PR on the current branch:
gh pr view HEAD --json number,body,title 2>/dev/null
If this succeeds, an existing PR was found. Save the PR number and body for Step 2.
Best-effort: pull post-merge acceptance criteria from the /devflow:implement workpad. When /devflow:implement parses a related issue's Acceptance Criteria (its Phase 1.4), it tags items that can only be verified after merge with a trailing (post-merge) marker on the checkbox line. Surface those items in the PR body so the merger sees them and can tick them off after deploy.
If an issue number is available (from $ARGUMENTS or extracted from the existing PR body via (?i)(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+) — mirroring GitHub's own case-insensitive closes-keyword detection), look up the workpad and read its body:
ISSUE_NUMBER=$ARGUMENTS # or the extracted number
WORKPAD_ID=$("${CLAUDE_SKILL_DIR:-<absolute skill base directory this runner reports in context>}"/../../scripts/workpad.py id "$ISSUE_NUMBER" 2>/dev/null || true)
if [ -n "$WORKPAD_ID" ]; then
WORKPAD_BODY=$("${CLAUDE_SKILL_DIR:-<absolute skill base directory this runner reports in context>}"/../../scripts/workpad.py body "$WORKPAD_ID" 2>/dev/null || true)
fi
If WORKPAD_BODY is set, scan its ## Acceptance Criteria section for lines matching ^[-*]\s+\[[ x]\]\s+.*\(post-merge\)\s*$. Strip the leading checkbox and the trailing (post-merge) tag from each match; collect them as POST_MERGE_ITEMS for Step 2's template.
If no workpad exists, no issue number is available, or no (post-merge)-tagged items are found, POST_MERGE_ITEMS stays empty and the template's Post-Merge Verification section is omitted entirely. The lookup is best-effort — never fail the run on a missing workpad.
Best-effort: pull deferred review findings from the manifest. /devflow:review-and-fix now writes each run's manifest run-scoped (.devflow/tmp/review/<slug>/<run-id>/deferrals.json), and /devflow:implement Phase 4.0.5 merges every run-scoped manifest into one slug-level aggregate at .devflow/tmp/review/pr-<N>/deferrals.json, then files follow-up issues and updates that aggregate in place with id and follow_up fields per entry. Read the slug-level aggregate (the single hydrated path Phase 4.0.5 produces) and surface its entries in the PR body as a Scope-Acknowledged Findings block so /devflow:review (run later as a formal merge signal) can match them and demote the corresponding findings to Informational. (Ordinary entries render only with a populated follow_up.issue — and only Phase 4.0.5 populates that, so the aggregate is the authoritative source; the run-scoped per-run files are raw, un-hydrated inputs to the merge. A settled-by-disclosure foreclosure entry (issue #621) is the exception: it renders WITHOUT a follow_up.issue — the shipped disclosure is its deliverable, no follow-up issue is ever filed, and the PR-body block is the foreclosure's sole durable record.)
PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null || true)
if [ -n "$PR_NUMBER" ]; then
DEFERRALS_FILE=".devflow/tmp/review/pr-${PR_NUMBER}/deferrals.json" # slug-level aggregate written by /devflow:implement Phase 4.0.5
if [ -s "$DEFERRALS_FILE" ]; then
DEFERRALS_BODY=$(cat "$DEFERRALS_FILE")
fi
fi
If DEFERRALS_BODY is set and the parsed JSON has at least one renderable entry under deferrals[], render the Deferred Findings section in Step 2's template — a human-readable Markdown table (one row per deferral) for readers, plus the exact machine payload (the same schema_version: 1 / deferrals[] YAML shape) inside the hidden DEVFLOW_DEFERRED_PAYLOAD HTML comment that scripts/match-deferrals.py parses. An entry is renderable when it has a populated follow_up.issue OR its reason.category (equivalently the aggregate's flat category) is settled-by-disclosure (a foreclosure, which never has a follow_up.issue). An ordinary entry (one of the three non-foreclosure categories) lacking a follow_up.issue is a stale half-written manifest — skip it silently.
Carry-forward safety (issue #621 — the foreclosure block is a sole durable record). Because a settled-by-disclosure foreclosure files no follow-up issue, the PR-body Scope-Acknowledged block is its only durable record — a regeneration from a fresh environment (where .devflow/tmp/review/… is empty) must therefore never silently wipe it. So:
DEFERRALS_BODY empty/invalid), do not omit the section: read the existing PR body's DEVFLOW_DEFERRED_PAYLOAD block and preserve it verbatim (re-emit its entries unchanged). Only when neither an aggregate nor an existing block exists is the section omitted.DEVFLOW_DEFERRED_PAYLOAD but absent from the aggregate (matched by its dfr- id) so a foreclosure recorded on an earlier pass is never dropped by a later regeneration that no longer sees its manifest.The lookup is best-effort — never fail the run on a missing or unparseable manifest, and never wipe an existing foreclosure block on one.
Generate a fresh description using the template below.
Fetch the existing body and apply these merge rules:
Re-generate from the diff (always overwrite — these reflect current state):
POST_MERGE_ITEMS is non-empty — re-derived from the workpad on every run so the list stays in sync with the latest /devflow:implement parse)follow_up.issue, or a settled-by-disclosure foreclosure — re-derived from the manifest on every run so the block stays in sync with the latest /devflow:implement Phase 4.0.5 filing; carry-forward-safe: a regeneration with no manifest present preserves the existing block's foreclosure entries verbatim rather than wiping them, per the carry-forward rule in Step 1)Merge (keep existing items that are still relevant, add new ones, remove stale ones):
Merge (combine existing and new):
$ARGUMENTS provides an issue number, include it; also keep any existing issue references that differ from $ARGUMENTSPreserve as-is:
<!-- PR_BODY_START --> or AFTER <!-- PR_BODY_END --> in the existing body — output it in the same position relative to the markersIf the existing body has NO markers: Treat the entire existing body as pre-marker content. Output it above <!-- PR_BODY_START -->, then generate the full template below the marker.
Output the description as plain text (not inside a code block) so it appears directly in your response:
[Group changes by module or concern. Use bold for the area name, colon, then a brief description.]
[Area name]: [What changed] [Area name]: [What changed]
Resolves #[issue number, or omit this section if no issue number was provided]
[Omit this entire section when POST_MERGE_ITEMS is empty. When non-empty, render as:] The following items can only be verified after this PR is merged or deployed. Tick each after performing the check.
[Omit this entire section only when there are NO renderable entries AND no existing PR-body block to carry forward (issue #621). An entry is renderable when it has a populated follow_up.issue, OR its reason.category is settled-by-disclosure (a foreclosure — no follow_up.issue). When rendering, render with the markers — the /devflow:review verdict matcher parses them exactly. The visible content is a human-readable Markdown table; the exact machine payload lives in a hidden HTML comment (DEVFLOW_DEFERRED_PAYLOAD) so it does not appear in the rendered PR body. One table row per deferral; one payload entry per deferral, in the same order. A foreclosure row cites the disclosure path in the Follow-up column (e.g. disclosure: docs/…) instead of a #<N> issue reference. Carry forward any existing-body foreclosure entry absent from the aggregate, per Step 1's carry-forward rule:]
These review-agent findings were deferred under the Scope-Acknowledged Findings contract. /devflow:review honors matching entries as Informational; closing a linked follow-up issue invalidates the deferral and forces re-verification. A settled-by-disclosure foreclosure has no follow-up issue — the cited disclosure is its deliverable, and a review re-verifies the disclosure phrase against the tree.
| Severity | File | Finding | Follow-up |
|---|---|---|---|
<path>:<start>-<end> | # | ||
<path>:<start>-<end> | disclosure: <disclosure path> |
[Describe UI changes, or "N/A" if none]
[Describe breaking changes and migration steps, or "None"]
Rules:
# immediately before a number unless it is a real issue or PR reference — GitHub renders #2 as a link to issue/PR 2, which misleads readers. For an ordinal, count, or list position, spell it out ("item 2", "step 3"), never #2. Genuine references like #123 stay as-is.gh pr edit --body resolves the repo through org-scoped GraphQL and fails under a repo-scoped token). The -F body=@- form reads the field value literally from stdin, so the quoted-heredoc's no-expansion guarantee (backticks and $ preserved) holds exactly as gh pr edit --body did:
gh api --method PATCH "repos/{owner}/{repo}/pulls/$PR_NUMBER" -F body=@- <<'EOF'
[full output including any pre/post-marker content]
EOF
npx claudepluginhub the01geek/devflow-autopilot --plugin devflowGenerates pull request descriptions from branch changes using the gh CLI. Discovers repo PR templates, validates branch state, and drafts PR bodies.
Generate a comprehensive pull request description following the repository's standard template. Use when the user wants to create or update a PR description.
Drafts conventional commit PR titles and Shopware 5-section descriptions for core PRs targeting trunk. Analyzes branch against trunk, uses session context, asks for missing info.