Loading...
Loading...
Generate an interactive PR review walkthrough as an HTML page. Fetches PR data via gh API, categorizes files into core vs mechanical changes, adds reviewer annotations, and renders diffs with moved-code detection. Use when the user pastes a GitHub PR URL and asks for a review, walkthrough, or summary, or says "review this PR".
npx skill4agent add beremaran/pstack pr-review-canvasgh apigh api repos/{owner}/{repo}/pulls/{number} --jq '{title, body, user: .user.login, state, additions, deletions, changed_files, base: .base.ref, head: .head.ref}'
gh api repos/{owner}/{repo}/pulls/{number}/files --paginate --jq '.[] | {filename, status, additions, deletions, patch}'
gh api repos/{owner}/{repo}/pulls/{number}/comments --jq '.[] | {user: .user.login, body, path, line}'<body>.bp-section<pre><div class="file-card">
<div class="file-hdr" onclick="toggle(this)">
<span class="fname">retryClient.ts</span>
<div class="fstats"><span class="pill add">+173</span><span class="pill del">−11</span><span class="chev open">▶</span></div>
</div>
<div class="file-body open">
<div class="file-note">
<strong>What this does in plain English:</strong>
<pre style="margin-top:8px;color:var(--text);font-size:12px;line-height:1.6;">
fetch(url):
if circuit breaker is open → fail fast
retry up to N times:
try fetch with timeout
on success → close circuit breaker, return
on retryable error → wait (exponential backoff + jitter)
on non-retryable error → throw
circuit breaker records failure</pre>
</div>
<div class="bp-section" style="margin:0;border:0;border-radius:0;">
<div class="bp-hdr" onclick="toggleBP(this)">
<span>Show full implementation (+173 lines)</span><span class="chev">▶</span>
</div>
<div class="bp-body"><div data-diff="retryClient"></div></div>
</div>
</div>
</div>| Class | Purpose |
|---|---|
| Page header |
| Stat badges (+N, -N, N files) |
| Centered content wrapper (max 900px) |
| Summary/TL;DR box |
| Section heading with bottom border |
| Inline code reference (mono, blue, dark bg) |
| Collapsible file card (use |
| Sticky reviewer annotation inside a file card |
| Collapsed boilerplate card (use |
| Note inside a boilerplate card |
| Review checklist box |
| Function | Usage |
|---|---|
| Toggle a |
| Toggle a |
| Render a unified diff. |
| HTML-escape a string |
data-diff<div data-diff="KEY"></div><script id="pr-diffs-json" type="application/json">template.html</script>json.dumps(...)<script>jqgh api repos/{owner}/{repo}/pulls/{number}/files --paginate \
--jq '[.[] | {key: (.filename | gsub("[^a-zA-Z0-9]"; "_")), value: (.patch // "")}] | from_entries' \
> /tmp/pr-patches-{number}.jsontemplate.htmlpython3 <<'PY'
import json
from pathlib import Path
patches = json.loads(Path('/tmp/pr-patches-{number}.json').read_text())
html = Path('/tmp/pr-review-{number}-body.html').read_text()
css = Path('styles.css').read_text()
js = Path('renderer.js').read_text()
tmpl = Path('template.html').read_text()
# Prevent literal </script> from terminating HTML script tags early.
safe_json = json.dumps(patches).replace('<', '\\u003c').replace('>', '\\u003e').replace('&', '\\u0026')
out = (
tmpl.replace('/* INJECT_CSS */', css)
.replace('/* INJECT_JS */', js)
.replace('<!-- INJECT_BODY -->', html)
.replace('{"__PR_DIFFS_PLACEHOLDER__":true}', safe_json)
)
Path('/tmp/pr-review-{number}.html').write_text(out)
PYdata-diff<div data-diff="path_to_file_ts"></div><head>renderDiff(target, lines)<script>targetlines<style><script><body>/tmp/pr-review-{number}-body.html/tmp/pr-patches-{number}.jsonjqcd /tmp && python3 -m http.server 8432 --bind 127.0.0.1http://127.0.0.1:8432/pr-review-{number}.htmlcd /tmp--directory /tmpcd /tmp#1a1a1avar(--warning)var(--success)var(--danger)var(--accent)position: sticky; top: 0top: 35px.file-body.open