$npx -y skills add opendatahub-io/ai-helpers --skill pr-jira-linkerFind and link Jira issues to PRs/MRs that are missing Jira references. Supports single PR/MR linking and batch audit of configured repos. Use when the user mentions "link PR to Jira", "scan PRs", "PR audit", "MR missing Jira", "link merge request", or wants to connect code change
| 1 | # PR-Jira Linker |
| 2 | |
| 3 | Detects PRs/MRs missing Jira references, finds or creates the matching Jira issue, and links them bidirectionally. |
| 4 | |
| 5 | ## Required Tools |
| 6 | |
| 7 | - **GitHub** (MCP: user-github): `list_pull_requests`, `pull_request_read`, `update_pull_request` |
| 8 | - **GitLab** (MCP: plugin-gitlab-GitLab): `get_merge_request`, `get_merge_request_commits`, `search`, `create_workitem_note` |
| 9 | - **Jira** (MCP: user-mcp-atlassian): `jira_search`, `jira_add_comment`, `jira_create_issue` |
| 10 | - **Shell** — git commands, `curl` for GitLab API fallback (constrained; see safety rules below) |
| 11 | |
| 12 | ## Configuration |
| 13 | |
| 14 | The skill reads repository lists and project defaults from a user-provided `config.yaml` file. A minimal example: |
| 15 | |
| 16 | ```yaml |
| 17 | defaults: |
| 18 | project_key: "MYPROJECT" |
| 19 | issue_type: "Task" |
| 20 | security_level: "Red Hat Employee" |
| 21 | priority: "Normal" |
| 22 | component: "Engineering" |
| 23 | team: "My Team" |
| 24 | team_id: "your-team-id-here" |
| 25 | |
| 26 | repos: |
| 27 | gitlab: |
| 28 | - project: "my-org/my-repo" |
| 29 | host: "gitlab.example.com" |
| 30 | github: |
| 31 | - owner: "my-org" |
| 32 | repo: "my-repo" |
| 33 | ``` |
| 34 | |
| 35 | ## Jira Key Detection |
| 36 | |
| 37 | Scan for pattern `[A-Z][A-Z0-9]+-[0-9]+` in: (1) PR/MR title, (2) description, (3) branch name, (4) commit messages. |
| 38 | If found, mark as **candidate key detected** and verify bidirectional linkage (PR/MR references Jira key and Jira issue has PR/MR URL comment) before classifying as **linked**. |
| 39 | |
| 40 | ## Mode 1: Single PR/MR |
| 41 | |
| 42 | **Trigger**: User provides a PR/MR URL or says "link this PR to Jira". |
| 43 | |
| 44 | ### Step 1: Read the PR/MR |
| 45 | |
| 46 | Use `pull_request_read` (GitHub) or `get_merge_request` (GitLab). If GitLab MCP is down, fall back to `curl --negotiate -u:` against the GitLab REST API under the following **mandatory safety constraints**: |
| 47 | |
| 48 | 1. **Host allowlist**: Only issue requests to hostnames explicitly listed in `config.yaml` under `repos.gitlab[].host`. Reject any target not in the allowlist. |
| 49 | 2. **No raw IPs**: Reject URLs containing raw IP addresses (IPv4 or IPv6). Resolve hostnames and reject any that resolve to private/reserved ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 169.254.0.0/16, ::1, fc00::/7, fe80::/10) to prevent SSRF. |
| 50 | 3. **URL canonicalization**: Parse the URL, verify scheme is `https://`, and reconstruct from validated components. Do not pass user-supplied URLs directly to shell commands. |
| 51 | 4. **Safe argument passing**: Pass URL, host, and path as separate, safely shell-quoted arguments. Never string-concatenate untrusted input into commands. Pass credentials via environment variables (e.g., `GITLAB_TOKEN`), not interpolated into command strings. |
| 52 | 5. **Resource limits**: Always use `--max-time 30 --max-redirs 3` to enforce timeouts and limit redirects. |
| 53 | 6. **Protocol restriction**: Use `--proto '=https'` to prevent protocol downgrade or scheme switching. |
| 54 | |
| 55 | Extract title, description, branch name, author. |
| 56 | |
| 57 | ### Step 2: Detect Jira Reference |
| 58 | |
| 59 | Scan title, description, branch, and commits for Jira key. |
| 60 | |
| 61 | - **Found**: Report candidate key and verify bidirectional link status before deciding skip/fix. |
| 62 | - **Not found**: Proceed to Step 3. |
| 63 | |
| 64 | ### Step 3: Find Matching Jira |
| 65 | |
| 66 | Search Jira with **escaped/sanitized** keywords from the PR/MR title (treat user text as data, not query syntax). Strip or escape JQL/Lucene operators (`+ - && || ! ( ) { } [ ] ^ " ~ * ? : \ /`) before constructing the query. If sanitized tokens are empty, fall back to prompting the user for manual input. Show matches and let user pick, or offer to create new / enter key manually / skip. |
| 67 | |
| 68 | ### Step 4: Link Bidirectionally |
| 69 | |
| 70 | After user selects or creates a Jira issue: |
| 71 | |
| 72 | **4a. Add Jira ref to PR/MR**: Use `update_pull_request` (GitHub) to append to description. For GitLab, use `create_workitem_note` to add a comment with the Jira link. |
| 73 | |
| 74 | **4b. Add PR/MR link to Jira**: Use `jira_add_comment` with MR/PR URL, repo, branch, and author. |
| 75 | |
| 76 | **4c. Report success**: Show both URLs and confirm what was linked. |
| 77 | |
| 78 | **Never modify a PR/MR or Jira issue without showing a preview and getting user confirmation.** |
| 79 | |
| 80 | ## Mode 2: Batch Audit |
| 81 | |
| 82 | **Trigger**: "scan PRs for missing Jira links", "PR audit", "check repos for unlinked PRs". |
| 83 | |
| 84 | 1. **Load repos** from config.yaml |
| 85 | 2. **Scan** open PRs/MRs in each repo, run Jira key detection on each |
| 86 | 3. **Report**: List missing vs. linked (verified bidirectionally), offer to fix individually or fix all |
| 87 | 4. **Fix**: Run the Single PR/MR flow for each selected item |
| 88 | |
| 89 | ## Creating New Jira Issues |
| 90 | |
| 91 | When no matching Jira exists, the skill can create one: |
| 92 | |
| 93 | 1. Gather context from PR/MR content (title, description, diff) |
| 94 | 2. Read default |