用途
Scratch 用于探索和理解一个已经存在的项目。它的目标不是静态阅读代码,而是通过编写并实际运行脚本来走查项目的关键模块,让用户通过真实执行建立对代码库的心智模型。
它面向的是「理解你已有的东西」,即以安全、探索性的方式接触真实代码。如果用户想评估某个新东西是否值得采用,应改用 try 技能。
触发方式
用户可能会说类似的话:
- scratch this project
- scratch this project — focus on the auth system
- help me understand how this codebase works
- walk me through the internals
- how does the payment module actually work?
- scratch around in the data pipeline
用户还可以选择性地提供:
- 要重点关注的特定模块或子系统
- 想回答的问题(例如「这里的缓存是怎么工作的?」)
- 某个约束(例如「我只关心 API 层」)
- 展示想追踪行为的截图
工作流程
阶段 0:准备工作区
确认当前位于一个已有项目中:查找 git 根目录、package.json、pyproject.toml、Cargo.toml、go.mod 等。如果不在项目内,告知用户并停止。
在项目根目录创建 .scratch/。如果 .gitignore 中尚未包含 .scratch/,建议添加。
安全规则:.scratch/ 中的脚本不得修改生产代码。它们以只读方式导入并演练模块。不得写入项目数据库、不得改动配置文件、不得在 .scratch/ 之外产生副作用。
工作区结构:
<project-root>/
└── .scratch/
├── 01-<module>.{ts,py,js}
├── 02-<module>.{ts,py,js}
├── ...
├── 99-compose.{ts,py,js}
└── Tutorial.md
阶段 1:提炼
目标:识别项目的关键原语、模块和子系统。
如果当前会话中可用 distill 技能,则用它生成原语清单——一份包含 5-8 个关键模块/子系统的简明列表,每项附一行描述。这是一次初步识别,不是完整的迭代式提炼。把用户陈述的目标或角度作为上下文传入,并将 distill 的输出作为探索计划。
如果 distill 技能不可用,则进行人工侦察,按以下顺序阅读:
- 入口点:主文件、CLI 处理器、路由定义、index.*。
- 包清单:依赖、脚本、导出。
- 源码树:了解整体形态,例如 ls -R src/ | head -40 或等价命令。
- 公共 API:阅读主要入口点,看项目对外暴露什么。
- 测试:阅读 2-3 个测试文件,测试能揭示作者的心智模型。
侦察后形成一份心智清单(先记住,暂不写出):列出各模块及其一行说明,以及它们如何连接。如果用户给出了特定角度(如「缓存是怎么工作的?」),则筛选出相关模块;否则覆盖最重要的 5-8 个模块。
阶段 2:探索脚本
目标:每个原语或关键模块边界对应一个脚本,每个脚本自包含且可运行。
脚本结构示例:
// MODULE: <Name>
// WHAT: <one-line description>
// EXPECT: <what should happen when you run it>
// ENTRY POINT: <file:line in the real codebase>
<import from the project's own modules>
<exercise the module with clear inputs>
<print/assert the result>
// FINDINGS:
// - <what you learned about how this works>
// - <surprising internal behavior>
// - <connections to other modules>
探索脚本的规则:
- 每个文件只针对一个模块,不演练多个子系统。
- 从实际项目导入——这些脚本演练真实代码,而非 mock。对于编译型语言或无法直接导入的情况,改为调用项目的 CLI 或 API。
- 写完每个脚本后实际运行它,捕获 stdout/stderr。
- 如果失败,进行调试。失败本身也是有价值的信号,记录到 FINDINGS 中。
- 为每个模块记录真实代码库中的入口点(file:line),告诉用户从哪里开始阅读。
- 使用数字前缀表示阅读顺序:01-、02-、03- 等。
- 匹配项目的语言和运行时。
每个脚本运行后更新心智模型。如果发现意外情况,调整剩余脚本的计划。
阶段 3:组合脚本
目标:把 2-4 个模块串联起来,演示一条贯穿代码库的真实工作流。
// COMPOSITION: <what this demonstrates>
// MODULES USED: 01, 03, 05
// SCENARIO: <realistic workflow through the codebase>
这展示项目内部实际如何连接。组合脚本应端到端追踪一个真实的用户可见操作——例如一个请求进来、被校验、访问数据库、返回响应的全过程。
如果用户提供了特定场景、问题或截图,组合脚本必须针对它。如果没有,则追踪探索过程中浮现出的最具代表性的端到端工作流。
阶段 4:Tutorial.md
编写 .scratch/Tutorial.md,结构如下:
- 标题与一行架构摘要
- Architecture Overview:2-3 句话,说明心智模型与各部分如何组合
- Key Modules:逐个模块说明其作用、内部工作方式、代码库入口点(file:line),并链接到对应的 .scratch/01-* 脚本
- How They Connect:模块如何连接、数据流、关键路径,链接到 .scratch/99-compose
- Surprises:从阅读代码无法猜到的点,如隐藏耦合、隐式契约、非显而易见的初始化顺序
- Dead Ends:没走通的路径、失败的方法、看起来重要其实不重要的模块、误导性的命名。保留失败信号,避免用户重复走弯路
- Mental Model:压缩后的理解。如果要用 2 分钟向新团队成员解释这个代码库,你会怎么说?
执行纪律
所有阶段遵循以下节奏:
WRITE ──> RUN ──> OBSERVE ──> NOTE FINDINGS
^ │
└─────── ADJUST PLAN ─────────────┘
- 小步编写,频繁运行。不要写完 5 个脚本再一起运行。
- 捕获真实输出。在 Tutorial.md 的发现中包含实际 stdout(可裁剪)。
- 如果某处调试超过 2 分钟,将其记为复杂度信号并继续前进。
- 不要造假。如果某个模块的行为与预期不符,如实说明。用户想了解的是实际如何工作,而不是应该如何工作。
完成前检查清单
- .scratch/ 中包含 3-8 个带编号的探索脚本
- .scratch/99-compose.{ts,py} 存在且可运行
- 每个脚本都实际执行过
- 每个脚本都记录了真实代码库中的入口点(file:line)
- 没有脚本修改生产代码
- .scratch/ 中存在包含所有章节的 Tutorial.md
- Surprises 和 Dead Ends 章节记录了非显而易见的发现
- 已建议将 .scratch/ 加入 .gitignore
边界情况
- 项目使用多种语言:用被探索模块各自的语言编写脚本,并在 Tutorial.md 中注明跨语言边界。
- 项目需要运行服务(数据库、Redis 等):注明依赖,尽可能 mock,无法 mock 则跳过。依赖本身也是一项发现。
- 项目是 monorepo:询问聚焦哪个包或服务。如果用户未指定,选择最核心的一个并注明该选择。
- 项目没有测试:这本身就是一项发现,记录到 Surprises 中。
- 编译型语言(Go、Rust、Java):编写调用项目 CLI、API 或构建产物的脚本,而不是直接导入源码。
- 用户提供截图或场景:组合脚本必须针对该场景,其他脚本应支持理解它。
更新检查
在会话中首次使用时,静默检查是否有更新版本:
获取 https://raw.githubusercontent.com/abpai/skills/main/versions.json,将 scratch 的版本与本文件元数据中的 version 比较。
如果远程版本更新,在主任务前暂停并询问:
scratch update available (local {X.Y} → remote {A.B}). Would you like me to update it for you first? I can run npx skills update scratch for you.
如果用户同意,先运行更新再继续。如果用户拒绝,则继续使用当前本地版本。如果获取失败或无法访问网络,则静默跳过。