Skip to content
Back to skills

Reading Pr Context

ASecurity

Use when starting a PR review and the session file needs to be created or refreshed. Not user-facing — invoked by the orchestrator only.

  • 3 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 26, 2026
developmentpythongobashcode-reviewgit

Works with

  • claude code

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add ahardin13/review-assistant --skill reading-pr-context --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reading Pr Context?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reading Pr Context
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ahardin13-reading-pr-context/badge)](https://www.skillsdirectory.com/skills/ahardin13-reading-pr-context)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: reading-pr-context
description: Use when starting a PR review and the session file needs to be created or refreshed. Not user-facing — invoked by the orchestrator only.
---

# Reading PR Context

Gather all context for a PR review: metadata, diff, linked issues, REVIEW.md guidelines, and code-review analysis. Write everything to a session file. Do not present anything to the user — the calling skill handles that.

**File I/O:** Use `Bash` with heredocs or `>>` for session/temp files — not Write, Read, or Edit tools.

## Inputs

- `pr_number`: required
- `repo`: owner/repo
- `threshold`: confidence threshold (default: 50)

## Step 1: Clean up old sessions

Data dir: `$HOME/.local/state/review-assistant` (kept OUT of `~/.claude/` so "always allow" permission rules persist — paths under `~/.claude/` are treated as sensitive by Claude Code and re-prompt every session).

```bash
mkdir -p $HOME/.local/state/review-assistant/sessions && find $HOME/.local/state/review-assistant/sessions -name "*.md" -mtime +7 -delete
find $HOME/.local/state/review-assistant -maxdepth 1 -name "pr-*-recreate-*.json" -mtime +7 -delete 2>/dev/null
```

The second `find` prunes the recreate-backup payloads written by `scripts/post-pending-review.py` before each destructive DELETE; they live one level above `sessions/` so the first `find` doesn't reach them.

## Step 2: Check PR eligibility

```bash
gh pr view <PR_NUMBER> --repo <REPO> --json state,isDraft,title
```

- **Closed/merged:** Use `AskUserQuestion`: "PR #N is closed. Proceed anyway?" with options "Yes, review it anyway" / "No, exit". Stop if no.
- **Draft:** Proceed normally, note "This is a draft PR."
- **Already reviewed:** Check for existing session file(s) matching `pr-<NUMBER>-*.md` in `$HOME/.local/state/review-assistant/sessions/`. If found, use the most recent one (by filename timestamp). Use `AskUserQuestion`:
  > "You've reviewed this PR before (session: <date>). How would you like to proceed?"
  - "Full re-review" — start fresh, delete old session file
  - "Incremental" — review only changes since last review (reads `last_reviewed_sha` from session file, uses `git diff <last_reviewed_sha>..HEAD`)
  - "Update session" — re-analyze full diff, keep `## User Context` from previous session

## Step 3: Fetch PR metadata and diff

```bash
gh pr view <PR_NUMBER> --repo <REPO> --json title,body,commits,labels,milestone,closingIssuesReferences,headRefOid
```

Record `headRefOid` as `review_sha`.

Fetch linked GitHub issues from `closingIssuesReferences`:

```bash
gh issue view <ISSUE_NUMBER> --repo <REPO> --json title,body
```

Fetch the full diff and cache it for reuse by downstream skills and the analyzer subagent. Writing to our data dir (rather than `/tmp/`) keeps the path inside the pre-approved allowlist so subagents don't get a per-session permission prompt:

```bash
mkdir -p $HOME/.local/state/review-assistant
gh pr diff <PR_NUMBER> --repo <REPO> > $HOME/.local/state/review-assistant/pr-<PR_NUMBER>-diff.txt
```

Refer to this path as `<DIFF_PATH>` in later steps.

## Step 4: Produce the "why" summary

From the PR title, body, commit messages, and linked issues, write a 2-3 sentence summary of what changed and why.

**If the why is unclear** (empty body, unhelpful commits, no linked issues): ask the user ONE question:

> "I couldn't determine why this change was made. Can you give me a brief summary of the intent?"

## Step 5: Load REVIEW.md

Load in order (both if present):

1. `~/.claude/REVIEW.md`
2. `REVIEW.md` at the project root

If neither exists: note "No REVIEW.md found — proceeding without review guidelines."

Concatenate contents (per-repo appends to/overrides global).

## Step 6: Write initial session file

```bash
cat <<'EOF' > $HOME/.local/state/review-assistant/sessions/pr-<NUMBER>-<YYYYMMDD-HHMMSS>.md
# Review Session: PR #<NUMBER> — <title>
review_sha: <headRefOid>

## Why
<2-3 sentence summary>

## Findings
_Populated by analysis_

## Queued Comments
_Populated during interactive review_

## User Context
_Populated if user provides corrections_
EOF
```

## Step 7: Run code-review analysis

### 7a: Check code-review plugin availability

Attempt to invoke the `code-review:code-review` skill. If not available, stop:

> "The `code-review` plugin is required but not installed. Install with:
> ```
> claude plugin install code-review@claude-plugins-official
> ```
> Then re-run `/review-assistant`."

### 7b: Identify inconsequential files

From the diff, identify files to skip (pure renames, generated files, mass reformats, bulk deletions). Mark as `skip: true`. **Exception:** If REVIEW.md guidelines call for reviewing a category of these files, include them.

### 7c: Dispatch code-review-analyzer agent

Dispatch the `code-review-analyzer` agent (Agent tool with `subagent_type="review-assistant:code-review-analyzer"`) with the following task. Substitute `{pr_number}`, `{repo}`, `{diff_path}` (the `<DIFF_PATH>` from Step 3), and `{review_md_content}`:

> Review PR #{pr_number} in {repo}.
>
> - The PR diff has already been fetched and cached at `{diff_path}`. Use that file instead of re-running `gh pr diff`. Do NOT write scratch diffs to `/tmp/` — that path triggers a user permission prompt every session. If the `code-review` skill accepts a diff path, pass `{diff_path}` to it; otherwise read from `{diff_path}` directly.
> - Use a confidence threshold of 0 — return EVERY finding the code-review skill produces, regardless of confidence. The caller filters later; do not filter here.
> - In addition to any CLAUDE.md files, also check the diff against these review guidelines:
>
> ---
> {review_md_content}
> ---
>
> If no review guidelines were provided above (the section between the --- markers is empty), skip the review guidelines compliance check.

### 7d: Parse and deduplicate findings

Parse the subagent's response. The agent's output format is a metadata head row followed by an indented description continuation block; the block ends at the next `- file:` line (or end of output). Inside the block, blank lines are paragraph breaks — preserve them. Each finding needs: `file`, `line`, `severity`, `confidence`, `source`, `description`. Skip malformed entries.

**Backwards compat:** if the head row has a trailing `description: <text>` (legacy single-line format), use that as the description. Treat it as a single paragraph.

Merge findings at the same `file` + `line`. Keep the highest confidence score. When merging descriptions, keep the longer/more-paragraph-shaped one rather than concatenating.

Serialize the merged list as JSON to `$HOME/.local/state/review-assistant/pr-<NUMBER>-findings.json` so Step 7e can read it.

### 7e: Anchor findings to the diff

Before writing findings to the session file, attach a line-text anchor (`code:` field) so downstream consumers can verify that comments land on the right lines.

**Do NOT write a standalone `/tmp/anchor-*.py` script for this step.** The authoritative anchorer is `scripts/classify-and-verify.py`, which runs at POST time inside `auto-draft-review` / `interactive-diff-review` and includes ±3-line text-match re-anchoring as a fallback. Step 7e's only job is to populate the `code:` text — a freelanced script duplicates the diff parser already in the plugin and adds drift risk for no benefit.

Use a one-shot inline `python3` invocation (no separate `.py` file). Read the cached diff once, build an in-memory `(file, right_line) -> text` / `(file, left_line) -> text` index, look up each finding's `(file, line)`, and emit decorated findings to stdout. ~25 lines is enough — example shape:

```bash
DIR="$HOME/.local/state/review-assistant"
python3 - "$DIR/pr-<NUMBER>-diff.txt" "$DIR/pr-<NUMBER>-findings.json" <<'PY' > "$DIR/pr-<NUMBER>-findings.anchored.jsonl"
import json, re, sys
right, left, cur, rl, ll = {}, {}, None, 0, 0
DIFF = re.compile(r"^diff --git a/(.+?) b/(.+)$")
HUNK = re.compile(r"^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@")
for raw in open(sys.argv[1]):
    raw = raw.rstrip("\n")
    if (m := DIFF.match(raw)): cur = m.group(2); right.setdefault(cur, {}); left.setdefault(cur, {}); continue
    if (m := HUNK.match(raw)): ll, rl = int(m.group(1)), int(m.group(2)); continue
    if cur is None or raw[:3] in ("+++", "---"): continue
    if   raw[:1] == "+": right[cur][rl] = raw[1:]; rl += 1
    elif raw[:1] == "-": left[cur][ll]  = raw[1:]; ll += 1
    elif raw[:1] == " ": right[cur][rl] = left[cur][ll] = raw[1:]; rl += 1; ll += 1
for f in json.load(open(sys.argv[2])):
    p = f["file"]                              # strip repo-absolute prefix if any
    cand = next((k for k in right if p == k or p.endswith("/" + k)), p)
    f["file"] = cand
    f["code"] = right.get(cand, {}).get(f["line"]) or left.get(cand, {}).get(f["line"])
    f["in_diff"] = cand in right
    print(json.dumps(f))
PY
```

Findings come in via `sys.argv[2]` rather than stdin because `python3 -` consumes stdin for the heredoc script body — `< findings.json` would be silently dropped.

For each finding:

- If `code` is populated: render `code: \`<exact line text>\`` and `in_diff: true` in Step 7f.
- If the file isn't in the diff, or the line isn't in any hunk: set `in_diff: false`. **Do NOT drop the finding** — auto mode still reports it, and the post-time classifier may re-anchor it by text match.

### 7f: Write findings to session file

Record the active threshold for later consumers:

```
threshold: <T>
```

Partition findings by confidence. Write those with `confidence >= threshold` under `## Findings`, and those below threshold under `## Below Threshold` (same format, minus `skip`). Both sections use this shape:

```
## Findings
- file: src/foo.ts, line: 42, severity: high, confidence: 87, source: bug-scan, skip: false, in_diff: true
  code: `const id = user.id;`
  **Possible null dereference.** When `getUser()` returns null this line throws a TypeError. The pattern appears in three callers, only one of which handles the rejected promise.

  **Suggestion:** wrap the call in a guard, or change `getUser` to throw a typed error so the caller has to acknowledge the failure mode.
- file: generated/types.ts, line: 1, severity: info, confidence: 100, source: claude-md, skip: true, in_diff: true
  code: `export type Foo = ...`
  Generated file — skipping.

## Below Threshold
- file: src/bar.ts, line: 15, severity: low, confidence: 30, source: code-comments, in_diff: true
  code: `setTimeout(fn, 86400);`
  Magic number 86400 could be a named constant.
```

Description-block rules:

- Each continuation line MUST start with at least two spaces. Indented Markdown nesting (lists, fenced code blocks at four-space indent) past the prefix is preserved when the body is rendered on GitHub.
- **Blank lines between paragraphs MUST be bare blank lines** — not two-space-indented empty lines. The classifier's parser treats truly empty raw lines as paragraph breaks (`\n\n`); a blank line that contains stray whitespace breaks differently.
- Single-paragraph descriptions stay on a single continuation line. Don't manufacture paragraph breaks that aren't in the source finding.

Interactive consumers read only `## Findings`. Auto-mode reads both so it can report what got filtered.

The `code:` line is the anchor that `classify-and-verify.py` uses to confirm the finding lands on the right diff line (with a ±3 line scan on mismatch). Keep backticks around the text literally — they're stripped by the parser.

## Step 8: Hand off silently

Do NOT print a closing summary, "session file ready" message, or "returning to the orchestrator" line — any user-facing text here reads as a turn boundary and causes the orchestrator to stop before dispatching the walkthrough skill.

Hold the session file path in working memory. The orchestrator's next step (Step 4 of `review-assistant.md`) will pass it to either `auto-draft-review` or `interactive-diff-review`. Proceed directly to that dispatch with no intervening output.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.