# Github Code Review Pr

> MANDATORY TRIGGER: Any URL containing `/pull/` + review intent = invoke this skill IMMEDIATELY. Do NOT analyze the hostname — git.x.com, gitlab.y.com, code.z.au are all potentially GitHub Enterprise. NEVER speculate about the platform; just run the skill and let `gh` succeed or fail. Zero pre-analysis, zero hesitation. Comprehensive PR code review via GitHub CLI (`gh`). Trigger on review/审查/评审/CR + PR URL or PR number. For routine PR actions (not full code review), prefer `gh-operations`.

- Skill: `jie-meng/github-code-review-pr` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add jie-meng/github-code-review-pr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jie-meng/github-code-review-pr/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: Apache-2.0
- Author: jie-meng (https://skillmd.com/u/jie-meng)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/jie-meng/github-code-review-pr

---


# When to Use This Skill

## ZERO-SPECULATION RULE (read this FIRST)

**If the user's message contains a URL with `/pull/` and any review intent, invoke this skill IMMEDIATELY.** Do NOT spend ANY tokens analyzing the hostname, guessing the platform, or debating whether `gh` will work. The correct behavior is:

1. See `/pull/` URL + review intent → trigger skill
2. Run `review_runner.py prepare <URL>` → let `gh` succeed or fail
3. If `gh` fails → report the error and suggest `gh auth login --hostname <host>`

**WRONG behavior (NEVER do this):**
- "This appears to be a self-hosted GitLab instance" — WRONG, you don't know that
- "git.company.com looks like GitLab" — WRONG, it's GitHub Enterprise
- "gh CLI won't work with this host" — WRONG, you haven't tried yet
- Any reasoning about whether the host is GitHub, GitLab, Bitbucket, etc. — WRONG

**WHY:** Users who paste a `/pull/` URL and say "review" are trusting you to act. The domain `git.*.com`, `git.*.com.au`, `code.*.com` — these are overwhelmingly GitHub Enterprise. Even if they aren't, trying `gh` first and failing fast is better than wasting 500 tokens on speculation. Trust the user's input first, fix errors later.

## Trigger conditions

**ALWAYS invoke this skill when user wants to review a Pull Request:**
- "review this PR" / "review PR" / "PR review" / "PR code review"
- "审查这个PR" / "帮我看这个PR" / "PR审查" / "review pull request"
- "review https://github.com/owner/repo/pull/123"
- "帮我看一下这个 PR https://git.company.com/org/repo/pull/456"
- User provides ANY URL containing `/pull/` and asks for review/feedback
- User provides a PR number and asks for review/feedback
- "help me review this pull request"
- "use github-code-review-pr skill"
- User shared a PR URL in previous turn, then says "使用技能" / "use this skill"

Trigger on any URL containing `/pull/`, regardless of the host domain. Do not make any judgment about the platform from domain text — trigger this skill and let `gh` handle it.

**This skill reviews remote PRs via `gh` CLI (not local staged changes).**
For local staged changes, use `code-review-staged` instead.

# Requirements

- **GitHub CLI (`gh`)** must be installed and authenticated
- **`curl`** must be available for downloading PR screenshots/assets
- **Optional for enterprise SSO**: `curl --negotiate -u :` support for SPNEGO/Kerberos-protected asset URLs
- Run `skills-check github-code-review-pr` to verify dependencies

## Security — MANDATORY rules for AI agents

1. **NEVER echo, print, or log** the values of any environment variable containing credentials (`GH_TOKEN`, `GITHUB_TOKEN`, etc.). Do NOT run commands like `echo $GH_TOKEN` or `printenv GITHUB_TOKEN` — even for debugging.
2. **NEVER pass token/credential values as inline CLI arguments or env-var overrides.** `gh` reads credentials from its own config — just run `gh` commands directly.
3. **When debugging auth errors**, rely solely on `gh auth status` output and `gh` error messages. Do NOT attempt to verify tokens by reading or printing them.
4. **NEVER extract credentials from OS credential stores or config files.** Strictly forbidden commands include:
   - `security find-internet-password`, `security find-generic-password` (macOS Keychain)
   - `git credential fill`, `cat ~/.git-credentials`, `cat ~/.netrc`
   - Reading `~/.config/gh/hosts.yml` or any `gh` auth config file
   - Any command that outputs a password, token, or secret value from any credential store
5. **NEVER use extracted credential values in commands.** Do NOT manually construct authenticated requests (e.g. `curl -H "Authorization: token <value>"`). The `gh` CLI handles all authentication internally — use `gh api` for API calls instead of `curl` with raw tokens.

# Requirements for Outputs

## Code Review Quality

### Review Standards
- Code review MUST be comprehensive, identifying all potential issues
- Review MUST be thorough and rigorous, highlighting suspicious code
- Review MUST provide actionable, concrete suggestions with file paths and line references
- Review MUST consider the project's existing conventions, patterns, and style
- Review language MUST match the user's input language (Chinese or English)

### Language Detection
- Detect user's input language automatically
- If user input contains Chinese characters (Unicode U+4E00-U+9FFF), output review in Chinese
- If user input contains only English, output review in English

# Implementation

The skill executes these steps:

## Step 1: Parse PR Reference

Accept PR input in any of these formats:
- Full URL: `https://github.com/owner/repo/pull/123`
- Full URL with any host: `https://git.mycompany.com/owner/repo/pull/123`
- PR number (when inside a repo): `123`
- PR number with repo: `owner/repo#123`

Extract **owner**, **repo**, **PR number**, and **hostname**, then proceed immediately to Step 2. Do not make any judgment about what platform the host belongs to — `gh` will succeed or fail on its own, and its error output is sufficient to diagnose any issue.

MANDATORY execution rule:
- For any input containing `/pull/`, the next command must be `review_runner.py prepare <URL_or_NUMBER>`.
- Do not run host-based branching logic first.
- Do not run `gh auth status` as a pre-check gate before the first `gh pr view`/`gh pr diff` call.

## Step 2: Fetch PR Metadata and Diff

### MANDATORY: Use the bundled runner script

**Do NOT manually run `gh pr view` or `gh pr diff`.** Always use the bundled runner script — it performs Step 2, Step 3 (path selection + checkout), and cleanup wiring in a single deterministic call:

```bash
python3 scripts/review_runner.py prepare <URL_or_NUMBER>
```

**STOP — How to locate the script (FORBIDDEN: Glob, find, rg, or any recursive search):**

Check these fixed paths in order. Use the first one that exists. This works on macOS, Linux, and Windows:

```python
import pathlib, subprocess, sys

candidates = [
    pathlib.Path.home() / ".config/opencode/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".claude/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".copilot/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".cursor/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".gemini/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".codex/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".qwen/skills/github-code-review-pr/scripts/review_runner.py",
    pathlib.Path.home() / ".grok/skills/github-code-review-pr/scripts/review_runner.py",
]
script = next((p for p in candidates if p.exists()), None)
if not script:
    print("ERROR: review_runner.py not found at any known install path", file=sys.stderr)
    sys.exit(1)
subprocess.run([sys.executable, str(script), "prepare", "<URL_or_NUMBER>"])
```

If you are running shell commands instead of Python, convert the above to the equivalent `ls`/`test -f` checks — but NEVER use recursive search.

This command:
1. Fetches PR metadata (`gh pr view`) and diff (`gh pr diff`) exactly once
2. Runs `path_select.py` (A → B → C → D selection with `[PATH-CHECK]`/`[PATH-SELECTED]` trace)
3. When neither A nor B matches, queries repo size via `gh api` to decide C vs D:
   - Repo ≤ 100 MB → Path C: clone into shared cache via `repo_manager.py sync` (reusable)
   - Repo > 100 MB → **pauses with exit code 10** — asks the AI agent to present options to the user (see below)
   - If size query fails → defaults to Path C (with fallback to D if clone fails)
4. Handles checkout with fallback (`pull/<PR>/head`)
5. Saves all outputs to a session manifest

Machine-readable output lines (normal exit code 0):
- `RUN_MANIFEST=<path>` — session manifest JSON (needed for cleanup and gate scripts)
- `PR_VIEW_JSON_PATH=<path>` — saved PR metadata JSON file
- `PR_DIFF_PATH=<path>` — saved PR diff file
- `CLEANUP_LOG_PATH=<path>` — where to tee cleanup output in Step 7a
- `REVIEW_TEXT_PATH=<path>` — where to write the review text in Step 7b
- `SELECTED_PATH=A|B|C|D|DIFF_ONLY` — which path was selected
- `REPO_WORKDIR=<path>` — local repo directory (empty if diff-only)
- `PR_STATE=OPEN|CLOSED|MERGED` — PR state
- `CONTEXT_MODE=full_repo|diff_only` — whether full repo context is available
- `CONTEXT_LIMITATION=<message>` — explanation when context is limited
- `CHECKOUT_DONE=true|false` — whether the PR branch is already checked out at `REPO_WORKDIR`
- `NOTE: PR branch is already checked out...` — human-readable confirmation (when `CHECKOUT_DONE=true`)

### Handling exit code 10 (large repo — user decision required)

When the repo exceeds the size threshold (default 100 MB) and neither Path A nor B matched, `review_runner.py prepare` exits with code **10** instead of auto-deciding. It emits these lines:

- `NEEDS_USER_DECISION=true`
- `REPO_SIZE_MB=<float>` — actual repo size in MB
- `THRESHOLD_MB=<int>` — the threshold that was exceeded
- `RECOMMENDED_DEFAULT=D|diff-only` — which option to pre-select (D for ≤ 1 GB, diff-only for > 1 GB)
- `PENDING_RUN_DIR=<path>` — session directory with saved metadata/diff
- `PR_VIEW_JSON_PATH=<path>` — already fetched, reusable
- `PR_DIFF_PATH=<path>` — already fetched, reusable

**When you receive exit code 10, you MUST present the user with three options.**

Use the repo size to determine the recommended default, then display the options. **Do NOT invent your own recommendation — follow the table below exactly.**

**Default selection rule:**

| Repo size | Default option | Rationale |
|---|---|---|
| ≤ 1 GB | **2. Sparse clone (Path D)** | Blobless sparse clone only downloads tree metadata (usually <50 MB even for large repos) and fetches file content on demand. Good trade-off between download cost and review context quality. |
| > 1 GB | **3. Diff-only** | Monorepo-scale repos have deep commit history and massive tree objects. Tree metadata alone can take minutes to transfer. Diff-only is the pragmatic choice. |

Display the repo size and present the options. The **default option** (from the table above) MUST be pre-selected (cursor/arrow points to it). Do NOT add "(Recommended)" text to any option — the pre-selection is the recommendation.

> This repository is **{REPO_SIZE_MB} MB** (exceeds the {THRESHOLD_MB} MB threshold).
>
> How would you like to proceed?
> 1. Clone to shared cache (Path C) — larger download, but cached for future reviews
> 2. Sparse clone to temp dir (Path D) — smaller download, only PR-related files, deleted after review
> 3. Diff-only — no clone, review based on PR diff and metadata only (limited context)

After the user chooses, resume the session:

```bash
python3 scripts/review_runner.py prepare <URL_or_NUMBER> \
    --force-path C|D|diff-only --run-dir <PENDING_RUN_DIR>
```

This resumes the pending session: it reuses the previously fetched metadata and diff (no repeated `gh pr view`/`gh pr diff`), executes the user's chosen path, and emits the standard machine-readable output lines with exit code 0.

**After `review_runner.py prepare` succeeds (exit code 0) — MANDATORY next actions:**

When you see `CHECKOUT_DONE=true` in the output, the runner has ALREADY fetched and checked out the PR branch. You MUST NOT run any git commands to prepare the repo.

1. Read PR metadata from `PR_VIEW_JSON_PATH` (do NOT re-run `gh pr view`)
2. Read PR diff from `PR_DIFF_PATH` (do NOT re-run `gh pr diff`)
3. **SKIP Step 3 entirely — go directly to Step 4 (read files).** The runner output says `CHECKOUT_DONE=true` — the PR branch is ALREADY checked out at `REPO_WORKDIR`. To gather context, just read files there:
   ```bash
   cd <REPO_WORKDIR>
   # read files directly — CHECKOUT_DONE=true means the PR branch is already here
   ```
4. Save all emitted paths (`RUN_MANIFEST`, `CLEANUP_LOG_PATH`, `REVIEW_TEXT_PATH`) — you will need them in Step 7

**WRONG (do NOT do this when CHECKOUT_DONE=true):**
- `git fetch origin <branch>` — WRONG, runner already fetched
- `git checkout <branch>` — WRONG, runner already checked out
- `gh pr checkout` — WRONG, runner already handled this
- Running ANY git command to "prepare" the repo — WRONG, it's already prepared

### Fallback: Manual commands (ONLY if review_runner.py is not found)

If and only if `review_runner.py` cannot be located at any install path, fall back to manual commands. **You MUST still run `path_select.py` before any clone/fetch** (see Step 3).

#### 2a. PR metadata
```bash
GH_PAGER=cat gh pr view <URL_or_NUMBER> --json number,title,body,state,author,baseRefName,headRefName,labels,reviewDecision,additions,deletions,changedFiles,commits,files,comments,reviews,url
```

Key fields:
- `title`, `body` — PR description and intent
- `baseRefName`, `headRefName` — branches involved
- `files` — list of changed files with `path`, `additions`, `deletions`
- `commits` — commit history in the PR
- `comments`, `reviews` — existing discussion context
- `url` — used to extract `owner/repo`

#### 2b. PR diff
```bash
GH_PAGER=cat gh pr diff <URL_or_NUMBER>
```

#### 2b.1 Single-fetch rule (avoid repeated network calls)

- Fetch metadata and diff once, then reuse the captured output throughout the review.
- Do NOT re-run `gh pr diff`/`gh pr view` unless output is missing/corrupted or PR head changed during review.
- If a re-fetch is required, state the reason explicitly.

Enforcement rule:
- Persist first successful outputs to variables/files (for example `PR_VIEW_JSON_PATH`, `PR_DIFF_PATH`) and reuse them for all later analysis.
- Any second `gh pr view`/`gh pr diff` call MUST include a log line in advance: `[REFETCH-REASON] <specific reason>`.
- "Need to read another section" is NOT a valid reason; read from the previously captured output instead.

### 2c. Collect and analyze PR images (default when relevant)

After metadata is fetched, inspect `body`, `comments`, and `reviews` for image links:
- Markdown image syntax: `![alt](https://...)`
- Plain asset URLs (especially `/assets/` links)

Automatically do this (without extra user back-and-forth) when:
- user asks to read screenshot/image content, or
- screenshots are part of PR verification evidence (offline check steps, UI proof, tracking proof), or
- image information is required to validate correctness/risk.

Download relevant images under a random run directory in the unified cache.

**Bash (macOS / Linux):**
```bash
if [[ "$(uname -s)" == "Darwin" ]]; then
  CACHE_ROOT="$HOME/Library/Caches/mythril-skills-cache"
else
  CACHE_ROOT="${XDG_CACHE_HOME:-$HOME/.cache}/mythril-skills-cache"
fi
CACHE_DIR="$CACHE_ROOT/github-code-review-pr"
mkdir -p "$CACHE_DIR"
RUN_DIR=$(mktemp -d "$CACHE_DIR/XXXXXXXX")
IMAGE_CACHE="$RUN_DIR/images"
mkdir -p "$IMAGE_CACHE"
```

**PowerShell (Windows):**
```powershell
$CACHE_ROOT = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "mythril-skills-cache"
$CACHE_DIR = Join-Path $CACHE_ROOT "github-code-review-pr"
New-Item -ItemType Directory -Force -Path $CACHE_DIR | Out-Null
$RUN_DIR = Join-Path $CACHE_DIR ([System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory -Force -Path $RUN_DIR | Out-Null
$IMAGE_CACHE = Join-Path $RUN_DIR "images"
New-Item -ItemType Directory -Force -Path $IMAGE_CACHE | Out-Null
```

Never use ad-hoc temp locations like `/tmp/<custom-folder>/...` for image artifacts.

**Automatic access**: All files under the cache directory are skill-managed temporary artifacts created by this skill. Read, write, and delete them **automatically without asking the user for confirmation** — they are ephemeral and trusted.

Use authenticated retrieval in this order:
1. `curl -fsSL "<image_url>" -o "<local_path>"`
2. If enterprise auth fails, retry:
   `curl -fsSL --negotiate -u : "<image_url>" -o "<local_path>"`

**Post-download validation (MANDATORY):** After each download, verify the file is actually an image — enterprise auth redirects often return HTTP 200 with an HTML login page instead of the real image. Run:

```bash
file --mime-type -b "<local_path>"
```

- If output starts with `image/` → valid image, proceed to read it.
- If output is `text/html`, `text/plain`, or anything other than `image/*` → the download captured an auth redirect page, not the real image. **Treat this as a retrieval failure**: delete the file (`rm -f "<local_path>"`), do NOT attempt to read it, and do NOT retry reading it. Log a one-line note (e.g., "Image download returned HTML instead of image data — likely auth redirect, skipping") and move on.

Only read **validated** images with image-capable tools and summarize:
- what the screenshot shows (UI/debug panel/logs),
- key values/events/URLs visible in the image,
- whether screenshot evidence supports the PR claim.

If image retrieval fails (curl error, non-image content, or auth redirect), log the URL and reason, skip the image, and continue the review. Do NOT repeatedly attempt to read invalid files. If ALL images fail, note in the review that image evidence could not be verified due to access restrictions, and suggest the user check screenshots manually.

## Step 3: Get Local Access to the Repository

The goal is to have repo context available locally so context gathering is just file reads — no per-file API requests. Try these paths **in order** — pick the first one that applies.

### Skip condition: If `review_runner.py prepare` was used in Step 2

**STOP — Skip this entire Step 3.** Do NOT run `git fetch`, `git checkout`, `gh pr checkout`, or any clone/branch command. The runner already did all of this. The PR branch is already checked out at `REPO_WORKDIR`. Just `cd` there and start reading files (Step 4).

The rest of Step 3 below is the **manual fallback path** — only execute it when `review_runner.py` was NOT available and you used manual Step 2 commands.

### Path Selection Script (MANDATORY when not using review_runner.py — run first, before any clone or fetch)

**Do NOT write inline path-check logic.** Instead, run the bundled `path_select.py` script. It checks A → B → C → D in order and prints `[PATH-CHECK]` / `[PATH-SELECTED]` lines as it runs — guaranteeing the trace appears in the execution log at decision time, not as prose in the final review output.

The script lives alongside this SKILL.md at `scripts/path_select.py`. Locate and run it using Python directly — do NOT use shell `eval` or complex shell expansions, as these may be blocked by shell safety filters in some AI tools:

**MANDATORY (permission-safe):** do not use recursive glob/find in `~` to locate this script. Use fixed candidate paths only.

```python
import subprocess, os, pathlib

# Step 1: locate path_select.py from known install locations
search_dirs = [
    pathlib.Path.home() / ".config/opencode/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".claude/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".copilot/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".cursor/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".gemini/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".codex/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".qwen/skills/github-code-review-pr/scripts",
    pathlib.Path.home() / ".grok/skills/github-code-review-pr/scripts",
]
script = next((d / "path_select.py" for d in search_dirs if (d / "path_select.py").exists()), None)

# Step 2: get current repo (empty string if not inside any repo or gh unavailable)
result = subprocess.run(
    ["gh", "repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"],
    capture_output=True, text=True, env={**os.environ, "GH_PAGER": "cat"}
)
current_repo = result.stdout.strip() if result.returncode == 0 else ""

# Step 3: run path selection — prints [PATH-CHECK] / [PATH-SELECTED] trace lines
# and prints machine-readable lines: SELECTED_PATH=..., REPO_PATH=...
output = subprocess.run(
    ["python3", str(script), "https://<host>/<owner>/<repo>", current_repo],
    capture_output=False  # prints trace directly to stdout
)
```

After the script runs, read `SELECTED_PATH` and `REPO_PATH` from the `KEY=VALUE` lines in the output (parse from stdout if needed), or re-run with `capture_output=True` to parse programmatically.

**Rules:**
- Run this block before any `git fetch`, `git checkout`, or clone command.
- Do not proceed to Step 4 until `[PATH-SELECTED]` is visible in the log.
- Do NOT re-implement path-check logic inline — always use the script.

### Execution guardrail (MANDATORY)

Immediately after parsing `SELECTED_PATH` / `REPO_PATH`, register cleanup handling for Step 7.
Do this **before** any fetch/checkout/clone command so cleanup still runs on mid-review failures.

- Keep `SELECTED_PATH` in a shell variable for cleanup branching.
- For Path A, capture `ORIGINAL_BRANCH` immediately.
- For Path B, capture `REPO_PATH` and resolve `DEFAULT_BRANCH` once.
- For Path C, capture `REPO_PATH` (same as Path B — it's a shared cache repo).
- For Path D, capture created temp dirs (`REVIEW_DIR`, `RUN_DIR`) as soon as they are created.
- Use an `EXIT` trap (or equivalent) so cleanup executes whether review succeeds, partially fails, or exits early.

### Path A: Already inside the target repo

When `SELECTED_PATH=A`, proceed immediately:

```bash
git fetch origin <baseRefName> <headRefName>
ORIGINAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
git checkout <headRefName>
```

Same reasoning as Path B: use `git checkout <headRefName>` directly (branch name known from Step 2a) rather than `gh pr checkout`, which can fail on Enterprise hosts that require separate `gh auth login --hostname` even when `gh pr view/diff` works.

After review, restore the original branch (see Step 7).

### Path B: Shared repo cache hit — full repo already cached

When `SELECTED_PATH=B`, `$REPO_PATH` is already set by the path-selection script above.

Update the cached repo and checkout the PR branch. `headRefName` is already known from Step 2a metadata — use it directly:

```bash
cd "$REPO_PATH"

# Fetch both branches: base for diff comparison, head for the PR code
git fetch origin <baseRefName> <headRefName>

# Checkout the PR branch using the branch name from metadata
git checkout <headRefName>
```

If the command above fails (missing remote branch, force-pushed branch, stale refs, or host-specific behavior), do **not** silently continue as if full repo context were available.

Run this mandatory fallback sequence:

```bash
# Keep error output for the final review limitation note
FETCH_ERR="$(git fetch origin <baseRefName> <headRefName> 2>&1)" || true

if ! git rev-parse --verify --quiet "origin/<headRefName>" >/dev/null; then
  # Fallback: fetch PR head ref directly by PR number
  git fetch origin "pull/<PR_NUMBER>/head:pr-<PR_NUMBER>-head" 2>&1 || true
fi

if git rev-parse --verify --quiet "origin/<headRefName>" >/dev/null; then
  git checkout <headRefName>
elif git rev-parse --verify --quiet "pr-<PR_NUMBER>-head" >/dev/null; then
  git checkout "pr-<PR_NUMBER>-head"
else
  CONTEXT_MODE="diff_only"
  CONTEXT_LIMITATION="Path B branch checkout failed: ${FETCH_ERR:-unknown fetch error}"
fi
```

When `CONTEXT_MODE=diff_only`:
- Continue review using Step 2 metadata + diff only (no fake local-context claims)
- Explicitly state this limitation in the review output
- **Still run Step 7 cleanup** and print `[PATH-CLEANUP] ...` status lines

**Why `git checkout <headRefName>` instead of `gh pr checkout`**: `gh pr checkout` can fail if the host hasn't been authenticated with `gh auth login --hostname <host>`, even when `gh pr view/diff` succeeds (those commands may use a different auth path). Since `headRefName` is already known from Step 2a metadata and the branch is available after `git fetch origin <headRefName>`, using `git checkout` directly is more reliable and avoids this failure mode.

For base branch comparison, use `origin/<baseRefName>` (the remote-tracking ref, guaranteed fresh after the fetch).

### Path C: Clone into shared cache (default for small/medium repos)

When `SELECTED_PATH=C`, the repo is not yet available locally. Before cloning, the runner queries the repo's disk size via `gh api repos/<owner>/<repo> --jq '.size'`:

- **Repo ≤ 100 MB** → proceed with Path C automatically (clone into shared cache)
- **Repo > 100 MB** → **pause and ask the user** — the runner exits with code 10 and the AI agent presents three options: Path C (clone to shared cache anyway), Path D (sparse clone to temp dir), or diff-only (no clone). See "Handling exit code 10" in Step 2 for details.
- **Size query fails** → proceed with Path C as default (safe fallback to D if clone fails)

When proceeding with Path C, the repo is cloned into the **shared `git-repo-cache`** via the bundled `repo_manager.py sync` command. This creates a blobless clone that is **reusable across sessions and skills** — the next PR review on the same repo will hit Path B instantly.

```bash
python3 scripts/repo_manager.py sync "<repo-url>"
```

This command:
1. Checks `repo_map.json` for an existing entry (handles stale entries automatically)
2. Clones with `--filter=blob:none` into `git-repo-cache/repos/<host>/<owner>/<repo>/`
3. Resets the working tree to a clean state on the default branch
4. Registers the repo in `repo_map.json` for future lookups
5. Prints the local path to stdout

After the clone, fetch and checkout the PR branches — same as Path B:

```bash
cd "$REPO_PATH"
git fetch origin <baseRefName> <headRefName>
git checkout <headRefName>
```

The same fallback sequence as Path B applies if checkout fails (`pull/<PR_NUMBER>/head` ref).

**Why Path C over direct sparse clone**: For most repos (up to tens of thousands of files), the blobless clone is only a few MB of tree/commit metadata. The benefit is substantial — the repo persists in the shared cache, so future reviews, `git-repo-reader` queries, and other skills can reuse it without any clone at all. File blobs are fetched on demand when files are read, keeping the initial cost low.

**Fallback to Path D**: Path C degrades to Path D when `repo_manager.py sync` fails at runtime (e.g., auth issues, network errors). The `[PATH-FALLBACK]` trace line is emitted. For repos exceeding the size threshold, the user is asked to choose (see "Handling exit code 10" in Step 2).

After review, the repo is reset to the default branch (same cleanup as Path B — see Step 7). The cached repo is **NOT deleted** — it is shared and reusable.

### Path D: Blobless sparse clone to temp directory (large repos or fallback)

Path D is used for **large repos** (> 100 MB per GitHub API) or as a **fallback** when Path C fails. It uses **blobless clone + sparse checkout** into a disposable temp directory under the skill's own cache, downloading only the directories needed for review.

**Why NOT `--depth=1` or `--single-branch`**: With `--single-branch` the refspec is restricted to one branch, making it impossible to later `git fetch origin <headRefName>` for a different branch. `--depth=1` implies `--single-branch`. Using `--filter=blob:none --sparse` (without depth/single-branch) gives us all refs and history metadata at minimal cost — blobs are only fetched when files are actually checked out.

**Path D location rule (MANDATORY):** Clone directly into the unified skill cache — never use `/tmp`, `$TMPDIR`, or any ad-hoc directory.

Create a temp directory under the **unified skill cache**:

**Bash (macOS / Linux):**
```bash
if [[ "$(uname -s)" == "Darwin" ]]; then
  CACHE_ROOT="$HOME/Library/Caches/mythril-skills-cache"
else
  CACHE_ROOT="${XDG_CACHE_HOME:-$HOME/.cache}/mythril-skills-cache"
fi
CACHE_DIR="$CACHE_ROOT/github-code-review-pr"
mkdir -p "$CACHE_DIR"
REVIEW_DIR=$(mktemp -d "$CACHE_DIR/XXXXXXXX")
gh repo clone <owner/repo> "$REVIEW_DIR" -- --filter=blob:none --sparse
cd "$REVIEW_DIR"
```

**PowerShell (Windows):**
```powershell
$CACHE_ROOT = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "mythril-skills-cache"
$CACHE_DIR = Join-Path $CACHE_ROOT "github-code-review-pr"
New-Item -ItemType Directory -Force -Path $CACHE_DIR | Out-Null
$REVIEW_DIR = Join-Path $CACHE_DIR ([System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory -Force -Path $REVIEW_DIR | Out-Null
gh repo clone <owner/repo> "$REVIEW_DIR" -- --filter=blob:none --sparse
Set-Location $REVIEW_DIR
```

Now the repo is cloned with sparse checkout already enabled (cone mode). The working directory contains only root-level files (README.md, AGENTS.md, pyproject.toml, etc.) — no subdirectories are checked out yet.

Add directories needed for review based on the PR metadata from Step 2:

**1. Directories containing PR-modified files** — extract from the `files` list in Step 2a:
```bash
git sparse-checkout add src/components src/utils tests/unit
```
Only add the directories that contain files changed in the PR. This pulls just those directory trees.

**2. Directories for related files** — if the diff references imports or base classes from other paths, add those too:
```bash
git sparse-checkout add src/types src/shared
```

Now checkout the PR branch to get the PR's version of those files:
```bash
git fetch origin <headRefName>
git checkout <headRefName>
```

(Use `git checkout <headRefName>` directly — same reasoning as Path A/B/C: more reliable than `gh pr checkout` on Enterprise hosts.)

After review, delete the temp directory (see Step 7).

### Path selection summary

| Path | Condition | Speed | Context depth |
|---|---|---|---|
| A | Already inside target repo | Instant | Full repo |
| B | Repo found in shared cache | Fast (just `fetch` two branches) | Full repo |
| C | Repo not cached, ≤ 100 MB — clone to shared cache | Moderate (blobless clone, reusable) | Full repo |
| D | Repo > 100 MB (user chose D), or Path C clone failed | Moderate (blobless sparse clone, disposable) | Targeted files only |
| DIFF_ONLY | User explicitly chose no clone (large repo) | Instant | PR diff + metadata only |

Note: Path B and C can temporarily degrade to diff-only mode if branch fetch/checkout fails. This is allowed only when the fallback sequence above is attempted and logged. When the repo exceeds the size threshold, the user is asked to choose between C, D, and diff-only (see "Handling exit code 10" in Step 2). Path C falls back to Path D if `repo_manager.py sync` fails at runtime.

## Step 4: Gather Repository Context

### For Path A, Path B, and Path C (full repo available)

The full repo is available locally. Read files directly — git auto-fetches blob content on demand (blobless clone).

#### 4a. Project structure overview

```bash
git ls-tree -r --name-only HEAD | head -200
```

This reveals the project's module organization, naming conventions, and architecture.

#### 4b. Coding conventions and config files

Read key project files to understand coding standards. Prioritize by relevance to the changed files' languages.

**AI agent instruction files** (highest priority — these define project conventions explicitly):

| File | Tool |
|---|---|
| `AGENTS.md` | Cross-tool standard (Codex, Cursor, Copilot, Amp, Windsurf, Devin) |
| `CLAUDE.md` | Claude Code |
| `GEMINI.md` | Gemini CLI |
| `.github/copilot-instructions.md` | GitHub Copilot |
| `.cursorrules` / `.cursor/rules/` | Cursor |
| `.windsurfrules` / `.windsurf/rules/` | Windsurf |

**Project and build config files**:

| File | Purpose |
|---|---|
| `README.md` | Project overview, setup instructions |
| `CONTRIBUTING.md` | Development guidelines, contribution rules |
| `pyproject.toml` / `setup.cfg` | Python project config, linting rules |
| `package.json` | Node.js project config, scripts, lint config |
| `.editorconfig` | Editor formatting rules |
| `.eslintrc.*` / `biome.json` | JS/TS linting rules |
| `Makefile` / `Justfile` | Build conventions |
| `Cargo.toml` | Rust project config |
| `go.mod` | Go module config |
| `.clang-format` / `.clang-tidy` | C/C++ formatting rules |

**Constraints:**
- Read at most 3-5 config files — prioritize the ones most relevant to the changed files' languages
- Skim only; skip files larger than ~50KB

#### 4c. Full content of modified files

Read the **full current content** of PR-modified files to understand complete context around changes. Use a **change-volume driven** strategy instead of a fixed file count:

- **> 50 lines changed** (additions + deletions): Must read full file — major changes require complete context
- **5-50 lines changed**: Read full file — surrounding code is important for correctness judgment
- **< 5 lines changed**: The diff alone may suffice; skip full read unless the change is in a critical path (e.g., security, auth, financial logic)
- **New files**: Always read in full (subject to the size limit below)
- **Skip binary files** and **very large files** (>100KB) — only use the diff for those. This size limit applies to ALL files, including new files

#### 4d. Related files not in the diff (targeted)

If the diff references imports, base classes, interfaces, or function calls from files NOT in the PR:
- Just read the file directly — git auto-fetches the blob on demand
- Read **3-5** related files that are strictly necessary to validate the correctness of the changes
- Only read files where understanding their content directly affects review quality (e.g., interface definitions, base classes, callers of changed functions, related tests)

### For Path D (blobless clone with sparse checkout)

#### 4a. Project structure overview

Even with sparse checkout, the **tree objects are fully available**:

```bash
git ls-tree -r --name-only HEAD | head -200
```

This reveals the full project structure without downloading any file content.

#### 4b. Coding conventions and config files

Same as above — root-level files are already checked out via `--sparse` on clone.

#### 4c. Full content of modified files

Same change-volume driven strategy as above. All modified files are already checked out via sparse checkout of their directories.

#### 4d. Related files not in the diff (targeted)

If the diff references files from directories NOT already in sparse checkout, add them:
```bash
git sparse-checkout add <directory>
```
Git will auto-fetch the needed blobs. Read 3-5 related files that are strictly necessary to validate correctness.

### Accessing other repositories during review

If the user provides a URL to another repository during the review (e.g., a backend API repo to verify schema compatibility), **do NOT clone it yourself** into the review cache. Instead, delegate to the `git-repo-reader` skill — it will clone the repo into the shared cache and let you read it. This is better because:
- The clone is cached and reusable across sessions
- It avoids duplicating clone logic inside this skill
- Future reviews or questions about that repo will hit the cache instantly

If the `git-repo-reader` skill is not available, fall back to a blobless clone in the review cache (same as Path D).

### For DIFF_ONLY (user chose no clone)

When the user explicitly chose diff-only mode for a large repo, no local repo is available. The review relies entirely on the PR metadata and diff fetched in Step 2.

- **4a. Project structure**: Not available. Note this limitation in the review.
- **4b. Coding conventions**: Not available from local files. Infer conventions from the diff content itself (naming patterns, formatting style, language idioms visible in the changed code).
- **4c. Modified file content**: Use only the diff hunks from `PR_DIFF_PATH`. Full file content is not available.
- **4d. Related files**: Not available. Flag in the review that cross-file validation was not possible due to diff-only mode.

**MANDATORY**: Explicitly state in the review output (Section 2 — Repository Context Analysis) that the review was performed in diff-only mode and that full repo context was not available. This is a known limitation the user accepted.

## Step 5: Detect Language

Analyze user's input to determine review output language:
- Contains **any** Chinese characters → Chinese review (even if mixed with English, e.g., "review 一下这个PR" → Chinese)
- Only English (zero Chinese characters) → English review

## Step 6: Perform Code Review

### Evidence certainty rules (MANDATORY)

- Separate **confirmed findings** from **potential risks**.
- A confirmed finding must be directly supported by evidence from the diff, file content, or PR metadata.
- A potential risk must be labeled as such (e.g., "Potential risk") and include one concrete validation step.
- Do NOT present speculative runtime/CI behavior as established fact without direct evidence.

### Finding classification format (MANDATORY)

The final review must separate findings into two explicit groups:

- `Confirmed Findings` / `已确认问题`: only evidence-backed issues
- `Potential Risks` / `潜在风险`: hypotheses that still need validation

Classification constraints:
- Every item must be in exactly one group (never both)
- Every `Potential risk` item must include one concrete validation step
- If no confirmed issues exist, explicitly write `Confirmed Findings: none`
- If no potential risks exist, explicitly write `Potential Risks: none`

### Internal consistency check (MANDATORY before final output)

Run this final check before sending the review:

1. Remove any item that was later disproven during the same review run.
2. Do not keep self-contradicting pairs like "Issue: X" and later "No issue for X".
3. If investigation shows "not an issue", keep only the final conclusion and delete the earlier suspicion from issue lists.
4. Ensure the verdict (`Approve` / `Request Changes` / `Comment`) matches the final issue severity.

### Review output structure (MANDATORY — use exactly these 6 sections)

**Do NOT create ad-hoc section structures** like "Strengths / Issues / What's Working Well". The review MUST use exactly the 6 sections defined below, in order. Every review must include all 6 sections.

**RECOMMENDED: Generate review skeleton first.** Before filling in review content, generate a deterministic skeleton to reduce structural drift:

```bash
RUN_DIR="$(dirname "<RUN_MANIFEST>")"
REVIEW_DRAFT_PATH="$RUN_DIR/review_draft.md"
python3 scripts/review_template_builder.py \
  --manifest <RUN_MANIFEST> \
  --output "$REVIEW_DRAFT_PATH" \
  --language en
```

Use the generated skeleton as the structural foundation for your review. Fill in each `[fill]` placeholder with evidence from the PR metadata, diff, and repo context. Do NOT invent a different section structure.

**CRITICAL: Do NOT send the review to the user yet.** After completing all 6 sections, you MUST proceed to Step 7 (cleanup + gate) before presenting any review output. Write the review content to a temp file and continue to Step 7.

Structure the review into these sections:

### If Chinese review requested:

#### 1. PR 概览
- PR 的目的和动机（基于标题、描述、分支名）
- 变更规模：X 个文件，+Y / -Z 行
- 涉及的主要模块和功能领域
- 若 PR 含截图/图片证据，补充 **图片证据摘要**：逐张说明图片内容、关键信息、与代码变更的对应关系

#### 2. 仓库上下文分析
- 项目技术栈（语言、框架、工具链）
- 项目的编码规范和风格（基于 config 文件和已有代码推断）
- 此 PR 是否符合项目整体风格和架构模式

#### 3. 代码质量 & Clean Code 评价
- 全面评估变更的代码风格、命名、注释、可读性、可维护性、设计架构、模块解耦、重复代码等
- 特别关注：变更是否与项目现有代码风格一致
- 发现任何易错写法、不安全代码、低效实现、反模式或不符合最佳实践的地方要具体列出
- 指出被修改的具体位置与问题描述（文件名、代码片段，或足够明确的定位描述）
- 提出详细的修复/重构/优化建议，并解释理由

#### 4. 潜在的重大问题和风险
- 检查代码逻辑是否存在难以发现的 bug、异常未处理、未校验边界条件、性能瓶颈、安全隐患等
- 检查是否有遗漏的修改（如：改了接口但没改调用方，改了 schema 但没改迁移）
- 指出这些疑点，并简单说明为何值得关注

#### 5. 增量建议
- 给出进一步增强代码质量、工程可维护性、测试覆盖的建议
- 建议是否需要补充测试、文档、类型声明等
- 如果 PR 描述缺失或不清晰，建议改进 PR 描述

#### 6. 总结评价
- 给出整体评价：**Approve** / **Request Changes** / **Comment**
- 简要总结关键发现和建议优先级

### If English review requested:

#### 1. PR Overview
- Purpose and motivation of the PR (based on title, description, branch names)
- Change scope: X files changed, +Y / -Z lines
- Primary modules and functional areas affected
- If screenshots/images are present, include a **Visual Evidence Summary**: what each image shows, key observed values/events, and how it maps to PR claims

#### 2. Repository Context Analysis
- Project tech sta

…(truncated)
