关于 pr-recap
pr-recap 是为代码审查者撰写拉取请求摘要的技能,以 GitHub 评论形式发布并核验事实。它涵盖差异分析、可选示意图绘制和评论维护流程。
主要功能
- 01基于差异核验说法
- 02面向审查者的叙述模板
- 03可选 PNG 机制示意图
- 04GitHub 评论更新流程
- 05核对提交 SHA 与链接
适用场景
- 01概述涉及行为变化的拉取请求
- 02新提交后更新摘要
- 03发布前核验摘要内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mdsmithaustin/ pstack --skill pr-recap安装后在 Claude Code 中输入/pr-recap确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
CREDITS.mdreferences/block.mdreader.mdsketch.mdreferences/examples/bars.jsoncheck.jsonflow.jsonrewire.jsonto-scale.jsonscripts/attachment.mjsscripts/sketch/bun.lockdraw.mjspackage.jsonsketch.mjsscripts/sketch/vendor/OFL-Excalifont.txt详细介绍
PR 摘要
为审查 PR 的人员写一份摘要。审查者应能从首屏了解这项变更为何存在、改了什么,以及如何实现。摘要应指向差异内容,不能代替阅读差异。
在本地运行此工作流。摘要是一条 PR 评论:GitHub Markdown 加一张上传到 PR 自有主机的 PNG 图片。需要 gh,绘图需要 Bun 和随附的绘图工具。不会向任何其他主机发布内容。
判断 PR 是否需要摘要
如果差异中没有审查者必须追踪的行为变化,就跳过摘要。例如,版本固定及其哈希值、拼写或措辞修正、仅格式变更、仅锁文件变更,以及单个提交的回滚。即使跨越主版本,版本固定也无需摘要,因为版本固定没有可绘制的机制。
对于跳过摘要的 PR,不要在 GitHub 上发布任何内容。在回复中写一行:
Recap skipped for PR <n>: <what the diff is>, <why it needs no recap>.
否则,写摘要。每次推送导致差异发生变化后,都要重新撰写摘要;须等新提交已发布,且在其上运行的任何门禁都已得出结果。
收集事实
- 从 origin 的 fetch URL 解析出
<host>/<owner>/<repo>。每次调用gh pr时都传入-R <host>/<owner>/<repo>。每次调用gh api时都传入--hostname <host>。 - 使用
gh pr view <n> -R <host>/<owner>/<repo> --json number,title,body,baseRefName,baseRefOid,headRefOid,files,commits,reviews,comments,closingIssuesReferences读取 PR 记录。记录baseRefOid和headRefOid。摘要中的每项事实都必须描述这两个 SHA 之间的差异。 - 使用
gh pr diff <n> -R <host>/<owner>/<repo>阅读完整差异。阅读你要描述的每个代码块周围的代码、其调用方,以及所有已关联的问题。不要对未读过的文件作出任何断言。 - 阅读已发布的审查意见、已发布的评审结论评论,以及 head 上的 CI 运行记录。未在 PR 上发布的评审结论不能作为证据。
- 根据这些记录构建每项断言。会话记忆可以帮助你找到记录,但不能证明断言成立。
撰写前先梳理核心内容
在笔记中回答以下三个问题,每个答案都要依据一条记录:
- 原因。 在此 PR 之前出了什么问题,或缺少什么?指出人们实际看到的具体症状,例如路由丢失、结论错误,或合并时没有监视器。“改进 X”不是原因。
- 内容。 PR 更改的一个机制。如果更改了多个机制,就选出原因所依赖的那个。
- 方式。 修复问题的具体改动,例如调整顺序、新增角色、新增检查,或重新连接调用。
不要编造理由。只有在差异、PR 正文、已关联的问题、提交消息或已发布的审查意见中有依据时,才能说明原因。其他原因一律标注为 (a guess)。
撰写评论
复制此区块中的模板。首屏必须恰好包含三个部分:
- 核心内容。 最多两句话,不超过 60 个单词。第一句说明原因,并指出具体症状。第二句说明改了什么以及如何实现,措辞应与审查者在差异中看到的内容一致。不要使用分号、文件列表,也不要列出绘图已展示的数量。
- 示意图。 展示一张描绘已更改机制的图片。参见示意图。
- 未涵盖内容。 用一行指出 PR 无法证明的内容,例如未测量的运行时表现、未经测试的路径,或已发布评审结论中的注意事项。
其他所有内容都放在它们之后的一个折叠式 <details> 区块中:风险说明、SHA、阅读顺序、简短的变更区域列表,以及证明材料链接。不要使用表格。
绝不要从差异中复制秘密、令牌或看起来像凭据的值。应以 <redacted> 替代。
对摘要中的句子使用 technical-writing 技能,然后使用 unslop 技能。
绘制草图
在技能目录之外,选择一个可写的工作文件夹,用于存放 scene.json、sketch.png、story.txt 和 recap.md。从该文件夹运行渲染、读取和发布命令。将 <skill> 替换为此技能目录的绝对路径。
按照草图操作。简而言之:从其目录中选一幅图,在工作文件夹中编写 scene.json,使用 bun "<skill>/scripts/sketch/sketch.mjs" scene.json sketch.png 渲染,修复它报告的所有问题,然后在使用前亲自查看 PNG。
如果运行时没有 Bun,或只有较旧版本,请按照草图中的 npx --allow-scripts=bun [email protected] 方式渲染。如果该方式也失败,或运行时无法显示图片,则跳过草图。发布故事、“Not covered”行和详情块,并在回复中说明已跳过草图及原因。不要用 Mermaid 图或表格替代。
发布前检查每项声明
- 列出回顾中的每个事实句子和草图中的每个标签。
- 根据 diff 的代码块或 SHA 固定的行逐一核查。标记为已验证、已更正、不受支持或无法验证。
- 修正每项已更正的声明。删除每项不受支持或无法验证的声明,或将其改写为猜测。若标签有变化,重新渲染草图。
- 检查每个链接。将源代码链接固定到记录的 head SHA。Review 和 CI 链接必须指向 PR 上的记录,CI 运行必须对应于该 head。评论中绝不可包含机器人 review 的 URL,包括代码跨度中的 URL。回顾不会对 review 的发现进行分类,watch-pr 和 merge-gate 也绝不会将回顾视为已确认 review。
- 再次读取
headRefOid。如果它发生变化,请根据新的 diff 重新整理回顾。
将标记保留在笔记中,不要放进评论。在回复中报告计数。
发布或更新评论
将回顾写入同一工作文件夹中的 recap.md,即使跳过草图也要写入。包含草图时,图片行格式为 。所有命令都从该文件夹运行。attachment.mjs 的路径为 <skill>/scripts/attachment.mjs。使用 bun 或 node 运行它。使用 --host 传入 PR 所属的主机。
评论会在起始标记后的下一行记录草图:``。下次刷新时会读取该记录;如果草图没有变化,则重用已上传的图片。不会记录评论 ID,因为评论无法保存自己的 ID。通过起始标记和作者查找你的回顾评论。
没有草图时,执行步骤 1,跳过步骤 2 和 3,并运行 gh api --hostname <host> -X PATCH "repos/<owner>/<repo>/issues/comments/<current>" -f body="$(cat recap.md)",将 recap.md 写入 current。如果没有现有评论,则运行 gh pr comment <n> -R <host>/<owner>/<repo> --body-file recap.md。从评论中取得 <keep>,然后转到步骤 4。
- 按时间从早到晚列出你的回顾评论。最后一条为
current。如果没有,则转到步骤 3。
me=$(gh api --hostname <host> user --jq .login)
gh api --hostname <host> --paginate "repos/<owner>/<repo>/issues/<n>/comments" \
--jq ".[] | select(.user.login == \"$me\" and (.body | startswith(\"\"))) | .id"
gh api --hostname <host> "repos/<owner>/<repo>/issues/comments/<current>" --jq .body > old.md
- 重用已上传的图片。只有当
old.md记录了此sketch.png的哈希时,reuse才会输出记录的 URL。然后,stamp会将该 URL 和新的记录写入新正文,再由 API 将其写入current。不运行 attach。
url=$(bun "<skill>/scripts/attachment.mjs" reuse --host <host> sketch.png old.md) &&
bun "<skill>/scripts/attachment.mjs" stamp --host <host> sketch.png recap.md "$url" > body.md &&
gh api --hostname <host> -X PATCH "repos/<owner>/<repo>/issues/comments/<current>" -f body="$(cat body.md)"
将 <current> 作为 <keep>,然后转到步骤 4。如果 reuse 以状态码 1 退出,说明草图已更改或评论中没有记录,因此需要上传。
- 上传草图。先尝试一次 CLI:
gh pr comment <n> -R <host>/<owner>/<repo> --body-file recap.md --attach sketch.png
它会输出新评论的 URL。其中的 #issuecomment-<id> 即为 <keep>。使用 gh api --hostname <host> "repos/<owner>/<repo>/issues/comments/<keep>" --jq .body > new.md 将该评论正文读取到 new.md,运行 stamp --host <host> sketch.png new.md > body.md,再使用 body.md 对 <keep> 执行 PATCH。转到步骤 4。
如果 gh 拒绝上传,或没有 --attach,则 CLI 无法上传到此主机。将其归类为 CLI 限制即可,不要重复尝试。GHES 可能会拒绝上传,但这并不表示该仓库禁止附件。
-
用户已授权手动上传。 向用户提供
sketch.png的绝对路径。请他们将它拖入网页界面中 PR 的评论框,但不要提交,然后把出现的 URL、或<img>标记粘贴回来。将粘贴内容写入paste.txt,然后运行此代码块:url=$(bun "<skill>/scripts/attachment.mjs" url --host <host> paste.txt) && bun "<skill>/scripts/attachment.mjs" stamp --host <host> sketch.png recap.md "$url" > body.mdurl接受这些标记形式,并输出纯 URL;对于其他主机或路径,则以状态码 1 退出。代码块的状态码 1 可能来自任一命令,因此请查看错误信息。no https://<host>/user-attachments/assets/<uuid> URL found来自url,表示粘贴的内容不是此主机上的附件。将其视为无法上传,不要猜测 URL,并采用未获授权的路径。no <host> attachment image in the body; pass the attachment URL to replace ./sketch.png或the body must start with来自stamp,表示recap.md存在编写错误。修正recap.md,然后重新运行代码块。代码块以状态码 0 退出时,用body.md执行 PATCH,更新current(<keep>为current);如果没有current,则用gh api --hostname <host> -X POST "repos/<owner>/<repo>/issues/<n>/comments" -f body="$(cat body.md)"执行 POST,并将返回的id作为<keep>。 -
未获授权。 从
recap.md中移除图片行。用该正文覆盖current;如果没有current,则发布该正文,并将其设为<keep>。报告upload unavailable: <the gh error>。
- 删除你发布的其他所有 recap,让 PR 只保留一个。
me=$(gh api --hostname <host> user --jq .login)
gh api --hostname <host> --paginate "repos/<owner>/<repo>/issues/<n>/comments" \
--jq ".[] | select(.user.login == \"$me\" and (.body | startswith(\"\")) and .id != <keep>) | .id" |
while read -r id; do gh api --hostname <host> -X DELETE "repos/<owner>/<repo>/issues/comments/$id"; done
- 检查已保存的评论,统计你的 recap 数量,然后确认图片是否正常显示。分别报告上传、已保存的评论和图片显示这三项结果。
me=$(gh api --hostname <host> user --jq .login)
gh api --hostname <host> --paginate "repos/<owner>/<repo>/issues/<n>/comments" \
--jq ".[] | select(.user.login == \"$me\" and (.body | startswith(\"\"))) | .id" | grep -c .
gh api --hostname <host> "repos/<owner>/<repo>/issues/comments/<keep>" --jq .body > saved.md
bun "<skill>/scripts/attachment.mjs" check --host <host> --head <head SHA, 8 characters> saved.md
curl -s -o /dev/null -w '%{http_code} %{content_type}' -L -H "Authorization: Bearer $(gh auth token --hostname <host>)" "<url that check printed>"
check 会确认起始标记、结束标记、head SHA,以及此主机上的图片(两种形式均可)。如果存在记录,它会检查正文中的记录 URL 是否为图片 URL。它不要求必须有记录,也不验证记录的哈希值。阅读错误信息,确定哪项检查未通过。计数必须输出 1。如果输出其他数字,表示步骤 4 失败,请重新运行该步骤。获取操作必须输出 200 image/png。404 表示此请求无法获取该资源。重复使用的 URL 获取失败时,视为记录已失效,并从步骤 3 重新上传。未登录状态下的获取操作可能会在上传有效时失败,因此不能据此得出任何结论。没有草图时,使用 --no-image 运行 check;此时只检查标记和 head SHA,并跳过获取操作。
编辑器生成的 <img width=... alt=... src="..."> 标记可通过 url 和 check 的检查。编写 recap 时,使用 Markdown 图片形式,并采用代码块中的替代文本。stamp 会在其读取的正文中保留图片形式。将图片托管在仓库中或作为发行资源,不属于此技能的范围。
将其发布为 PR 评论,切勿放入 PR 正文。在由 no-mistakes 管控的仓库中,PR 正文由该门禁负责。
回复
- 评论 URL 和 recap 所描述的 head SHA,或跳过说明。
- 使用了哪幅目录绘图,或跳过草图的原因。
- 声明核查的数量:已验证、已更正,以及作为猜测已删除或重写的数量。
CREDITS.md 列出了此技能改编自的来源。