关于 generate-diffmap
generate-diffmap 是用于编写和分享软件变更说明的 Agent 技能,以持续更新的 Markdown 规格文档呈现现状、计划和进展。它指导作者先审查代码并确认范围,再用分阶段说明、图表和源码引用解释变更。
主要功能
- 01持续更新的 Markdown 规格文档
- 02起草前确认变更范围
- 03分阶段说明变更
- 04图表与调用栈视图
- 05链接源码的引用
- 06通过 Diffmap 提供和分享文档
适用场景
- 01记录现有拉取请求
- 02解释计划中的代码变更
- 03跟踪已完成的实现阶段
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tanishqkancharla/ diffmap --skill generate-diffmap安装后在 Claude Code 中输入/generate-diffmap确认已加载。
附带文件agents/openai.yaml
详细介绍
持续维护的规格说明
规格说明是一个 Markdown 文件,路径为 specs/<short-kebab-case-name>.md,用于展示当前系统、计划和已完成的工作。随着计划变化和工作推进,持续更新它。若要演示现有拉取请求,请使用其规格说明(如果有);否则写在 .diffmap/ 下。写入前,请检查仓库中的路径和符号。
先确定范围。 写入前先阅读相关代码。如果设计、范围或阶段划分不明确,请先与用户确认,再开始大量编写:提供几个选项,并说明你推荐哪一个。
提供和分享
在仓库根目录运行:
npx @tanishqkancharla/diffmap list # reuse the URL if this file is already served
npx @tanishqkancharla/diffmap serve specs/<name>.md
npx @tanishqkancharla/diffmap share specs/<name>.md # only when asked; prints https://diffmap.dev/g/<id>
保持服务持续运行,并将文件路径和 URL 告知用户。仅在用户提出要求时才打开浏览器。分享前,请提交已完成的工作,并让其中的引用指向该提交,如阶段完成时所示:share 会将每个被引用提交的差异保存到 gist 中,但没有提交的引用只会打开分享时文件的状态。分享的 gist 不会公开列出:任何拥有 URL 的人都可以阅读,包括它快照中的所有文件。拉取请求中的规格说明也可通过 https://diffmap.dev/<owner>/<repo>/pull/<n>/specs/<name>.md 访问,并固定到某个提交。
结构
面向需要快速了解变更的人来写。先从使用者会遇到的变化讲起,例如屏幕、命令、API 调用或错误,再根据读者的需要,逐步说明实现。规格说明通常包含问题概述、解决方案概述、目标与非目标、阶段和 参考资料;根据内容决定每部分需要写什么。
阶段。 每个阶段都要足够小,能够单独提交。规划中的阶段标题使用普通格式,进行中的标题加上 ⏳,已完成的标题加上 ✅。每个阶段都先从客户端视角简要说明 当前情况 和 提议方案,再按变更的特点展开说明:新流程可能需要图示和调用栈,重构可能需要调用栈差异,依赖项升级可能只需一两句话。篇幅应与变更相称,每件事只在适当的位置说明一次。规格说明用于解释变更,而不是跟踪任务。
图示。 图表、调用栈和代码往往比段落更能说明变更,因此, prose 冗长时不妨使用它们。只有在图示能展示读者需要了解的内容时才添加,不要为了填充版面而添加。
这些视图都符合一个模型:服务接收消息、更新自身状态,然后转发消息。每种视图都展示服务之间或单个服务内部的情况,既可以涵盖所有路径,也可以跟踪一条消息:
| 所有路径 | 单条消息 | |
|---|---|---|
| 服务之间 | mermaid 流程图:有哪些服务,各自负责什么 | services:消息如何穿过服务,以及它会改变哪些状态 |
| 单个服务内部 | 伪代码和类型:服务的状态、载荷和逻辑 | callstack:服务如何将一条消息转化为结果 |
当各部分会保持状态、并行运行或分流时,使用 services;当它们是会返回、分支或提前返回的函数调用时,使用调用栈。Mermaid 时序图和状态图是更轻量的选项。如果一个视图需要展示超过约 7 个服务,请将其中一些合并,并另行说明。
- 要展示变更,请在
mermaid或services后添加diff,并像调用栈那样让每行以-(仅当前情况)、+(仅提议方案)或空格(两者共有)开头。 - Mermaid 图表应在语言标记后使用
[标题]添加标题,例如mermaid diff [无效记录会在存储前停止]。services代码围栏应在第一步之前使用start "caption"作为整个图表的标题,并在其下方用>行以几句话概述整个流程;每一步都要标注发生的情况。 - 使用实际名称标注节点和箭头,并通过
%% ref指令链接现有源代码。
调用栈。 将每行写成数据流形式:result = function(inputs);在重要位置使用 return 行。用 - 标记原有内容,用 + 标记变更,用空格标记未变更内容;差异应对比同一函数变更前后的情况。使用 ├── 和 └── 展示调用嵌套。通过源代码引用链接实际函数;未链接的辅助函数是伪代码,并非承诺提供的 API。调用栈控制在约 12 行,并将深入流程拆分为概览和每个变更后被调用函数各自的调用栈。
代码。 代码围栏应简短,并紧扣段落所述内容;用 // ... 省略未变更的代码。在语言标记后使用 {2-3} 高亮代码行(在 callstack 后使用 {3-4} 高亮行),并在底部用注释说明,而不要写冗长注释:对名称,无论其出现在哪里,都使用 // [!note symbol] text;对从代码围栏顶部开始计数的行,使用 // [!note L3-L5] text。调用栈也使用相同的说明方式,但注释符号为 #。
链接。 使用 references 链接现有源代码;计划中的符号写作普通文本。
行文
所有正文、注释和图示标签都遵循奥威尔在《政治与英语》中的六条规则。代码名称不属于术语。
示例阶段
此示例使用的是说明性文件和提交 SHA,而非 Diffmap 源码路径。此更改涉及几个层次,因此使用了多个视图;较小的阶段需要的视图更少。
### ⏳ Phase 1: Validate request input
**Today**
The handler passes records straight to the service. A client that sends an empty name gets a success response, and the bad record surfaces later as a storage error the client never sees, so they keep sending bad input.
**Proposed**
A client that sends an empty name gets an error naming the `name` field, so they can fix the request right away. Bulk import in Phase 2 needs every saved record to be valid, so this check comes first.
From the client's side, the same empty-name request now fails at once:
```ts
const response = await client.saveRecord({ name: "" });
// before: { ok: true }
// after: { ok: false, error: "name must not be empty" }
```
Today every record reaches storage. In the proposed flow, a valid record follows the same save path, but an invalid one returns an error before storage. The unlinked `validateInput` helper is pseudocode, not a proposed public API:
```mermaid diff [Invalid records stop before storage]
flowchart LR
- A[requestHandler] -->|record| B[existingService]
+ A[requestHandler] -->|record| V[validateInput]
+ V -->|valid record| B[existingService]
+ V -->|invalid record| E[Return name error]
B -->|record| D[Storage]
%% ref node:A [[src/request.ts#requestHandler]]
%% ref node:B [[src/service.ts#existingService]]
```
The call stack shows how the validation result controls the return path:
```callstack
response = requestHandler(record) [[src/request.ts#requestHandler]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) # reject empty names
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return result
```
In code, the handler returns before the existing save:
```ts {2-3}
function requestHandler(record: RecordInput) {
const error = validateInput(record);
if (error) return error;
return existingService(record);
}
// [!note validateInput] Returns an error when `name` is blank, otherwise undefined.
// [!note L3] Storage never sees an invalid record.
```
The new check:
```ts
function validateInput(record: RecordInput) {
if (record.name.trim() === "")
return { ok: false, error: "name must not be empty" };
return undefined;
}
```
阶段完成时
开始下一阶段之前:
- 提交代码。如果该阶段包含多次提交,则在以下步骤提到 SHA 的地方使用
<base>..<head>。 - 在 Today 和 Proposed 之后立即添加一个带有该 SHA 的
changes代码围栏。它会呈现阶段摘要卡片。在 SHA 下方,按审阅顺序逐行列出审阅者应查看的文件路径:通常先列出面向客户端的入口点,再依次列出各层文件。省略无需仔细审阅的文件,例如锁文件、生成的代码、快照、测试样例和机械式重命名;摘要卡片会将它们折叠到“另外 N 个文件”下。 - 将阶段中更改的所有引用指向该提交:堆栈行、
%% ref指令和正文文本。 - 更新图表、调用栈和伪代码,使其与实现一致,并将标题标记为 ✅。
- 单独提交规范更新;其中需注明代码提交的 SHA。
- 重启
serve;它会在启动时检查提交引用。
示例阶段随后如下所示:
```changes
a1b2c3d
src/request.ts
src/validate.ts
```
```callstack
response = requestHandler(record) [[a1b2c3d:src/request.ts#L8-L20]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) [[a1b2c3d:src/validate.ts#L1-L9]]
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return result
```
源码引用
路径相对于仓库根目录。
[[path/to/file.ts#symbolName]]会链接到 TypeScript 或 JavaScript 声明;如果名称有歧义,请限定名称,例如[[src/store.ts#Store.save]]。[[path]]会链接到文件;[[path#L12-L30]]会链接到一个行范围。[[<sha>:path#L12-L30]]会链接到提交中的行;如果阶段包含多次提交,则使用<base>..<head>,其中<base>是首次提交之前的提交。行号按提交后的文件编号,因此无法链接已删除的行。可使用git show <sha> -- <path>和git show <sha>:<path> | nl -ba查找行号。- 不带提交的引用会打开该文件未提交的更改;如果没有未提交的更改,则打开当前文件。共享的 gist 会打开分享时的文件版本。
- 在正文中,引用会呈现为可点击的行内代码;如需显示语法,请将其用反引号括起来。
- 在 Mermaid 中,
%% ref node:<id> [[reference]]用于链接节点或参与者,%% ref edge:<index> [[reference]]用于链接边或消息,索引按声明顺序从零开始。每个目标使用一条指令;如有需要,可使用多个引用。
代码围栏:mermaid 用于绘制图表;services 用于绘制服务、其状态以及服务间事件的动画图表(在任一围栏后添加 diff 可用 - 或 + 标记行);callstack 用于堆栈行;changes 用于提交或范围摘要(未提交更改时留空),并在其下方列出待审阅的文件;html 用于你编写的可信 HTML。其他语言用于伪代码和类型,可选用 {…} 高亮内容,并添加 [!note …] 行。在语言标记后添加 [Title],例如 ```callstack [Request flow],可为调用栈、代码围栏或 Mermaid 图表添加标题。
查阅其余内容
- 完整参考和围栏语法 → diffmap README
- 示例 → annotations、diagrams 和 services 测试样例