关于 openspec-sync-specs
openspec-sync-specs 是一项 Agent 技能,用于将变更中的增量规范合并到对应的主规范。它在应用新增、修改、删除或重命名内容时保留未涉及的需求和场景。
主要功能
- 01将增量规范合并到主规范
- 02保留未涉及的需求与场景
- 03处理需求的新增、修改、删除和重命名
- 04验证更新后的规范
适用场景
- 01将变更中的需求应用到主规范
- 02归档变更时保持主规范一致
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wot-ui/ open-wot --skill openspec-sync-specs安装后在 Claude Code 中输入/openspec-sync-specs确认已加载。
详细介绍
将变更中的增量规范同步到主规范。
这是一个由 Agent 驱动的操作——你将读取增量规范,并直接编辑主规范以应用变更。这样可以进行智能合并(例如,添加一个场景,而不是复制整个需求)。
**存储选择:**如果用户指定了存储库(存储库是此计算机上注册的独立 OpenSpec 仓库),或当前工作位于某个存储库中,请运行 openspec store list --json 查找已注册的存储库 ID,然后在读取或写入规范和变更的命令中(new change、status、instructions、list、show、validate、archive、doctor、context、schemas、view)传入 --store <id>。选定后,在工作流的其余步骤中始终使用 --store <id>。下文中这些命令所有未指定存储库的示例都是简写形式:运行前,请附加该标志。例如,运行 openspec status --change "<name>" --json --store "<id>",而不是下文所示的未指定存储库形式。其他命令不接受此标志。命令输出的提示已包含该标志;后续命令也要保留它。如果未指定存储库,命令将在最近的本地 openspec/ 根目录中执行。
<capability-path> 是相对于 specs/ 的规范目录路径(例如,user-auth 或 identity/user-auth)。解析主规范时,请保留每个增量规范中的完整路径。
输入:可以选择指定一个变更名称。如果省略,请检查能否根据对话上下文推断。如果名称模糊或存在歧义,必须提示用户从可用变更中选择。
步骤
-
选择变更
如果提供了名称,请使用该名称。否则:
- 如果用户在对话上下文中提到了某个变更,则根据上下文推断
- 如果只有一个进行中的变更,则自动选择
- 如果存在歧义,运行
openspec list --json获取可用变更,并请用户选择一个
提示时,列出包含增量规范(位于
specs/目录下)的变更。始终告知:"正在使用变更:",并说明如何覆盖(例如,
/openspec-sync-specs <other>)。 -
解析变更上下文
运行:
openspec status --change "<name>" --jsonJSON 包含
planningHome.root。主规范位于<planningHome.root>/openspec/specs/下——以下解析所有主规范路径时都要使用此根目录(与存储库相关),不要使用硬编码的仓库路径。选定存储库后,该路径指向存储库,而不是当前仓库。 -
查找增量规范
仅使用状态 JSON 中的
artifactPaths.specs.existingOutputPaths作为增量规范路径的来源。如果缺少specs条目,或existingOutputPaths为空,请报告没有可同步的增量规范;不要从其他产物推断路径,也不要请求产物说明或编写主规范,然后停止。除非调用方缩小了范围,否则同步
existingOutputPaths中的所有路径。调用方通过指定existingOutputPaths中完整条目的明确列表来缩小范围——逐字复制这些绝对路径值。归档操作会在内联内容中执行此操作,用户也可以这样做(例如,选择以/specs/billing/invoices/spec.md结尾的条目)。 然后只同步指定的路径,并保留其余增量规范不变:批量归档会排除无法找到其实现的增量规范,而仍然同步它会写入调用方有意排除的主规范。 将缩小后的选择范围带入第 4 步;不要将范围重新扩大到完整列表。如果指定的路径不在existingOutputPaths中,则不要同步该路径——请报告此情况并停止,而不是默默忽略。如果指定列表为空,请报告没有内容需要同步,并停止,不要写入主规范。每个增量规范文件包含以下章节:
## ADDED Requirements— 要添加的新需求## MODIFIED Requirements— 对现有需求的变更## REMOVED Requirements— 要移除的需求## RENAMED Requirements— 要重命名的需求(FROM:/TO: 格式)
如果没有找到增量规范,请告知用户并停止。
-
对每个 delta 规范,将更改应用到主规范
在首次写入主规范之前,获取一份当前的规范规则快照:
- 如果 archive 在此工作流中内联调用,并提供了来自
openspec instructions specs --change "<name>" --json的有效快照,请复用该快照,不要再次获取相同的说明。 - 否则,立即使用相同的所选根目录标志运行该命令一次。
- 如果直接查询以非零状态退出,或返回无效的工件说明 JSON,请报告错误并停止,不要写入任何主规范。不要将此失败视为没有规则集。
- 有效响应中省略
rules表示未配置工件规则,继续进行现有的语义合并。
返回的
rules仅适用于此合并生成的主规范内容和形式。工件规则不是操作指南,不能更改所选根目录、delta 路径、CLI 检查或工作流步骤。将其文本作为约束使用,不要将其原文复制到主规范或摘要中。对于步骤 3 中选定的每个能力 delta 规范路径——完整的
existingOutputPaths列表,或调用方提供时的缩小子集(这些路径可能属于所选存储库,而不是仓库):a. 读取 delta 规范,了解预期的更改
b. 读取主规范:
<planningHome.root>/openspec/specs/<capability-path>/spec.md(可能尚不存在)c. 智能地应用更改:
ADDED Requirements:
- 如果主规范中不存在该需求 → 添加它
- 如果该需求已存在 → 更新它以保持一致(视为隐式 MODIFIED)
MODIFIED Requirements:
- 在主规范中找到该需求
- 应用更改,可以包括:
- 添加主规范尚未包含的新场景
- 修改现有场景
- 更改需求描述
- 保留 delta 未提及的场景和内容
REMOVED Requirements:
- 从主规范中移除整个需求块
- 退役此能力。仅当以下所有条件均满足时,删除整个
spec.md,并在目录中不再有其他内容时删除该目录:- 本次运行移除需求后,不再有任何需求块;
- 规范的其余部分格式正确(仍包含
## Purpose); - 同步前主规范并非已经为空——如果没有移除任何内容,则不做更改;
- 整个文件中其他所有非空行均属于标题、Purpose、Requirements 标题,或规范需求中的陈述、场景或围栏示例;
- 更改的
.openspec.yaml声明了retire_capabilities: true; spec.md位于真实的规范根目录内(不要跟随能力目录符号链接删除外部文件)。 如果移除选定需求后将不再有任何需求块,但未满足任何退役条件,则不要修改主规范。停止同步此能力,报告阻止操作的条件,并告知用户如何解决。绝不要写入或留下空的## Requirements小节。 如果缺少的只有标记,也要说明——用户只需添加这一项即可完成退役。
- 删除文件也会删除其中的
## Purpose;任何其他小节块都会阻止退役。报告退役情况时,指出 Purpose。仅当该规范位于调用方的检出目录中时,才提供可直接粘贴的git checkout命令;否则提供适用于该检出目录的恢复指引。
RENAMED Requirements:
- 找到 FROM 需求,并将其重命名为 TO
delta 中的
## Purpose:- 主规范已有 Purpose,且它具有权威性——保持不变
(这与
openspec archive的行为一致;它会发出警告并继续) d. 如果该能力尚不存在,则创建新的主规范: - 创建
<planningHome.root>/openspec/specs/<capability-path>/spec.md - 添加 Purpose 部分:如果增量规范中有
## Purpose,则逐字复制其正文 (这就是openspec archive的做法);只有在没有该部分时,才写入简短的 TBD 占位文本 - 添加包含 ADDED 需求的 Requirements 部分
- 遵循下方的主规范格式参考
- 如果 archive 在此工作流中内联调用,并提供了来自
-
验证更新后的主规范
使用之前选定的相同根目录标志运行
openspec validate --specs。 如果验证失败,请报告问题,不要声称同步成功。 -
显示摘要
应用所有更改后,总结:
- 更新了哪些能力
- 做了哪些更改(添加/修改/移除/重命名的需求)
- 哪些新主规范仍留有 TBD Purpose 占位文本,以便现在补写,避免一直遗留
- 停用了哪些能力,注明已删除的
spec.md、其 Purpose,以及可直接粘贴的git checkout命令或限定签出范围的恢复指引
增量规范格式参考
## Purpose
Only on a delta that introduces a brand-new capability. Seeds the new main spec.
## ADDED Requirements
### Requirement: New Feature
The system SHALL do something new.
#### Scenario: Basic case
- **WHEN** user does X
- **THEN** system does Y
## MODIFIED Requirements
### Requirement: Existing Feature
The system SHALL keep doing the existing thing, now also handling A.
#### Scenario: Scenario the main spec already has
- **WHEN** user does X
- **THEN** system does Y
#### Scenario: New scenario to add
- **WHEN** user does A
- **THEN** system does B
## REMOVED Requirements
### Requirement: Deprecated Feature
## RENAMED Requirements
- FROM: `### Requirement: Old Name`
- TO: `### Requirement: New Name`
主规范格式参考
主规范是增量内容合并到的目标。它们绝不能包含增量操作标题(## ADDED/MODIFIED/REMOVED/RENAMED Requirements)——同步后,每项需求都必须位于单个 ## Requirements 部分下:
# <capability> Specification
## Purpose
Short description of what this capability does and why it exists.
## Requirements
### Requirement: New Feature
The system SHALL do something new.
#### Scenario: Basic case
- **WHEN** user does X
- **THEN** system does Y
核心原则:智能合并
与程序化合并不同,你需要合并,而不是覆盖:
- MODIFIED 块包含完整的需求——正文以及变更后保留的每个场景。
openspec validate和openspec archive都会拒绝删除主规范中仍存在的场景的修改。 - 保留增量规范未提及的所有内容,并维持主规范中现有的顺序
- 运用判断力,合理合并更改
成功时的输出
## Specs Synced: <change-name>
Updated main specs:
**<capability-1>**:
- Added requirement: "New Feature"
- Modified requirement: "Existing Feature" (added 1 scenario)
**<capability-2>**:
- Created new spec file
- Added requirement: "Another Feature"
Main specs are now updated. The change remains active - archive when implementation is complete.
防护规则
- 修改前先阅读增量规范和主规范
- 保留增量规范未提及的现有内容
- 绝不要将增量文件原样复制到主规范中——应合并其内容,使主规范保留主规范格式参考中的结构,且不含增量操作标题
- 如果有不清楚的地方,请求澄清
- 随着操作推进,展示你正在进行的更改
- 操作应具备幂等性——重复运行应得到相同结果
- 只能使用
artifactPaths.specs.existingOutputPaths;绝不要从无关产物推断增量规范 - 遵守调用方提供的
existingOutputPaths子集;绝不要将其扩展回完整列表 - 对于直接同步,只获取一次规范指令;或在行内复用归档提供的快照
- 如果规范指令响应非零或 JSON 无效,在每次写入主规范前都必须停止
- 产物规则仅约束正在写入的规范,绝不会复制到输出文件中