plugins/jira/skills/extract-prs/SKILL.md
Recursively extract GitHub Pull Request links from Jira issues
npx skillsauth add openshift-eng/ai-helpers extract-prsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill recursively discovers Jira issues and extracts all associated GitHub Pull Request links.
IMPORTANT FOR AI: This is a procedural skill - when invoked, you should directly execute the implementation steps defined in this document. Do NOT look for or execute external scripts (like extract_prs.py). Follow the step-by-step instructions in the "Implementation" section below.
Use this skill when you need to:
Key characteristics:
parent = KEY BFSplugins/jira/README.md for setup)jq installed for JSON parsinggh) installed and authenticated for fetching PR metadataPurpose: This skill returns structured JSON that serves as an interface contract for consuming skills and commands.
Delivery Method:
.work/extract-prs/{issue-key}/output.json if user explicitly requests to save resultsSchema Version: 1.0
{
"schema_version": "1.0",
"metadata": {
"generated_at": "2025-11-24T10:30:00Z",
"command": "extract_prs",
"input_issue": "OCPSTRAT-1612"
},
"pull_requests": [
{
"url": "https://github.com/openshift/hypershift/pull/6444",
"state": "MERGED",
"title": "Add support for custom OVN subnets",
"isDraft": false,
"sources": ["comment", "description", "remote_link"],
"found_in_issues": ["CNTRLPLANE-1201", "OCPSTRAT-1612"]
}
]
}
Fields:
schema_version: Format version ("1.0")metadata: Generation timestamp, command name, and input issuepull_requests: Array of PR objects with url, state, title, isDraft, sources, and found_in_issuesThe skill operates in three main phases:
Discovers all descendant issues using parent = KEY JQL with BFS recursion.
Implementation:
Fetch issue metadata via getJiraIssue with the issue key, requesting fields summary, description, issuetype, status, comment, and expand: "changelog".
fields.description -- for text-based PR URL extractionfields.comment.comments -- for PR URLs mentioned in commentschangelog.histories -- for remote link PR URLs from RemoteIssueLink field changesSearch for descendant issues using BFS via searchJiraIssuesUsingJql with jql: "parent = <issue-key>", fields: ["key"], and maxResults: 100. Recursively search parent = <child-key> for each result until no more children. Only fetch the key field here -- full data (including changelog) is fetched per-issue in Phase 2.
Fetch full data for each issue (including root + all descendants) via getJiraIssue with fields summary, description, issuetype, status, comment, and expand: "changelog". This fetches description, comments, and changelog (which includes remote links). Excludes issue links (relates to, blocks, etc.) -- only parent-child relationships.
Extracts PR URLs from two sources:
getJiraIssue with issueIdOrKey and expand: "changelog"changelog.histories[].items[] entries where field == "RemoteIssueLink"toString (or to_string) value of each entry for GitHub PR URLs matching https://github.com/{owner}/{repo}/pull/{number}RemoteIssueLink field changestoString or to_string field/pull/ or /pulls/ patternfields.description and fields.comment.comments[] (already fetched in Phase 1)fields.description (plain text or Jira wiki format)fields.comment.comments[] array and search each comment.bodyhttps?://github\.com/([\w-]+)/([\w-]+)/pulls?/(\d+)# From description (stored in variable from MCP response)
description_prs=$(echo "$description" | \
grep -oE 'https?://github\.com/[^/]+/[^/]+/pulls?/[0-9]+')
# From comments (parse JSON in memory)
comment_prs=$(echo "$issue_json" | \
jq -r '.fields.comment.comments[]?.body // empty' | \
grep -oE 'https?://github\.com/[^/]+/[^/]+/pulls?/[0-9]+')
# Combine all PRs
all_prs=$(echo -e "${description_prs}\n${comment_prs}" | sort -u)
Deduplication:
sources array: ["comment", "description", "remote_link"] (alphabetically sorted)
"comment": Found in issue comments"description": Found in issue description"remote_link": Found via Jira Remote Links APIfound_in_issues array: ["OCPSTRAT-1612", "CNTRLPLANE-1201"] (alphabetically sorted)PR Metadata: Fetch via gh pr view {url} --json state,title,isDraft. CRITICAL: When building the output JSON, you MUST use the exact values returned by gh pr view - do NOT manually type or guess PR states/titles.
Output: Build JSON in memory using jq -n, output to console. Only save to .work/extract-prs/{issue-key}/output.json if user explicitly requests.
Important: Use bash variables for all data - no temporary files to avoid user confirmation prompts.
expand="changelog" returns error, continue with text-based extraction only (graceful degradation)pull_requests array (valid result)maxResults parameter in searchJiraIssuesUsingJqlgh pr view fails due to rate limiting, display error with reset timegh pr view returns error (PR deleted/private), exclude that PR from outputAPI calls: BFS searchJiraIssuesUsingJql calls + N getJiraIssue (with changelog) + M gh pr view
File I/O: Save PR metadata to .work/extract-prs/{issue-key}/pr-*-metadata.json, build final JSON by reading files
research
Shared engine for analyzing Jira issue activity and generating status summaries
testing
Snapshot OpenShift payload data (release controller, PR diffs, comments, CI jobs, JUnit results, regression tracking) to a local directory for offline analysis
development
Analyze a payload snapshot to identify root causes of blocking job failures, score candidate PRs, and produce an HTML report with revert recommendations
tools
Create TRT JIRA bugs, open revert PRs, and trigger payload jobs for high-confidence revert candidates