pr-review-canvas について
GitHub の pull request を対話型 HTML レビューにするスキルです。要約、ファイルへの注釈、差分表示、任意の図を通じて、レビュー担当者の変更理解を支援します。
主な機能
- 01PR メタデータとファイルの取得
- 02変更内容の要約
- 03注釈付きファイル区分の表示
- 04差分の描画と分類
- 05図とチェックリストへの対応
活用シーン
- 01ガイド付き PR レビューの作成
- 02複雑なコード変更の説明
- 03移動コードや定型コードの明示
- 04ローカル対話型レビューの共有
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cursor/ plugins --skill pr-review-canvasインストール後、Claude Code で/pr-review-canvasを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
renderer.jsstyles.csstemplate.html詳細
PR レビューボード
GitHub PR のインタラクティブな HTML レビューを生成します。重要なポイントを同僚が案内してくれるような内容にします。
ワークフロー
1. PR データを取得する
これらの gh api 呼び出しを並列で実行します。
gh 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}'
2. PR を分析して本文の HTML を作成する
差分を読み、PR の内容を把握し、<body> の内容を HTML で直接作成します。表現方法は自由です。レビュアーに PR の内容をわかりやすく説明することが目的です。PR に最も適した構成を選んでください。
一般的な構成(必要に応じて調整してください):
- タイトル、PR 番号、作成者、統計情報を含むヘッダー
- PR の内容を平易な言葉で説明する概要ボックス
- 注釈と差分を含む主要ファイルのセクション
- 機械的な変更や定型的なファイルはデフォルトで折りたたむ
- 最後にレビュー用チェックリスト
次のような内容を追加することもできます:
- 冗長なコードの擬似コードによる要約:アルゴリズムを平易な言葉や短い擬似コードで示し、実際の差分はその下に折りたたんで表示します(
.bp-sectionカードを使い、「完全な実装を表示」というラベルを付けます)。150 行のリトライ/バックオフ/エラー処理コードが、実際には「指数バックオフとサーキットブレーカーを使って取得する」だけの場合などに役立ちます。 - 図(インライン SVG、CDN 経由の mermaid、
<pre>内の ASCII アート) - 変更前と変更後の制御フローを示すフローチャート
- 以前の動作と新しい動作を比較する表
- 警告、質問、注意点を示すコールアウトボックス
- 役立つ場合はインタラクティブなウィジェット
- その他、レビューをわかりやすくするためのもの
擬似コードのパターン例:
<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>
3. 利用可能な CSS クラスと JS ユーティリティ
このスキルディレクトリにある styles.css と renderer.js を読み込んでください。これらには、ダークテーマのツールキットがあらかじめ用意されています。template.html にそのまま挿入してください。
使用できる CSS クラス:
| クラス | 目的 |
|---|---|
.header、.header h1、.header-meta | ページヘッダー |
.pill.add、.pill.del、.pill.files | 統計バッジ(+N、-N、N files) |
.content | 中央寄せのコンテンツラッパー(最大幅 900px) |
.summary | サマリー/TL;DR ボックス |
.section-title | 下線付きのセクション見出し |
.ic | インラインコード参照(等幅フォント、青色、暗い背景) |
.file-card、.file-hdr、.file-body | 折りたたみ可能なファイルカード(.file-hdr に onclick="toggle(this)" を使用) |
.file-note | ファイルカード内の固定レビュアー注釈 |
.bp-section、.bp-hdr、.bp-body | 折りたたまれたボイラープレートカード(onclick="toggleBP(this)" を使用) |
.bp-note | ボイラープレートカード内の注記 |
.verdict | レビューチェックリストボックス |
利用可能な JS 関数:
| 関数 | 用途 |
|---|---|
toggle(hdrElement) | .file-body の開閉 |
toggleBP(hdrElement) | .bp-body の開閉 |
renderDiff(target, diffInput) | 統合差分を描画します。target には DOM 要素、文字列 ID、CSS セレクターを指定できます。diffInput には生のパッチ文字列または行の配列を指定でき、どちらも使用できます。インポートを自動的に除外し、空白のみの変更を折りたたみ、移動したコードを検出します(青/紫の色合いで表示)。 |
esc(string) | 文字列を HTML エスケープします |
差分の描画 — 自動検出には data-diff 属性を使用します。
差分を描画したい箇所に、本文 HTML 内で <div data-diff="KEY"></div> のプレースホルダーを配置してください。レンダラーは DOM の読み込み後にこれらを自動検出し、template.html 内の <script id="pr-diffs-json" type="application/json"> 要素からデータを読み込んで埋め込みます。
重要:パッチ文字列には、改行、バックスラッシュ、引用符に加えて </script> が含まれる場合があります。 json.dumps(...) を使っても、出力を実行可能な <script> に直接貼り付けると、HTML の解析によってタグが途中で終了する可能性があるため不十分です。パッチ文字列を JS/JSON に手動で埋め込まないでください。代わりに、次の安全な方法を使用してください。
- 取得ステップで、エスケープを正しく処理する
jqを使ってパッチを JSON ファイルに保存します。
gh api repos/{owner}/{repo}/pulls/{number}/files --paginate \
--jq '[.[] | {key: (.filename | gsub("[^a-zA-Z0-9]"; "_")), value: (.patch // "")}] | from_entries' \
> /tmp/pr-patches-{number}.json
- 組み立てステップで、Python を使って JSON を
template.htmlに安全に挿入します。
python3 <<'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('', html)
.replace('{"__PR_DIFFS_PLACEHOLDER__":true}', safe_json)
)
Path('/tmp/pr-review-{number}.html').write_text(out)
PY
これにより、有効な JSON と、スクリプト内に安全に埋め込まれた HTML が確実に生成されます。エージェントは本文 HTML を一時ファイルに書き込み、その後 Python ですべてを安全に組み立てます。
差分データのキーは、HTML 内の data-diff 属性値と一致させてください。
<div data-diff="path_to_file_ts"></div>
renderer.js は <head> 内で読み込まれるため、必要に応じてインラインの <script> タグから renderDiff(target, lines) を直接呼び出し、独自の用途に利用することもできます。この関数では、target に DOM 要素、ID 文字列、CSS セレクターを、lines に文字列または配列を指定できます。
これらだけに限定されません。 インラインの <style> ブロックや <script> ブロック、SVG、図など、必要なものを自由に追加できます。あらかじめ用意された部品は時間の節約に役立ちますが、利用を制限するものではありません。
4. 組み立てと配信
-
本文 HTML(
<body>内に入るすべての内容)を/tmp/pr-review-{number}-body.htmlに書き込みます -
上記の手順 3 の
jqコマンドを使って、パッチを/tmp/pr-patches-{number}.jsonに保存します -
上記の手順 3 の Python 組み立てスクリプトを実行します(このスキルディレクトリから styles.css、renderer.js、template.html を読み込み、本文とパッチを安全に挿入して、最終 HTML を書き出します)
-
固定ポートでローカルサーバーを起動します。
cd /tmp && python3 -m http.server 8432 --bind 127.0.0.1これをバックグラウンドで実行し、アプリ内ブラウザーで
http://127.0.0.1:8432/pr-review-{number}.htmlに移動します。固定ポートを使い、
cd /tmpを実行する理由: バックグラウンドのシェルには TTY がないため、Python の起動メッセージ(「Serving HTTP on...」)がバッファーに残り続けます。ポート 0 を使うと、どのポートが選ばれたかを確認できません。また、--directory /tmpも使えますが、cd /tmpのほうが Python のバージョンを問わず確実です。ポート 8432 が使用中の場合は、8433、8434 などを試してください。
差分機能(renderer.js によって自動的に処理されます)
- インポートのみの行を除外します
- 空白のみの変更をコンテキスト行にまとめます
- 移動されたコードブロック(3 行以上連続する行が一箇所で削除され、別の場所に同一の内容が追加されたもの)を検出します -- 赤/緑ではなく青/紫でレンダリングされます
- ほぼ一致するもの(移動 + 小さな編集)には異なる紫の色合いが付きます
スタイルに関する注意
- ダークテーマ: 背景は
#1a1a1a、本文フォントは Inter、コードには IBM Plex Mono - オレンジには
var(--warning)、緑にはvar(--success)、赤にはvar(--danger)、青にはvar(--accent)を使用します - 固定されたファイルヘッダー(
position: sticky; top: 0)と注記(top: 35px)はスクロール中も固定されます - コアファイルはデフォルトで展開され(
.file-body.open)、機械的なファイルは折りたたまれます