mcpField
P
pr-review-canvas

pr-review-canvas

github.com/poteto测试与质量12950 次安装21 周前更新
代码仓库

关于 pr-review-canvas

将 GitHub 拉取请求数据和差异整理成交互式 HTML 评审页面的技能。通过摘要、注释、可折叠文件和差异呈现,帮助评审者理解更改。

主要功能

  1. 01用 gh 获取 PR 信息、文件和评论
  2. 02生成说明性 HTML 评审页面
  3. 03添加文件注释并组织差异
  4. 04折叠样板内容区块
  5. 05渲染差异并识别移动代码

适用场景

  1. 01评审复杂的拉取请求
  2. 02分享结构化的代码更改说明

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/poteto/plugins --skill pr-review-canvas

安装后在 Claude Code 中输入/pr-review-canvas确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

renderer.js
styles.css
template.html

详细介绍

PR 评审画布

生成一份交互式 HTML,评审 GitHub PR,仿佛有位同事正带你了解其中的重点。

工作流

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,并直接以 HTML 编写 <body> 内容。你可以充分发挥创意——目标是向评审者清楚地说明这个 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">&minus;11</span><span class="chev open">&#9654;</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">&#9654;</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 个文件)
.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> 占位元素。页面加载后,渲染器会自动查找这些元素,并从 template.html 中的 <script id="pr-diffs-json" type="application/json"> 元素填充内容。

重要:补丁字符串除了包含换行符、反斜杠和引号外,还可能包含 </script>。 即使使用 json.dumps(...),如果将原始输出粘贴到可执行的 <script> 中也不够安全,因为 HTML 解析可能会提前结束标签。切勿手动将补丁字符串嵌入 JS/JSON。请改用以下安全方法:

  1. 在获取步骤中,使用 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
  1. 在组装时,使用 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 脚本中。Agent 会将正文 HTML 写入临时文件,然后由 Python 安全地组装所有内容。

差异数据的键应与 HTML 中 data-diff 属性的值匹配:

<div data-diff="path_to_file_ts"></div>

由于 renderer.js 在 <head> 中加载,如果需要用于自定义场景,也可以直接从行内 <script> 标签调用 renderDiff(target, lines)。该函数接受 DOM 元素、ID 字符串或 CSS 选择器作为 target,并接受字符串或数组作为 lines。

你不必局限于这些内容。 可以添加自己的行内 <style> 块、<script> 块、SVG、图表或其他任何内容。预先构建的组件能节省时间,但不会限制你。

4. 组装并提供服务
  1. 将正文 HTML(即所有放在 <body> 内的内容)写入 /tmp/pr-review-{number}-body.html

  2. 使用上面第 3 步中的 jq 命令,将补丁保存到 /tmp/pr-patches-{number}.json

  3. 运行上面第 3 步中的 Python 组装脚本(从此技能目录读取 styles.css、renderer.js、template.html,安全注入正文和补丁,并写入最终 HTML)

  4. 在固定端口上启动本地服务:

    cd /tmp && python3 -m http.server 8432 --bind 127.0.0.1
    

    在后台运行此命令,然后在应用内浏览器中打开 http://127.0.0.1:8432/pr-review-{number}.html。

    为何使用固定端口并执行 cd /tmp: 后台 Shell 没有 TTY,因此 Python 会无限期缓冲其启动消息(“Serving HTTP on...”);使用端口 0 就永远无法得知分配了哪个端口。并且,虽然 --directory /tmp 可用,但 cd /tmp 在不同 Python 版本中更可靠。如果端口 8432 已被占用,请尝试 8433、8434 等。

Diff 功能(由 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),机械生成的文件默认折叠

评论

支持 Markdown