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:
- See
/pull/URL + review intent → trigger skill - Run
review_runner.py prepare <URL>→ letghsucceed or fail - If
ghfails → report the error and suggestgh 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 curlmust 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-prto verify dependencies
Security — MANDATORY rules for AI agents
- NEVER echo, print, or log the values of any environment variable containing credentials (
GH_TOKEN,GITHUB_TOKEN, etc.). Do NOT run commands likeecho $GH_TOKENorprintenv GITHUB_TOKEN— even for debugging. - NEVER pass token/credential values as inline CLI arguments or env-var overrides.
ghreads credentials from its own config — just runghcommands directly. - When debugging auth errors, rely solely on
gh auth statusoutput andgherror messages. Do NOT attempt to verify tokens by reading or printing them. - 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.ymlor anyghauth config file - Any command that outputs a password, token, or secret value from any credential store
- NEVER use extracted credential values in commands. Do NOT manually construct authenticated requests (e.g.
curl -H "Authorization: token <value>"). TheghCLI handles all authentication internally — usegh apifor API calls instead ofcurlwith 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 bereview_runner.py prepare <URL_or_NUMBER>. - Do not run host-based branching logic first.
- Do not run
gh auth statusas a pre-check gate before the firstgh pr view/gh pr diffcall.
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:
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:
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:
- Fetches PR metadata (
gh pr view) and diff (gh pr diff) exactly once - Runs
path_select.py(A → B → C → D selection with[PATH-CHECK]/[PATH-SELECTED]trace) - When neither A nor B matches, queries repo size via
gh apito 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)
- Repo ≤ 100 MB → Path C: clone into shared cache via
- Handles checkout with fallback (
pull/<PR>/head) - 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 filePR_DIFF_PATH=<path>— saved PR diff fileCLEANUP_LOG_PATH=<path>— where to tee cleanup output in Step 7aREVIEW_TEXT_PATH=<path>— where to write the review text in Step 7bSELECTED_PATH=A|B|C|D|DIFF_ONLY— which path was selectedREPO_WORKDIR=<path>— local repo directory (empty if diff-only)PR_STATE=OPEN|CLOSED|MERGED— PR stateCONTEXT_MODE=full_repo|diff_only— whether full repo context is availableCONTEXT_LIMITATION=<message>— explanation when context is limitedCHECKOUT_DONE=true|false— whether the PR branch is already checked out atREPO_WORKDIRNOTE: PR branch is already checked out...— human-readable confirmation (whenCHECKOUT_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=trueREPO_SIZE_MB=<float>— actual repo size in MBTHRESHOLD_MB=<int>— the threshold that was exceededRECOMMENDED_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/diffPR_VIEW_JSON_PATH=<path>— already fetched, reusablePR_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?
- Clone to shared cache (Path C) — larger download, but cached for future reviews
- Sparse clone to temp dir (Path D) — smaller download, only PR-related files, deleted after review
- Diff-only — no clone, review based on PR diff and metadata only (limited context)
After the user chooses, resume the session:
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.
- Read PR metadata from
PR_VIEW_JSON_PATH(do NOT re-rungh pr view) - Read PR diff from
PR_DIFF_PATH(do NOT re-rungh pr diff) - 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 atREPO_WORKDIR. To gather context, just read files there:cd <REPO_WORKDIR> # read files directly — CHECKOUT_DONE=true means the PR branch is already here - 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 fetchedgit checkout <branch>— WRONG, runner already checked outgh 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
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 intentbaseRefName,headRefName— branches involvedfiles— list of changed files withpath,additions,deletionscommits— commit history in the PRcomments,reviews— existing discussion contexturl— used to extractowner/repo
2b. PR diff
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 viewunless 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 diffcall 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:
 - 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):
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):
$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:
curl -fsSL "<image_url>" -o "<local_path>"- 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:
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 thanimage/*→ 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.
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_PATHin a shell variable for cleanup branching. - For Path A, capture
ORIGINAL_BRANCHimmediately. - For Path B, capture
REPO_PATHand resolveDEFAULT_BRANCHonce. - 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
EXITtrap (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:
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:
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:
# 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.
python3 scripts/repo_manager.py sync "<repo-url>"
This command:
- Checks
repo_map.jsonfor an existing entry (handles stale entries automatically) - Clones with
--filter=blob:noneintogit-repo-cache/repos/<host>/<owner>/<repo>/ - Resets the working tree to a clean state on the default branch
- Registers the repo in
repo_map.jsonfor future lookups - Prints the local path to stdout
After the clone, fetch and checkout the PR branches — same as Path B:
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):
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):
$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:
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:
git sparse-checkout add src/types src/shared
Now checkout the PR branch to get the PR's version of those files:
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
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:
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:
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 issuesPotential Risks/潜在风险: hypotheses that still need validation
Classification constraints:
- Every item must be in exactly one group (never both)
- Every
Potential riskitem 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:
- Remove any item that was later disproven during the same review run.
- Do not keep self-contradicting pairs like "Issue: X" and later "No issue for X".
- If investigation shows "not an issue", keep only the final conclusion and delete the earlier suspicion from issue lists.
- 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:
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)