Generate/Update PR Description
Generates or updates PR description with incremental information, auto-updates title, and links Linear tickets.
Prerequisites
# Check project setup (thoughts, CLAUDE.md snippet, config)
if [[ -f "${CLAUDE_PLUGIN_ROOT}/scripts/check-project-setup.sh" ]]; then
"${CLAUDE_PLUGIN_ROOT}/scripts/check-project-setup.sh" || exit 1
fi
Process:
1. Read PR description template
# Check if template exists
if [ ! -f "thoughts/shared/pr_description.md" ]; then
echo "❌ PR description template not found"
fi
If missing:
❌ PR description template missing
Your humanlayer thoughts setup is incomplete. Create a template at:
thoughts/shared/pr_description.md
See the PR description template you created earlier for reference.
Read template fully to understand all sections.
2. Identify target PR
If argument provided:
- Use that PR number:
/describe_pr 123
If no argument:
# Try current branch
gh pr view --json number,url,title,state,body,headRefName,baseRefName 2>/dev/null
If no PR on current branch OR on main/master:
# List recent PRs
gh pr list --limit 10 --json number,title,headRefName,state
Ask user: "Which PR would you like to describe? (enter number)"
3. Extract ticket reference
From multiple sources:
# 1. From branch name
branch=$(gh pr view $pr_number --json headRefName -q .headRefName)
if [[ "$branch" =~ ([A-Z]+)-([0-9]+) ]]; then
ticket="${BASH_REMATCH[0]}"
fi
# 2. From PR title
title=$(gh pr view $pr_number --json title -q .title)
if [[ "$title" =~ ([A-Z]+)-([0-9]+) ]]; then
ticket="${BASH_REMATCH[0]}"
fi
# 3. From existing PR body
body=$(gh pr view $pr_number --json body -q .body)
if [[ "$body" =~ Refs:\ ([A-Z]+-[0-9]+) ]]; then
ticket="${BASH_REMATCH[1]}"
fi
4. Read existing descriptions
Read current PR body from GitHub:
current_body=$(gh pr view $pr_number --json body -q .body)
Read saved description (if exists):
saved_desc="thoughts/shared/prs/${pr_number}_description.md"
if [ -f "$saved_desc" ]; then
# Read fully
# Note what sections exist vs what's new
fi
Check for metadata header:
<!-- Auto-generated: 2025-10-06T10:30:00Z -->
<!-- Last updated: 2025-10-06T14:45:00Z -->
<!-- PR: #123 -->
<!-- Previous commits: abc123,def456 -->
5. Gather comprehensive PR information
# Full diff
gh pr diff $pr_number
# Commit history with messages
gh pr view $pr_number --json commits
# Changed files
gh pr view $pr_number --json files
# PR metadata
gh pr view $pr_number --json url,title,number,state,baseRefName,headRefName,author
# CI/CD status
gh pr checks $pr_number
6. Analyze changes incrementally
If this is an UPDATE (saved description exists):
# Extract previous commit list from metadata
prev_commits=$(grep "Previous commits:" $saved_desc | sed 's/.*: //')
# Get current commits
current_commits=$(gh pr view $pr_number --json commits -q '.commits[].oid' | tr '\n' ',' | sed 's/,$//')
# Compare
new_commits=$(comm -13 <(echo "$prev_commits" | tr ',' '\n' | sort) <(echo "$current_commits" | tr ',' '\n' | sort))
Analysis:
- Identify what's NEW since last description
- Deep analysis of:
- Code changes and architectural impact
- Breaking changes
- User-facing vs internal changes
- Migration requirements
- Security implications
7. Merge descriptions intelligently
Auto-generated sections (always update):
- Summary - regenerate based on ALL changes
- Changes Made - append new changes, preserve old
- How to Verify It - update checklist, rerun checks
- Changelog Entry - update to reflect all changes
Preserve manual edits in:
- Reviewer Notes - keep existing unless explicitly empty
- Screenshots/Videos - never overwrite
- Manually checked boxes - preserve [x] marks for manual steps
- Post-Merge Tasks - append new, keep existing
Merging strategy:
## Changes Made
### Backend Changes
[Existing changes from previous description]
**New changes** (since last update):
- [New change 1]
- [New change 2]
### Frontend Changes
[Existing + new merged together]
Add change summary at top:
<!-- Auto-generated: 2025-10-06T15:00:00Z -->
<!-- Last updated: 2025-10-06T15:00:00Z -->
<!-- PR: #123 -->
<!-- Previous commits: abc123,def456,ghi789 -->
---
**Update History:**
- 2025-10-06 15:00: Added validation logic, updated tests (3 new commits)
- 2025-10-06 10:30: Initial implementation (5 commits)
---
8. Add Linear reference
If ticket found:
## Related Issues/PRs
- Fixes https://linear.app/{workspace}/issue/{ticket}
- Related to #NNN (reference sibling work by its **GitHub PR number**)
CTL-623 — sibling reference format (REQUIRED): When referencing related/sibling
work in prose, reference it by its GitHub PR number (#NNN), never by a bare
Linear token (TEAM-NNN) or a Linear issue URL. A bare sibling TEAM-NNN token in the
body is auto-linked by Linear's GitHub integration and drags that sibling's workflow
status (Done → Implement) on PR open/merge. Do not emit bare sibling Linear tokens
in prose. The own ticket's Fixes https://linear.app/... line is correct and stays —
that link/transition is intended. Sibling neutralization is handled mechanically by the
guard block appended at write-back time (see below).
Get Linear ticket details via direct SQL against the replica (see the linearis skill's
"Reading Linear" section). Extract title and description with jq. Use ticket title and
description for context.
9. Generate updated title
Title generation rules:
# If ticket exists, read its title via direct SQL against the replica
# (linear_read_ticket — replica-first, loud linearis fallback; CTL-1397).
source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/linear-read-replica.sh"
if [[ "$ticket" ]]; then
ticket_title=$(linear_read_ticket "$ticket" 2>/dev/null | jq -r '.title // empty')
if [[ -n "$ticket_title" ]]; then
title="$ticket: ${ticket_title:0:60}"
else
# Fallback: generate title from branch name + commits
title="$ticket: $(echo "$branch" | sed "s/^.*$ticket-//" | tr '-' ' ')"
fi
else
# No ticket: generate from primary change
title="Brief summary of main change"
fi
Auto-update without prompt - title is auto-generated section.
10. Run verification checks
For each checklist item in "How to Verify It":
# Example: "- [ ] Build passes: `make build`"
# Extract command: make build
# Try to run
if command -v make >/dev/null 2>&1; then
if make build 2>&1; then
# Mark as checked
checkbox="- [x] Build passes: \`make build\` ✅"
else
# Mark unchecked with error
checkbox="- [ ] Build passes: \`make build\` ❌ (failed: $error)"
fi
else
# Can't run
checkbox="- [ ] Build passes: \`make build\` (manual verification required)"
fi
Common checks to attempt:
make test/npm test/pytestmake lint/npm run lintnpm run typecheck/tsc --noEmitmake build/npm run build
Document results:
- ✅ if passed
- ❌ if failed (with error)
- Manual required if can't automate
11. Save and sync
IMPORTANT: Document Storage Rules
- ALWAYS write to
thoughts/shared/prs/for PR descriptions - NEVER write to
thoughts/searchable/— this is a read-only search index
Save description:
# Add metadata header
cat > "thoughts/shared/prs/${pr_number}_description.md" <<EOF
<!-- Auto-generated: $(date -u +%Y-%m-%dT%H:%M:%SZ) -->
<!-- Last updated: $(date -u +%Y-%m-%dT%H:%M:%SZ) -->
<!-- PR: #$pr_number -->
<!-- Previous commits: $commit_list -->
[Full description content]
EOF
Sync thoughts:
humanlayer thoughts sync
12. Update PR on GitHub
CRITICAL: NO CLAUDE ATTRIBUTION
Befo