mcpField
W
write-pr

write-pr

github.com/tldraw开发51.1k420 次安装今天更新
代码仓库

关于 write-pr

write-pr 是面向 tldraw/tldraw 的技能,用于撰写以评审者为中心的 PR 标题和说明。它涵盖内容结构、变更报告方式,以及画布交互录屏的记录流程。

主要功能

  1. 01遵循 Conventional Commits 的 PR 标题
  2. 02以评审者为中心的说明
  3. 03错误修复前后对照格式
  4. 04测试计划与发布说明规范
  5. 05画布交互录屏流程

适用场景

  1. 01撰写简洁的 PR 说明
  2. 02按前后行为描述错误修复
  3. 03为画布交互变更添加录屏

安装

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

$npx skills add https://github.com/tldraw/tldraw --skill write-pr

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

包含的文件

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

scripts/
example-scenario.mjs
record-interaction.mjs
touched-comments.ts

详细介绍

编写 pull request

tldraw/tldraw 中 PR 标题和描述的标准。

为审阅者而写(请先读这一节)

这是总规则;下面的一切都服务于它。描述要写给一位了解代码库架构、但没有读过你的代码或 diff 的审阅者。他们的时间才是稀缺资源。描述的任务是提供他们无法从代码中获得的背景框架,然后就别挡路。

默认要短。 几句话的背景框架是常态,而非例外。长度要与改动匹配:一行修复只需一句话;一个新系统则需要更完整的阐述。看起来又长又结构化的描述并不更有价值——往往价值更低,因为它把背景框架埋在脚手架之下。如果审阅者需要自己的 AI 来解读你的 AI 写的描述,那它就失败了。

按从粗到细排序。 把描述组织成倒金字塔,最重要的放最前。读者应该能在任何一点停下,并且带着正确、连贯的理解离开:略读者从开头几行得到目标和动机;权衡方案的人继续读进设计与决策;细读的审阅者再读到具体细节。绝不要让任何人必须读到结尾才知道这个 PR 是做什么的。

大致按以下顺序覆盖内容——每一层都比上一层更详细,且一旦改动不再需要,每一层都可省略:

  • 目标、动机和用例。 为什么做这个改动,为什么是现在,它用来做什么。之前哪里不对或缺失。这一条永远放最前。
  • 更高层次的改动。 解决方案的形态——行为与结构,而不是逐行复述 diff。
  • API 设计与决策。 新增或变更的公共接口、你选择的方案、你排除了什么及原因,以及任何你不确定的地方。你没有呈现出来的决策,审阅者就无法发现。
  • 示例片段与细节。 对于任何涉及 API、数据形态或使用模式的内容,几行前后对比胜过一整段。保持精简。

不要:

  • 复述 diff。不要逐文件走查,不要叙述某个函数做了什么,不要一步步描述代码如何工作。审阅者能读代码。
  • 生成与代码对应的表格或列表——比如只是重复签名的 Method | Description 表格、每个改动文件的清单、自解释名称的术语表。
  • 用繁文缛节填充。为显得详尽而存在的结构就是噪音。

绝不虚构为什么。 动机与权衡必须来自真实意图——提交记录、关联的 issue 或作者本人。如果你不知道某个改动为何做出,或考虑过什么,去问用户;不要猜。 一个自信但编造的动机比没用更糟:它具误导性,而这恰恰是审阅者最需要信任的东西。

PR 标题

使用语义化 PR 标题(Conventional Commits 格式):

<type>(<scope>): <description>
类型
  • feat - 新功能
  • fix - Bug 修复
  • docs - 仅文档
  • refactor - 既不修复 bug 也不添加功能的代码改动
  • perf - 性能改进
  • test - 添加或修复测试
  • chore - 维护任务
范围(可选)

一个描述受影响区域的名词:fix(editor):、feat(sync):、docs(examples):

示例
  • feat(editor): add snap threshold configuration option
  • fix(arrows): correct binding behavior with rotated shapes
  • docs: update sync documentation
  • refactor(store): simplify migration system

PR 正文

Bug 修复和改进类 PR(bugfix 和 improvement 改动类型)使用下面的 before/after 模板。其他所有 PR 使用这一个:

<description paragraph>

### Change type

- [x] `bugfix` | `improvement` | `feature` | `api` | `other`

### Test plan

1. Step to test...
2. Another step...

- [ ] Unit tests
- [ ] End to end tests

### Release notes

- Brief description of changes for users
描述段落

以以下内容开头:“In order to X, this PR does Y.” 遵循本技能顶部的评审者优先规则。

  • X 是让这项工作变得必要的具体情境——某人当时真正想做的事——而不是对 Y 所做事情的复述。“In order to let apps carry undo history across editor rebuilds, this adds an API to carry undo history across editor rebuilds” 是循环论证:X 只是把 Y 换了个说法。把 X 提升一个层次,指向真正的目标,例如“so desktop can reload the editor without losing the user's place, the way HMR preserves component state across a code edit.”
  • 写出真正驱动此事的案例,而不是假设性的情况。如果你为了说明原因而编造示例场景(“e.g. if someone toggled a plugin…”),你是在从已完成的代码反向推导出一个理由——你并没有写下真正促使这项工作的案例。应当从那个案例出发向前推进。
  • 谨防用抽象机制来替代动机。关于 SDK 的真实技术事实(“editor config is fixed at construction time”)解释的是一个约束,而不是为什么有人会在意。继续写下去,直到读者能想象出那个具体停摆或变得可能的事物。
  • 保持具体——避免“improve user experience”之类的模糊表述
  • 在第一段中链接相关 issue
  • 不要指望读者也去读所链接的 issue
Bug 修复与改进类 PR

Bug 修复与改进类 PR 使用固定结构,而不是描述段落。评审者优先规则同样适用;这一结构的存在是为了让评审者无需阅读 diff,就能看到之前哪里有问题(或有所欠缺)、现在做了什么,以及为什么。

This PR fixes a bug where <symptom a user or developer would hit>.

### Before

<what the code did and why that produced the symptom>

### After

<what the code does now>

### Implementation notes

<optional: what exactly changed and how — decisions, trade-offs, anything non-obvious>

### Change type

- [x] `bugfix` | `improvement`

### Test plan

- [x] Unit tests — the new test fails on `main` and passes with this change

### Release notes

- Fix <symptom> | Improve <behavior>

### Code changes

| Section   | LOC change |
| --------- | ---------- |
| Core code | +10 / -2   |
| Tests     | +5 / -0    |
  • 引言——一句话。对于 bug:“This PR fixes a bug where X,”,其中 X 是可观察到的症状,而不是原因或修复。对于改进:“This PR improves X so that Y,”,其中 Y 是用户或开发者现在能做到的事。如果有 issue,请在此处链接。如果该 PR 做了不止一件事,请添加第二句(“It also ...”),而不是列出清单。
  • 之前——之前的行为,以及产生该行为的机制。对于 bug,指出出问题的函数或路径;这里是唯一适合简短描述旧代码如何出错的地方,因为这正是评审者用来核对修复的原因。对于改进,描述用户或开发者之前不得不做什么(或做不到什么),以及代码中是什么导致了这种情况。
  • 之后——之后的行为。以与“之前”相同的详细程度说明代码现在做了什么。注明随之更新的任何规范或文档。
  • 实现说明——可选。仅当存在“之前/之后”这一对无法承载的内容时才包含:你选择了某种方案而非另一种、改动中的某个微妙之处、你有意留下的后续工作。没有什么可说的就完全省略该标题;大多数小型修复都没有可说的。
  • ### Change type / ### Test plan / ### Release notes / ### API changes / ### Code changes 块照常跟在后面。对于 bug 修复,请在测试计划中说明新测试是否在 main 上失败。

tldraw/tldraw#10117、#10118 和 #10119 是这种结构下 bug 修复的实际示例。

修复前后录屏

当某个 bug 修复或改进会改变画布交互的行为时——例如拖拽、调整大小、旋转、裁剪、控制柄或工具手势,而这些行为原本需要审查者在本地运行才能看到——请在 Before 和 After 两个部分各放一段简短录屏。文字描述机制;录屏展示症状及其消失,审查者更信任录屏,而不是对跳动或抖动的描述。对于画布上不可见的更改(数据、API、文档、工具),请跳过此步骤。

scripts/record-interaction.mjs 会针对正在运行的示例应用录制一段脚本化交互,输出为 16:9 MP4。它会隐藏 tldraw 界面元素,注入一个可见光标(无头录屏没有光标),运行场景模块,裁掉准备阶段的帧,并使用 ffmpeg 转码。scripts/example-scenario.mjs 是一个起点;将其复制到临时位置,并把主体改为该 PR 所影响的交互。不要提交场景文件。

它需要路径中有 ffmpeg 以及一个 Playwright Chromium 构建。本仓库禁用了安装脚本,因此浏览器不会通过 pnpm install 下载;请运行一次 pnpm exec playwright install chromium。然后,在仓库根目录下,并确保 pnpm dev 正在为示例应用提供服务:

# After: the current branch
node skills/write-pr/scripts/record-interaction.mjs <scenario.mjs> <scratch>/after.mp4

# Before: main's versions of the files this branch modifies, hot-reloaded by vite
git diff --name-only --diff-filter=M origin/main...HEAD | xargs git checkout origin/main --
sleep 10
node skills/write-pr/scripts/record-interaction.mjs <scenario.mjs> <scratch>/before.mp4
git diff --name-only --diff-filter=M origin/main...HEAD | xargs git checkout HEAD --
git status --porcelain   # must be empty before continuing

将检出范围限制为已修改文件:从 main 检出整个目录也会暂存仅存在于 main 的文件,而在此分支上新增的文件没有可恢复的 main 版本。仅在工作区干净时才使用此方法。

录屏本身的规则:

  • 两个视频使用相同场景。 两个视频必须仅在受测代码上不同,这样审查者才能进行同类比较。先录制 After,确认场景展示了该行为,然后录制 Before。
  • 让症状不可忽视。 选择能夸大 bug 的输入——大幅旋转、长距离拖拽、非正方形形状。15 度的旋转只会让形状移动几个像素,在视频尺度下看起来什么都不是。
  • 附加前检查帧。 生成一张缩略图拼贴(ffmpeg -i before.mp4 -vf "select='not(mod(n\,15))',scale=320:-1,tile=6x3" -frames:v 1 sheet.png)并查看。如果 Before 录屏看起来与 After 完全相同,通常意味着检出没有生效,而不是 bug 很细微。
  • 不要烧录标签。 ### Before 和 ### After 标题已经说明了哪个是哪个。
  • 保持简短。 几秒钟准备,交互,结尾停顿一下。每段不超过十秒。

使用 gh pr create 或 gh pr edit 附加(gh 2.99 或更高版本,在仓库内运行,使用绝对路径)。在正文中将每个文件引用为其所在段落的唯一内容,直接放在它所说明的标题下方,gh 会将引用改写为上传后的资源:

gh pr edit <number> --body-file body.md --attach /abs/path/before.mp4 --attach /abs/path/after.mp4
### Before

<mechanism>

![Before](/abs/path/before.mp4)

### After

<behavior now>

![After](/abs/path/after.mp4)
更改类型
  • 使用 [x] 仅勾选一种类型
  • 删除未勾选的项目
测试计划
  • 如适用,列出手动测试步骤
  • 如果更改无法手动测试,请移除编号列表
  • 勾选所包含测试类型的复选框
发布说明
  • 撰写简要说明,描述面向用户的更改
  • 使用祈使语气:“Add...”、“Fix...”、“Remove...”
  • 对于没有面向用户影响的内部工作(CI、工具、测试等),完全省略此部分

概念、示例和常见问题(仅当审阅者确实需要时)

以审阅者的需求为准,而不是 PR 的大小。大型 PR 并不是添加表格的理由——审阅者需要共享的词汇或使用示例来理解变更才是。大多数大型 PR 都达不到这个标准。当确实需要时,将相关章节放在标准的 ### Change type / ### Test plan / ### Release notes 块之前。

护栏:只有当某一行提供了代码中没有表达的信息时,它才值得占一席之地。一个只是重复签名的 Method | Description 表,或为一个不言自明的名称添加的 Concepts 行,都属于 diff 重复——删掉它。如有疑虑,就不要写。

从以下菜单中按大致顺序选取,只使用合适的内容:

  • Concepts —— 一张表格,列出 PR 引入的新术语/类型,包含 Term | Type | Meaning 列。当读者需要共享词汇来理解描述的其余部分时使用。
  • Module augmentation —— 简短的代码块,展示使用方如何扩展新类型,适用于 PR 暴露可增强接口的情况。
  • Editor API / Component props —— 用于新公开接口的 Method | Description 和 Prop | Type | Description 表。
  • Per-shape / per-feature breakdown —— 展示在新系统下每个受影响的 shape/模块返回或接受什么的表格。
  • Example —— 一个真实、可复制的代码片段,展示默认实现如何使用新系统,再加上第二个片段展示使用方如何覆盖它。
  • FAQ —— 预判的“我该如何……?”问题,附带简短代码回答。覆盖下游用户首先会想到的显而易见的自定义路径。
  • New examples —— 在 apps/examples/src/examples/ 下新增条目的项目符号列表,每条附带一行描述,让审阅者知道去哪里找可运行的演示。

参考:tldraw/tldraw#8410 是一个将所有上述章节整合在一起的良好范例。

这些章节放在 ### Change type 之前。标准的 ### Change type / ### Test plan / ### Release notes / ### API changes / ### Code changes 块仍按通常顺序出现在底部。

API changes 章节

当变更影响 api-report.md 时包含:

### API changes

- Added `Editor.newMethod()` for X
- Breaking! Removed `Editor.oldMethod()`
- Changed `Editor.method()` to accept optional `options` parameter

Code changes 表格

这张表是对“不要重复 diff”的刻意例外:它是一个高层索引,而不是假装有洞见的散文。它让审阅者能一眼判断范围——有多少是核心代码、测试、生成文件还是工具——并决定看哪里。始终包含它。

创建一个表格,包含以下各个部分的净 LOC 变更。所有行的总和必须与 PR diff 总数一致。省略没有变更的行。

  • Core code —— SDK 包(packages/)源代码,不含测试和 API 报告
  • Tests —— 单元测试、e2e 测试(*.test.*、e2e/)
  • Automated files —— 生成的文件(例如 api-report.api.md、快照)
  • Documentation —— 文档站点和示例(apps/docs/、apps/examples/)
  • Apps —— 应用代码(apps/dotcom/、apps/mcp-app/、apps/vscode/ 等),不含 e2e 测试
  • Templates —— 起步模板(templates/)
  • Config/tooling —— 配置文件、锁文件、lint 配置、CI、构建脚本(.oxlintrc.json、pnpm-lock.yaml 等)
### Code changes

| Section         | LOC change |
| --------------- | ---------- |
| Core code       | +10 / -2   |
| Tests           | +5 / -0    |
| Automated files | +0 / -1    |
| Documentation   | +2 / -0    |
| Apps            | +3 / -1    |
| Templates       | +0 / -0    |
| Config/tooling  | +1 / -0    |

相关 issue

搜索并链接此 PR 所解决的相关 issue。

注释清理

提交前检查差异,而非描述:像仔细阅读新增代码一样阅读差异中新增的注释,并在打开 PR 前精简它们。

git diff origin/main...HEAD -U0 | grep -nE '^\+\s*(//|/\*|\*)'

根据根目录 AGENTS.md 中的 Comments 部分判断它输出的内容。该部分是规则,本节不再复述——阅读时记住一个判断标准就够了:

好的注释会指出故障模式,而非实现机制。

实现机制已经摆在眼前。看不到的是:没有这段代码会出什么问题。只描述代码作用的区块可以整个删掉;指出“否则会发生 X”的区块应保留这句话,删掉重复解释它的段落。

问题通常在长度,而非类别。 大多数需要修改的长注释块已经指出了真实的缘由——它们只是写得太长,通常是必要长度的两三倍。因此,常见的修改是精简到承载关键信息的几行,而非删除。

以下内容应保留,或最多稍作精简:图示、作为规范的枚举情况列表、来源信息(问题编号、“我们试过 X,结果是 Y”),以及承载不变量的内容。在 packages/* 中,@public 接口上的文档注释就是 API 参考——同样要以“是否说明了代码本身看不出的信息”为标准,但它们是交付内容,不是额外负担。

要在提交前检查,而不是评审后再检查,因为现在做成本低,之后做成本高。复述代码本身的注释读起来像凑字数,得花一轮评审往返才能删掉,而且合并后就没人会再回头处理它。解释改动是 PR 正文的职责——不该放在差异里。

邻近的过时内容

新增注释只是一半问题。评审者更常指出的是差异未触及、但已与差异不符的注释、文档和测试名称:行为发生变化的函数上方的文档注释、现在涵盖更多调用的 catch 日志消息、仍在讲解旧模式的文档页面。运行:

npx tsx skills/write-pr/scripts/touched-comments.ts

它会列出每处改动附近几行内未变更的注释,以及提及差异新增、删除或重命名声明的文档。逐一对照新代码检查其中的每项陈述,并修正或删除。脚本不会检查测试名称,因此请自行阅读已修改测试附近的 describe/it 标题。然后对 PR 描述做同样的检查:每句描述行为的话(“X 现在会执行 Y”、“健康检查会恢复 Z”、某个计划安排或数量)都必须符合最终差异,而非初稿。

人类备注(必须原样保留)

PR 作者常会在描述最开头写一段个人备注,前面带有“人物”表情符号,例如 🅯 或 👨(或类似符号)。这是人工撰写的内容,读者会信任它。

  • 更新现有 PR 描述时,如果正文以人物表情符号开头的段落或章节起始,必须在顶部逐字节原样保留——不得改写、重新排版、换说法、改标题或“润色”,哪怕只是稍微调整。
  • 如果人工备注超过一个段落,结尾会用 --- 分隔。必须从人物表情符号开始,到 ---(含)为止,全部原样保留。
  • 如果没有 ---,人工备注仅指以人物表情符号开头的首段。
  • 所有修改都必须放在人工备注之下(如果有 ---,则放在它之下)。
  • 创建新 PR 时,不要擅自添加人工备注——留给作者填写。

重要事项

  • 除非 PR 直接涉及 AI 工具,否则绝不添加 AI 署名
  • 描述标题不要使用标题式大小写;使用句子式大小写
  • 绝不要将自己列为任何提交的共同作者
  • 如果 PR 修改了任何 api-report.md,始终添加 API 变更章节

评论

支持 Markdown