关于 creating-spec
用于编写技术规格说明的技能,要求先探索相关代码库并确认设计决策。它提供分阶段流程、文档结构,以及 Effect-TS 和跨规格一致性检查。
主要功能
- 01跨代码库探索流程
- 02写作前澄清设计决策
- 03结构化规格说明模板
- 04Effect-TS 合规审查
- 05跨规格一致性检查
- 06实施与迁移规划
适用场景
- 01规划跨包的集中式模块
- 02记录 SDK 缺口与功能模块
- 03按项目规范检查规格说明
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pedronauck/ skills --skill creating-spec安装后在 Claude Code 中输入/creating-spec确认已加载。
详细介绍
创建 Spec
用于创建全面技术规范的系统化工作流,这些规范可整合分散的功能、填补 SDK 空缺,或规划新的功能模块。此技能编码了经过验证的流程,可用于编写高质量规范,例如会话管理和工具注册表桥接规范。
<critical_rules>
强制工作流
你必须按顺序遵循以下阶段。跳过或合并阶段会导致规范不完整。
阶段 1:深入探索(仅输出,不写入文件)
目标:在提出任何方案之前,梳理所有相关代码库中现有代码的全貌。
-
并行启动探索 Agent(同时启动 4-5 个),分别检查:
- 将实现规范的目标包(例如,
providers/sdk) - 实现最完整的参考代码(例如,
providers/runtime) - 拥有各自实现的使用方代码库(例如,
packages/electron/src/agents/、packages/electron/src/looper/) - 包含相关类型或工具的共享包(例如,
packages/types)
- 将实现规范的目标包(例如,
-
每个探索 Agent 都必须报告:
- 包含关键类型/接口的文件路径
- 组件之间的连接和交互方式
- 使用的模式(Effect 服务、Context.Tag、工厂等)
- 各代码库之间的重复和碎片化情况
-
强制要求:探索后直接阅读关键文件以验证发现。不要仅依赖探索 Agent 的摘要来了解类型签名或 API 结构。
-
首先展示发现——向用户说明:
- 跨代码库对比表(各处已有的内容)
- 重复情况图谱(多个位置实现的相同功能)
- 哪些内容应集中管理,哪些应保留原位
- 明确指出缺口
禁止事项:阶段 1 期间不得写入任何文件。仅在聊天中输出分析。
阶段 2:澄清(写入前提问)
目标:明确会影响规范架构的设计决策。
-
使用 AskUserQuestion 工具提出 2-4 个有针对性的问题,涉及:
- 范围决策(完全替换、构建基础组件,还是薄封装)
- 自动化程度(完全自动、手动,还是混合)
- 向后兼容性方面的考虑(原生支持,还是由使用方处理)
- 集中模块应包含和排除的内容
-
问题格式:
- 每个问题应提供 2-3 个具体选项,并附说明
- 用“(推荐)”标出推荐选项
- 选项应代表真正不同的架构选择,而非细枝末节的偏好
-
强制要求:等待用户回答后再继续。不要擅自假设默认选项。
禁止事项:一次最多提出 4 个问题。聚焦于会实质影响规范的决策。
第 3 阶段:编写规范
目标:在恰当的抽象层级编写全面的规范——描述行为约定和架构决策,而不是实现代码。
-
检查现有规范格式——阅读同一目录中的任何既有规范(例如,
tasks/prd-sdk/1_*.md、tasks/prd-sdk/2_*.md),以符合既定的风格和结构。 -
按照下面的模板结构编写规范。
-
必需章节(必须全部包含):
- 问题陈述(说明存在的问题或重复之处,以及当前造成的成本)
- 设计概述(ASCII 架构图、高层流程)
- 新增文件(目录结构)
- 详细 API 规范(接口、类型、服务标签)
- 替换内容(逐文件说明哪些内容将在何处变更)
- 与现有系统的交互(说明如何与现有内容共存)
- 公共 API 范围(明确的导出列表)
- 高层实现概述(面向使用者的代码示例)
- 测试策略(单元测试和集成测试计划)
- 实施顺序(列出带依赖关系和并行安排的编号步骤)
- 迁移路径(现有使用者的分阶段推出计划)
- 不会迁移的内容(明确列出排除项及原因)
第 3b 阶段:EFFECT-TS 合规性检查
目标:在向用户提交规范之前,验证其中的所有代码片段都遵循 Effect-TS 模式。
必须执行:编写规范后,根据 effect-ts 和 effect-ts 技能审核每个代码片段。检查:
- 导入模式:必须使用
import * as Module from "effect/Module",绝不能使用import { X } from "effect" - 名义类型:所有领域基本类型(ID、键、名称)都必须通过
Schema.String.pipe(Schema.brand("X"))添加名义标记。检查现有的protocol/branded.ts,遵循其中既定的模式。 - Schema 类模式:记录使用
Schema.Class,可区分变体(事件、状态类型)使用Schema.TaggedClass。检查protocol/events.ts中现有事件的定义,并严格保持一致。 - 不使用
Schema.Unknown:使用JsonValue、JsonObject(来自protocol/shared.ts),或使用Schema.Defect(封装外部错误)。绝不使用Schema.Unknown或类型中的裸unknown。 - 不使用
Schema.Enums:字符串字面量联合使用Schema.Literal("a", "b", "c")。Schema.Enums仅用于 TypeScriptenum类型。 - 不使用
Schema.suspend:仅用于递归类型。引用其他 Schema 时无需使用。 - 服务模式:服务必须使用带静态工厂方法和静态
layer的Context.Tag。绝不使用模块级函数实现服务逻辑或创建层。 - 静态辅助方法:辅助函数/工具函数必须是辅助类上的静态方法(例如,
MyHelpers.resolve()),不能是模块级的export const函数。 Effect.fn跟踪:所有服务方法实现都必须指定Effect.fn("ServiceName.methodName")以支持可观测性。请在规范中明确说明此要求。- 错误处理:领域错误使用
Schema.TaggedError。未知外部错误使用Schema.Defect封装。错误通道中不得出现unknown。 - 机密信息:API 密钥和敏感值使用
Redacted.Redacted,不能使用普通的string。 - 可测试性:使用
Clock.currentTimeMillis,而不是Date.now()或原始clock回调。Effect 的TestClock可用于控制测试时间。 - 不可变状态:服务状态必须保存在
Ref或SynchronizedRef中,不能使用可变的Map/Set。优先使用 Effect 提供的HashMap.HashMap。 - Schema.Class 构造:返回
Schema.Class类型实例时,必须始终使用new ClassName({...})构造函数,不能使用普通对象字面量。
阶段 3c:跨规范一致性检查
目标:确保新规范不会与其他规范中的定义相矛盾或重复。
强制要求:定稿前,检查同一目录中的所有现有规范:
- 不得重复定义:如果某个类型、常量或函数已在其他规范中定义,则应导入,不能重新定义。常见的重复来源:
- 提供方系列解析函数
- 提供方元数据键回退映射
- 涉及多个关注点的类型定义
- 命名一致:同一概念在所有规范中必须使用相同名称。如果规范 N 定义了
ProviderFamilyId,规范 N+1 就不能将其称为OpenResponsesProviderFamily。 - 类型兼容:如果两个规范扩展了同一个接口(例如
CompozyCallProviderOptions),请列出所有规范中的全部字段,而不只是当前规范中的字段。 - 实现顺序:如果你的规范依赖另一规范中的类型扩展(例如
ProviderId从 3 个值扩展为 11 个值),请在“实现顺序”部分明确记录此依赖。 - 规范所有权:每个类型、函数和常量都必须有一个规范的源文件。定义它的规范是其所有者。其他需要使用它的规范必须注明“从
src/path/file.ts导入(定义于规范 N)”。
阶段 4:高层实现(按需迭代)
目标:展示具体的、面向使用者的 API 示例,说明最终结果如何运作。
- 在规范中加入“高层实现概览”部分,其中包含:
- 设置/配置代码
- 注册/初始化代码
- 流式处理或执行期间的用法
- 直接使用工具函数的示例(编解码器、辅助函数等)
- 自定义提供方/扩展示例
- 前后依赖关系图(ASCII)
- 如果用户另行提出此要求,请在聊天回复中提供带注释代码块的简洁说明。
</critical_rules>
<spec_template>
规范文档结构
每份规范都遵循此结构。各部分均已编号,便于交叉引用。
# [Feature Name] for [Target Package]
## Problem Statement
- What systems currently exist (list each with location)
- What duplication exists (table format: concern | location A | location B | location C)
- What this costs today (concrete pain points, not abstract)
## Design: [Design Name]
- Architecture diagram (ASCII art showing data flow)
- Key principle (e.g., "fully automatic", "opt-in", "plug-in")
## New Files
- Directory tree of new files to create
## 1-N. Detailed Specifications
- Each major component gets its own numbered section
- Include full TypeScript interfaces with JSDoc
- Show Effect service tags, Layer factories
- Specify error types
## N+1. What This Replaces
- Table: Current File | What Moves | What's Eliminated
- Separate tables per codebase (runtime, agents, looper)
## N+2. Interaction With Existing Systems
- How new code coexists with existing services
- What changes vs. what stays the same
## N+3. Public API Surface
- Explicit export lists from sub-path and main entry
## N+4. High-Level Implementation Overview
- Consumer-facing code examples (setup, usage, customization)
- Before/after dependency graph
## N+5. Testing Strategy
- Unit test plan per module
- Integration test plan
- Migrated tests from existing codebases
## N+6. Implementation Order
- Numbered steps with dependency column and parallelization notes
- Table format: Step | What | Depends On | Parallelizable
## N+7. Migration Path
- Phased rollout (Phase 1, 2, 3...)
- Each phase: what changes, what re-exports, what gets deleted
## N+8. What Does NOT Move
- Table: Concern | Location | Why It Stays
- Be explicit about boundaries
</spec_template>
<quality_standards>
质量检查清单
在认为规范完整之前,请确认:
- 已完成跨代码库探索(而非仅探索目标包)
- 重复项映射清楚展示了各处的重复内容
- 所有设计决策均已通过 AskUserQuestion 与用户确认
- TypeScript 接口完整(不是伪代码)
- 符合 Effect-TS 要求——所有代码片段均通过阶段 3b 检查(品牌类型、Schema 模式、服务模式、Effect.fn、不使用 Schema.Unknown)
- 跨规范一致性——没有重复定义,命名一致,类型扩展兼容(阶段 3c)
- 规范所有权——每个类型/函数/常量都只有一个所有规范,其他规范均从该处导入
- Effect 模式遵循项目约定(Context.Tag、Layer、Schema.TaggedError)
- 实现顺序具有清晰的依赖链(包括跨规范依赖)
- 迁移路径分阶段进行(而非一次性整体切换)
- 明确列出不会迁移的内容,排除使用方关注事项
- 高层实现展示真实的、面向使用者的代码
- 文件路径遵循现有规范的命名约定(例如
tasks/prd-sdk/N_feature-name.md) - 避免过度规定——通过“规范与实现”测试(见下文)
写作风格
- 使用表格进行比较和映射(不要用散文)
- 使用 ASCII 图绘制架构图(不要用 mermaid)
- 使用 TypeScript 代码块编写接口(不要用伪代码)
- 内容具体:文件路径、函数名、类型名(不要写“该服务”或“该模块”)
- 使用具体的行数和文件名说明“当前成本”
- 每个部分都应回答“哪些内容会改变,哪些内容保持不变”
规范与实现测试(防止膨胀)
规范必须描述系统做什么以及为什么,而不是逐行描述如何实现。请对每个代码块应用此测试:
适合写入规范的内容(保留):
- 类型/架构定义——这些就是契约(例如,
Schema.Class、接口、品牌类型) - 接口签名——带有 JSDoc 的公共 API 表面
- 简短的行为伪代码(少于 15 行)——以较高层次概述关键算法步骤
- 决策表——提供方 → 配置 → 行为映射
- ASCII 图示——架构、数据流、依赖关系变更前后的对比图
- 每种模式一个示例——展示一次该模式,并注明“对 X、Y、Z 应用相同模式”
不适合写入规范的内容(替换):
- 完整函数体(超过 15 行)——替换为行为描述:“发生 X 时,系统应执行 Y”
- 重复的实现(同一模式展示 N 次)——只展示一次,用表格列出差异
- 内部状态类型——内部数据结构属于实现细节,应改为描述行为
- 确切的变量名和循环结构——对实现者限制过多
- 可直接复制粘贴用于生产环境的代码——如果实现者会直接复制而非理解,就说明细节过多
如何精简臃肿的章节:
- 从代码中提取架构决策,并在代码上方用文字说明
- 将实现代码替换为:行为契约、映射表或接口签名
- 如确实需要参考代码,将其移至附录——标注为“参考实现草图(非规范性)”
- 单示例规则:展示一个具体示例(例如,一个 env 构造器),其余部分用表格说明
转换示例:
臃肿(8 个 env 构造器,共 131 行):
export const buildZaiEnvVars = (config: ZaiConfig): Record<string, string> => {
const env: Record<string, string> = {};
env.ANTHROPIC_BASE_URL = "https://api.zai.com/v1";
if (config.apiKey) env.ANTHROPIC_AUTH_TOKEN = config.apiKey;
// ... 15 more lines
};
export const buildOpenRouterEnvVars = (config: OpenRouterConfig): ... => { ... };
// ... 6 more builders, same pattern
正确(表格 + 1 个示例):
关键决策:每次请求都重新构造环境变量(而非在创建时预先计算)。
| 提供方 | 基础 URL | 认证变量 | 额外变量 | 是否清除 API_KEY? |
|---|---|---|---|---|
| zai | https://api.zai.com/v1 | ANTHROPIC_AUTH_TOKEN | — | 是 |
| openrouter | https://openrouter.ai/api/v1 | ANTHROPIC_AUTH_TOKEN | — | 是 |
| ... | ... | ... | ... | ... |
所有构造器都遵循相同模式。参考:buildZaiEnvVars(如上所示)。
</quality_standards>
<anti_patterns>
不要做的事
工作流反模式
- 不要在探索之前编写规范——否则会遗漏重复内容和上下文
- 不要跳过澄清阶段——设计决策会影响每个章节
- 不要跳过 Phase 3b(Effect-TS 检查)——每份跳过此阶段编写的规范都有 5–7 处违规
- 不要跳过 Phase 3c(跨规范检查)——规范 2 和 5 各自定义了相同的 3 个函数,但签名不同,导致 3 处严重矛盾
- 不要在未阅读先前规范的情况下编写规范——格式一致性和类型一致性都很重要
- 澄清问题不要超过 4 个——只关注架构决策
内容反模式
- 不要编写含糊的接口——提供完整的类型签名并遵循 Effect 模式
- 不要提议一次性大规模迁移——始终采用分阶段推出
- 不要在 SDK 中纳入消费者关注点——审批关卡、UI 流式传输、提示词注入应留在消费者端
- 不要跳过“哪些内容不会迁移”一节——明确边界可防止范围蔓延
- 不要只提供高层概述而不附具体代码——展示设置、注册、使用和自定义方式
</anti_patterns>
臃肿反模式(来自真实审计发现)
- 不要编写完整的函数实现 — 包含 100 行以上函数体(环境构建器、适配器、驱动程序)的规格会把架构决策埋进大段代码中。改用行为描述和表格。
- 不要重复展示同一种模式 N 次 — 展示一次,并用表格列出差异。将一个环境构建器展示 8 次会浪费 131 行;使用表格加 1 个示例,只需 20 行且更清晰。
- 不要指定内部状态类型 —
RegistryState及其精确的Map键格式属于实现细节。描述行为约定(“按工具集签名缓存桥接器”)。 - 不要编写可直接复制粘贴的生产代码 — 如果实现者会原样复制你的代码,那你写的是实现,而不是规格。规格应要求读者进行理解和转换。
- 不要把关键决策埋在代码中 — “request.model 的双重角色”这一决策在一个 99 行的类体中难以察觉。应先用文字说明决策,再写代码。
Effect-TS 反模式(来自真实审计发现)
- 不要使用
Schema.Unknown— 使用JsonValue、JsonObject或Schema.Defect - 不要为 ID 使用普通类型别名 — 使用带品牌的 schema:
Schema.String.pipe(Schema.brand("X")) - 不要对事件使用
Schema.Class— 使用带判别的变体(带有type字段的事件),必须使用Schema.TaggedClass - 不要对字符串字面量使用
Schema.Enums— 使用Schema.Literal("a", "b", "c") - 不要定义模块作用域函数 — 在辅助类或服务类上使用静态方法
- 不要从 Schema.Class 工厂返回普通对象 — 使用
new ClassName({...})构造函数 - 不要对可测试操作使用原始回调 — 使用 Effect 服务(例如,用
Clock替代clock?: () => number) - 不要使用
as unknown as X双重类型断言 — 使用展开语法或Schema.encode
跨规格反模式(来自真实审计发现)
- 不要重新定义其他规格已定义的函数 — 应导入该函数。常见陷阱:多个缺口规格分别定义
resolveProviderMetadata等实用函数。 - 不要给同一概念使用不同名称 —
ProviderFamilyId与OpenResponsesProviderFamily曾造成混淆。选定一个名称并始终使用。 - 扩展共享类型时,不要默默丢弃字段 — 如果规格 1 向选项类型添加了
apiKey,而规格 2 扩展该类型,则应包含apiKey,或明确说明将其移除。 - 不要假设实现顺序 — 如果你的规格依赖另一份规格中的类型扩展(例如,
ProviderId的取值从 3 个增加到 11 个),应在“实现顺序”部分记录此依赖关系。
</anti_patterns>