Stores (beta)
规划跨仓库的变更:一个 store,多个仓库。
OpenSpec 通常位于单个仓库内:一个 openspec/ 文件夹,紧挨着它所规划的代码。Store 会将该文件夹移入一个独立的仓库,多个代码仓库便可以共享它。
在每台机器上完成一次性设置后,status、new change、archive 等命令就可以在任意目录下对 store 使用。
team-plans (一个 store:OpenSpec 位于自己的仓库中)
├── .openspec-store/store.yaml store 的名称
└── openspec/
├── specs/
└── changes/
▲
│ 在每台机器上设置一次,
│ 像任何仓库一样通过 push 和 clone 共享
┌─────────────┼─────────────┐
│ │ │
web-app api-server mobile-app
(代码仓库) (代码仓库) (代码仓库)你使用 git 共享 store,就像共享代码一样:自行完成 commit、push、pull 和 review。Spec 和变更会像代码一样,获得各自的分支和 pull request。
什么时候需要 store
使用 store 的两个常见原因:
前端和后端分属不同仓库:一个功能会同时涉及两者,计划需要安放在同一个地方,而不是被拆成两半。
shop-plans (store) └── openspec/changes/add-discounts/ 该功能的一份计划 ▲ ┌─────────┴─────────┐ │ │ storefront api (前端仓库) (后端仓库)一个产品,多个客户端仓库:Android、iOS 和 web 各自从自己的仓库发布,却共享同一份预期行为。spec 描述的是行为,而不是实现,因此一份 spec 就能同时服务于三者。
product-specs (store) └── openspec/specs/checkout/spec.md 预期的行为 ▲ ┌─────────────┼─────────────┐ │ │ │ android-app ios-app web-app
(代码仓库) (代码仓库) (代码仓库)
你可以拥有多个 store,不过我们建议将其数量保持在较低水平。
## 设置存储库
由一个人创建存储库,然后其他人加入。
1. **创建存储库**(一个人操作,每个团队一次)。运行 `openspec store setup` 并回答提示:
```bash
# 可在任意位置运行;它会询问要创建什么以及创建在哪里
openspec store setup它会询问三个问题:
- 存储库名称:
team-plans - 此存储库应位于何处?:预填为
~/openspec/<name>,按 Enter 接受,或输入另一个路径 - 创建此存储库吗?:显示即将创建的内容,回答
Yes
然后它会报告所创建的内容:
Store ready: team-plans
Location: ~/openspec/team-plans
OpenSpec root: ready
Registry: registered
Next: run normal OpenSpec commands against this store, for example:
openspec new change <change-id> --store team-plans
Share this store by committing and pushing it like any Git repo.将其推送到你的 Git 托管平台。 首先在你的托管平台上创建一个空的
team-plans仓库。设置过程不会添加 Git 远程仓库,因此请将存储库连接到该仓库,然后推送:bash# 将存储库连接到 Git 托管平台上的空仓库 cd ~/openspec/team-plans git remote add origin git@github.com:acme/team-plans.git # 发布它 git push -u origin main加入存储库(每位队友,每台机器一次):
bash# 将存储库获取到你的机器上 git clone git@github.com:acme/team-plans.git ~/openspec/team-plans # 告诉 OpenSpec 它位于何处 openspec store register ~/openspec/team-plansyamlStore registered: team-plans Location: /Users/you/openspec/team-plans OpenSpec root: ready Registry: registered注册会告诉你的机器这个存储库位于何处。存储库的名称已经提交在其内部,位于
.openspec-store/store.yaml。设置过程已注册创建者的副本,因此只有克隆得到的副本需要执行此步骤。确认它正常工作,从任意目录:
bash# 任何 OpenSpec 命令都能按名称访问该存储库 openspec status --store team-plansyamlUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans) No active changes. Create one with: openspec new change <name> --store team-plans
设置类型
OpenSpec 有三种设置。本页其余部分使用以下名称:
- repo-local:OpenSpec 位于你的仓库内,不使用存储库。这是默认设置。
- store-only:你的仓库不保留自己的 specs 或 changes。所有内容都位于存储库中。
- store-optional:你的项目有自己的
openspec/文件夹,并且在需要时也可以访问存储库。
默认:OpenSpec 位于你的仓库内(repo-local)
openspec init 会在你的代码旁放置一个 openspec/ 文件夹,该仓库的 specs 和 changes 就存放在那里。不涉及存储库。这是 设置你的项目 所介绍的设置,大多数项目永远不需要其他设置。
web-app (代码仓库)
└── openspec/
├── specs/
└── changes/OpenSpec 位于仓库之外,在存储库中(store-only)
该仓库不保留自己的 specs 或 changes。它规划的所有内容都位于存储库中,仓库配置中的一行将两者连接起来。
当由一个团队构建所有仓库并在一处进行规划时,这很常见。上面的示例都属于这种形态。
team-plans (存储库)
└── openspec/
├── specs/ 该仓库的 specs 位于此处
└── changes/ 其 changes 也位于此处
▲
│ store: team-plans (连接行)
web-app (代码仓库)
└── openspec/
└── config.yaml 没有其他内容OpenSpec 既在你的仓库中,也在存储库中(store-optional)
该仓库在其自身工作中仍保持 repo-local,而存储库保存共享的 specs 和 changes。在仓库内部,OpenSpec 使用你项目的 openspec/ 文件夹,并且仅当你传入 --store 时才访问存储库。
当某个仓库在存储库存在之前就已使用 OpenSpec,或当一个基本独立的仓库只是偶尔接触共享工作时,这很常见。
team-plans (存储库)
└── openspec/ 共享的 specs 和 changes
▲
│ 仅当你传入 --store team-plans 时
web-app (代码仓库)
└── openspec/ 此仓库自己的内容
├── config.yaml
├── specs/
└── changes/仓库可以起初为 repo-local,之后再将其 specs 和 changes 移入存储库。将仓库的 specs 和 changes 移入存储库 说明了如何操作。
使用存储库时工件的创建位置
使用存储库时,OpenSpec 还必须决定工件的创建位置。这取决于你的设置:
- store-only(你的项目只写入存储库):每个工件都在存储库中创建。下面的
store:行记录了这一点。 - store-optional(你的项目有自己的
openspec/文件夹,同时也使用存储库):工件会在你的项目中创建,除非你在请求中指明存储库,或为该变更传入--store。随后,你的智能体会在工作流的其余部分沿用该标志。
OpenSpec 会将工件写入以下两个位置之一:你项目的 openspec/ 文件夹,或存储库的 openspec/ 文件夹。它按以下顺序选择,第一个适用的选项生效:
- 命令上的
--store <id>。 始终优先,无论从哪个目录运行。 - 你项目的
openspec/文件夹。 如果你的项目有自己的specs/或changes/文件夹,OpenSpec 会使用它们。 - 你项目中的
store:行。 这是 store-only 项目记录其存储库的方式。 - 你机器上的
defaultStore。 以上都不适用时的后备选项。
如果这些都不适用,并且你已注册存储库,命令会失败并列出它们。当某个存储库的 ID 与你的仓库在其 origin 远程中的名称匹配时,错误信息会指出该存储库以及选择它的 store: 行。OpenSpec 绝不会自行按名称选择存储库,因为不同组织中的仓库可能同名。
当 OpenSpec 选择某个存储库时,它会在命令输出之前打印 Using OpenSpec root: ...。
store: 行(store-only 项目)
向你项目的 openspec/config.yaml 添加一行:
# web-app/openspec/config.yaml
store: team-plans现在,你或你的智能体在项目内运行的所有内容都会使用该存储库,无需输入标志:
# 在 web-app 内,已连接
openspec statusUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
No active changes. Create one with: openspec new change <name> --store team-plans- 没有该行:在 store-only 项目中运行普通命令时,OpenSpec 会停止并报错,列出你已注册的存储库。
- 提交它:克隆你项目的队友也会获得该行。他们仍需要在自己的机器上注册该存储库(设置存储库的第 3 步),否则 OpenSpec 会报错并提示他们注册。
- 与实际文件夹并存:如果你的项目同时还有
specs/或changes/文件夹,OpenSpec 会使用这些文件夹,并忽略该行,同时给出警告。
在 store-only 仓库中安装集成
从代码仓库的根目录运行 init,以安装 AI 工具集成文件,同时不将规划移回该仓库:
# 在 web-app 内,位于仓库根目录
openspec init --tools claude- 集成文件:写入代码仓库。
openspec/config.yaml:逐字节保留,包括store:行。openspec/specs/和openspec/changes/:不会在代码仓库中创建。
OpenSpec 会拒绝从代码仓库的子目录运行此命令。请从仓库根目录运行。
OpenSpec 在这里也会拒绝 --language,因为语言设置属于外部存储库的配置。请在存储库根目录运行 init,或直接编辑该配置。
你机器上的 defaultStore
如果你参与的所有项目都使用同一个存储库,设置一次即可。当 OpenSpec 没有找到标志、没有本地 openspec/ 文件夹,也没有 store: 行时,就会回退到它:
# 当其他内容都未指定存储库时,使用 team-plans
openspec config set defaultStore team-plans
# 撤销它
openspec config unset defaultStore保持本地操作的命令。 init、update、templates、schemas 以及 openspec schema 子命令仅作用于当前目录,不接受 --store。
将仓库的 specs 和 changes 移入存储库
要将仓库从 repo-local 变为 store-only:
- 将仓库
openspec/specs/和openspec/changes/中的所有内容移动到存储库中的相同文件夹。 - 删除现已为空的文件夹,使仓库的
openspec/文件夹中只保留config.yaml。 - 向该
config.yaml添加store:行。
现在在仓库内运行 openspec status,会以 Using OpenSpec root: team-plans 开头。
在存储库中工作
无论对你还是对你的智能体,工作流都不会改变。Propose、apply 和 archive 仍按原有方式运行。唯一的区别是工件的创建位置,上一节 已对此进行说明。
在 store-only 仓库内部创建变更,它会落在存储库中:
# 在 web-app 内;store: 行会将其路由到 team-plans
openspec new change add-loginUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
Created change 'add-login' at /Users/you/openspec/team-plans/openspec/changes/add-login/
Schema: spec-driven
Next: openspec status --change add-login --store team-plans- 它去了哪里:进入存储库对应的 Git 仓库,而不是你的代码旁边。
- 共享它:在你提交并推送存储库对应的 Git 仓库之前,该变更只存在于你的本地检出中。队友拉取后即可看到它。工作流写入的每个工件也是如此。
- 文档中的路径:无论文档在何处显示
openspec/路径,在存储库设置中,该文件夹都属于存储库。
当工件被创建在你意想不到的位置时,openspec doctor 会在不做任何更改的情况下检查你的设置,并针对每条发现打印修复建议:
# 检查当前根目录及其存储库
openspec doctorDoctor
Root
Location: /Users/you/openspec/team-plans
OpenSpec root: ok
Store: team-plans (metadata ok)
References
(none declared)openspec context 会列出当前目录所使用的根目录和存储库,适合在你想要同样概览但不进行检查时使用。
要在同一个编辑器窗口中打开存储库和某个仓库,以便你的智能体可以同时读取两者,请参阅 Worksets(测试版)。
从其他存储库读取规格
你的仓库可以保留自己的 openspec/ 文件夹,同时让代理读取其他存储库的规格。在仓库的 openspec/config.yaml 中的 references: 下声明该存储库:
# api-server/openspec/config.yaml
references:
- team-plans引用是只读的。你的工作仍保留在你的仓库中,引用只改变代理获取的信息。
当工作流创建工件时,其指令会获得引用存储库规格的索引,每个规格都包含一行摘要和获取它的确切命令:
<referenced_stores>
<!-- 只读上游上下文。获取你需要的内容;引用你使用的内容。 -->
存储库 team-plans (/Users/you/openspec/team-plans):
- payments:向客户收费和退款的规则。
获取:openspec show <spec-id> --type spec --store team-plans
</referenced_stores>引用还可以包含存储库的克隆 URL,用于尚未拥有该存储库的机器:
references:
- team-plans
- { id: design-system, remote: "git@github.com:acme/design-system.git" }声明 URL 后,openspec doctor 会将缺失的存储库转换为可直接粘贴的修复方案:
# output wrapped to fit
References
- team-plans: 正常 (/Users/you/openspec/team-plans)
- design-system: 引用的存储库 'design-system' 未在此机器上注册。
修复: git clone -- git@github.com:acme/design-system.git '/Users/you/openspec/design-system' &&
openspec store register '/Users/you/openspec/design-system' --id design-systemBeta 限制
- 结构可能变化:命令名称、标志和文件格式可能在不同版本之间发生变化。升级后请重新阅读此页面。
- 不同步,设计如此:OpenSpec 从不克隆、拉取或推送。过期的检出会显示过期的规格,直到你拉取为止,引用从磁盘上的任何内容读取。
- 每个存储库名称一个检出:在已注册的名称下注册第二个文件夹会失败,并提示先运行
openspec store unregister。