Spec Kit Bundles 完全指南:bundle.yml 清单、specify bundle 命令体系与 Bundler 源码实现
【免费下载链接】spec-kit💫 Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit
Bundle 是 Spec Kit 把已有组件(extensions、presets、workflows、steps)组合成一个版本化、可安装单元的分发层。读完本文,你将掌握specify bundle全部子命令(search / info / install / update / remove / list / init / validate / build / catalog)的参数与行为边界,理解bundle.yml清单的完整结构与校验规则,并能从源码层面弄清安装幂等性、集成(integration)冲突检测、来源记录(provenance)与目录栈(catalog stack)的底层实现。
1. Bundle 是什么:组合层而非运行时层
用 Bundles 参考文档 的表述:extensions 和 presets 是"原语"(primitives),而 bundle 是一层分发与组合(distribution and composition)机制——它声明一个团队或角色所需的全部组件,并通过每个组件自身的安装机制一次性装好。Bundle 本身不引入任何新的运行时行为。
一条 bundle 的完整生命周期可以概括为四个动作:
- 描述:由
bundle.yml清单声明元数据、版本依赖与组件引用; - 发现:与其他组件共用同一套目录栈(catalog stack)被发现;
- 解析:安装时把声明的组件按固定版本(pinned version)解析成具体的安装计划;
- 执行:检查唯一的跨 bundle 冲突点(活动集成),幂等地应用每个组件,并写入完整的来源记录,以便日后干净地移除或刷新。
从源码结构看,这套机制全部落在src/specify_cli/bundler/包中,按职责分层:
- 模型层 models/:manifest.py(清单解析与结构校验)、records.py(已安装 bundle 的来源记录)、catalog.py(目录栈);
- 服务层 services/:resolver.py(清单 → 安装计划)、installer.py(执行安装/卸载)、conflict.py(冲突检测)、catalog_stack.py(跨源解析与搜索)、packager.py(构建产物)、validator.py 与 references.py(校验);
- CLI 层 commands/bundle/init.py:只负责参数解析与 Rich 输出渲染,业务逻辑全部委托给服务层(源码注释称之为 Principle I:"thin commands over services")。
2. bundle.yml 清单:结构与校验规则
2.1 一个真实的清单示例
仓库自带示例 examples/bundles/business-analyst/bundle.yml,完整展示了清单的全部顶层字段:
schema_version: "1.0" bundle: id: "business-analyst" name: "Business Analyst" version: "1.0.0" role: "business-analyst" description: "Spec-Driven Development setup for business analysts: requirements elicitation, traceability, and acceptance criteria." author: "spec-kit-examples" license: "MIT" requires: speckit_version: ">=0.9.0" tools: [] mcp: [] provides: extensions: - id: "agent-context" version: "1.0.0" presets: - id: "requirements-elicitation" version: "1.0.0" priority: 10 strategy: "append" steps: - id: "capture-requirements" - id: "trace-acceptance-criteria" workflows: - id: "requirements-to-spec" version: "1.0.0" tags: ["requirements", "traceability", "analysis"]examples/bundles/下还有 developer、product-manager、security-researcher 三个同构示例,可对照阅读。
2.2 字段校验规则(来自 manifest.py 源码)
BundleManifest 在from_dict之后通过structural_errors()做结构校验,规则如下:
| 规则 | 说明 |
|---|---|
schema_version | 必须在支持集合内,当前为{"1.0"}(SUPPORTED_SCHEMA_VERSIONS) |
| 必填字段 | bundle.id、bundle.name、bundle.version、bundle.role、bundle.description、bundle.author、bundle.license、requires.speckit_version |
bundle.version | 必须是合法 semver |
bundle.id | 必须是文件系统安全 slug:^a-z0-9?$(小写字母、数字、.、_、-,禁止路径分隔符)。这是因为 id 会被直接拼进产物文件名<id>-<version>.zip,防止路径穿越 |
| 组件版本 pin | extensions、presets、workflows 条目必须固定version;steps 可以不定版本 |
| 版本号 | 一经声明必须是合法 semver |
| presets 额外要求 | 必须声明整数priority;strategy必须是replace、prepend、append、wrap之一 |
另外两个容易踩坑的解析细节(源码注释中明确记录):
- YAML null 不会变成字符串 "None":
author:后面留空在 YAML 中是 null,解析器通过_text()把它映射为空字符串,再由必填检查拒绝,而不是放行一个"None"的作者; integration写成裸字符串会被拒绝:如果integration存在但不是 mapping(例如直接写integration: copilot),解析直接报错,而不是静默丢弃、把 bundle 错误地变成"集成无关"。
2.3 组件引用与集成声明
每个组件条目被解析为ComponentRef(kind/id/version/source/priority/strategy)。可选的integration字段(mapping 形式,含id)声明该 bundle 目标集成;若清单不声明integration,则 bundle 是"集成无关"的(is_agnostic()为 True),安装时继承项目当前活动集成。
requires.tools与requires.mcp是软依赖:解析安装计划时它们只产生警告("Requires external tools: ..."),不会阻断安装;而requires.speckit_version是硬门控——见下文 resolver。
3. 消费侧命令:search / info / install / update / remove / list / init
3.1 search:在目录栈中检索
specify bundle search [query]| 选项 | 说明 |
|---|---|
--offline | 不访问网络 |
--json | 输出机器可读 JSON |
在所有活动目录中搜索匹配查询的 bundle。不带查询时列出全部可用 bundle,附版本、角色、来源和信任指标(org 维护目录中的条目为verified,其余为community),让你在安装前判断信任级别。
从 catalog_stack.py 的search()看,匹配是对 id、name、role、description、tags 做小写子串检索;更关键的是每个 bundle id 只会出现一次,且解析到最高优先级的来源——先按优先级占用 id,再过滤查询,避免低优先级的同 id 影子条目把"你装不到的那个版本"广告出来。--json输出中每条记录还包含install_policy(install-allowed/discovery-only)与trust字段。
3.2 info:安装前的完整预览
specify bundle info <bundle_id>| 选项 | 说明 |
|---|---|
--offline | 不访问网络 |
--json | 输出机器可读 JSON |
显示 bundle 的完整元数据以及它完全展开的组件集合——每个 extension、preset、step、workflow 及其固定版本,外加 preset 的 priority 与 strategy,并附信任指标。这个预览与install实际应用的计划是同一份:你可以精确看到将被添加什么。与已安装 bundle 的可预见重叠也会在这里被提示。
源码中这条命令刻意做到"要么完整、要么报错":bundle_info 会真正下载并解析远端清单,而不是退化成目录里的provides计数——否则用户可能把一个无法验证的 bundle 误认为已知可安装。清单下载失败会以非零码退出,而不是静默降级。
3.3 install:一条命令装完整个组件栈
specify bundle install <bundle_id | path>| 选项 | 说明 |
|---|---|
--integration | 覆盖初始化/安装时使用的集成 |
--offline | 不访问网络 |
参数既可以是目录中的 bundle id,也可以是本地路径:构建好的.zip产物、bundle 目录、或bundle.yml文件本身。本地源不经过目录栈,直接安装(_local_manifest_source()在目录解析之前处理这三类本地形态)。
安装语义的几个关键点(均与文档一致,并可在 installer.py 中逐条印证):
- 自动初始化:当前目录还不是 Spec Kit 项目时,
install会先初始化项目,让全新 checkout 一条命令进入可用状态。此时--integration用于选择集成(优先级:显式覆盖 → bundle 声明 → 默认值)。注意源码里有一道顺序保障:所有硬兼容门控(Spec Kit 版本、集成冲突)都在specify init之前解析,避免一个不兼容的 bundle 先初始化出项目状态、随后才在版本检查上失败而留下半截状态。 - 不覆盖已初始化的项目:
--integration不能绕过已初始化项目的活动集成。若 bundle 目标集成与项目不一致,安装直接中止且不做任何改动;若项目活动集成无法确定(缺失或不可读的.specify/integration.json)而 bundle 又 pin 了集成,则用--integration确认目标。集成无关的 bundle 继承项目活动集成。 - 幂等:已存在的组件被跳过。这里的"已存在"判断是按 id 而非版本的(见 3.4 的 pin 语义)。
- 失败不留记录:安装失败时不写任何来源记录;本次运行中已装上的组件会被尽力回滚——回滚错误被吞掉,因此磁盘上可能残留部分状态。源码中回滚是"有界"的:只回滚本次调用新装的组件,事先就存在的组件永不被回滚;组件归属采用引用计数式逻辑,独立安装(未被任何 bundle 记录追踪)的组件绝不会被记到 bundle 名下,防止日后
remove时误删(源码注释引用的 FR-022)。
3.4 update:重新解析并刷新组件
specify bundle update [<bundle_id>]| 选项 | 说明 |
|---|---|
--all | 更新所有已安装 bundle |
--integration | 覆盖刷新组件时使用的集成;仅在项目活动集成无法确定时生效 |
--offline | 不访问网络 |
重新解析 bundle,并通过每个原语自己的 update 路径刷新组件:把已装组件提升到 bundle 新 pin 的版本,同时保留原语级覆盖(例如 preset priority)。update走的是install_bundle(..., refresh=True)路径:已安装组件不再被跳过而是重新应用;旧版本拥有、新清单不再提供的组件会被卸载(前提是其他已安装 bundle 不再需要它们);首次安装时间(installed_at)在跨刷新时保留。
Pin 只在安装时强制。幂等检查是按 id 的、版本无感的:已存在的组件在
install期间被跳过,不会拿磁盘上的版本与清单 pin 比较。因此版本 pin 只有在 bundler 真正首次安装或刷新某个组件时才保证被应用。用specify bundle update可以把每个自有组件重新按其 pin 版本应用一遍。
3.5 remove / list / init
specify bundle remove <bundle_id> specify bundle list [--json] specify bundle init [<bundle_id>] [--integration ...] [--offline]- remove:只卸载该 bundle 贡献的组件;其他已安装 bundle 仍需要的组件会原样保留,不做连带删除。实现上由 records.py 的
components_still_needed()算出"其他 bundle 仍需要的 (kind, id) 集合",再逐组件判定卸载或跳过;移除中途失败时,bundle 记录保持不动,错误信息会明确说明可能已部分卸载。 - list:列出项目中已安装 bundle 的版本、组件数与安装时间。
- init:先确保当前目录是 Spec Kit 项目(必要时幂等初始化),再可选地安装给定 bundle,适合作为新 checkout 的显式一步式引导。
4. 创建侧命令:validate / build / publish
4.1 validate:清单是否良构、引用能否解析
specify bundle validate [--path <bundle目录|bundle.yml>] [--offline]| 选项 | 说明 |
|---|---|
--path | bundle 目录或bundle.yml(默认当前目录) |
--offline | 只对照 bundled/已安装组件校验引用 |
报告bundle.yml是否良构、以及每个声明的组件引用能否解析。引用依次对照 bundled 组件、项目已安装组件、以及(在线时)活动目录来检查。只有当引用在所有可查位置都确定不存在时校验才失败——即有活动目录可达且确认该组件缺失。无法验证的引用(离线校验、或目录不可达)被降级为警告,让作者可以继续编写,而不是直接跑挂。
4.2 build:产出单一版本化分发产物
specify bundle build [--path <bundle目录>] [--output <输出目录>]| 选项 | 说明 |
|---|---|
--path | bundle 目录(默认当前目录) |
--output | 产物输出目录 |
从 bundle 目录生成一个版本化、可分发的.zip产物,命名<id>-<version>.zip,内嵌清单,可直接specify bundle install <artifact.zip>安装。packager.py 中的构建约束值得作者注意:
- 缺少
bundle.yml或README.md直接拒绝——每个 bundle 必须随附描述文档; - 清单结构无效时拒绝构建并指向
validate; - 所有文件读取被限制在 bundle 源目录内(路径收敛),排除
.git、__pycache__、.DS_Store; - 产物使用固定的 zip 时间戳(zip epoch),保证字节级可复现。
4.3 publish:托管产物与目录条目
Bundle 作者先在本地校验、打包,再把生成的产物和目录元数据托管到用户可访问的位置。目录条目指向 bundle 产物,但bundle.yml内部声明的组件仍会经过 bundled 组件、已安装组件,或活动的 extension / preset / workflow / step 目录来解析。
如果你的 bundle 引用了非默认目录中的组件,请在文档中写明这些目录 URL,并在一个添加了对应目录的干净项目上实测安装路径。提交社区 bundle 时,应把这份依赖解析证据附在 GitHub 仓库的 Bundle Submission issue 模板中。社区 bundle 的完整提交清单(公开仓库 + 合法bundle.yml、带specify bundle build产物的版本化 release、说明文档、目录条目建议、干净项目测试证据)与维护者的审核范围,见 Community Bundles 文档——维护者只核验提交元数据完整、格式正确、链接可达,不审计也不背书bundle 及其安装组件的行为,安装前请自行审查清单与组件来源。
5. 目录源管理:优先级、策略与信任
bundle 通过优先级有序的目录源栈(project、user、built-in 三级作用域)被发现。
# 查看活动目录栈(含各来源的作用域与安装策略) specify bundle catalog list # 添加一个项目作用域的目录源 specify bundle catalog add <url> [--policy install-allowed|discovery-only] [--priority <n>] [--id <id>]| 选项 | 说明 |
|---|---|
--policy | install-allowed或discovery-only |
--priority | 来源优先级(越小越优先;默认 10) |
--id | 显式指定来源 id |
# 移除项目作用域的目录源 specify bundle catalog remove <id_or_url>持久化在.specify/bundle-catalogs.yml中(见 catalog_config.py),磁盘形状为{schema_version, catalogs: [{id, url, priority, install_policy}]}。几个实现层面的约束:
- 内置默认源不可删除;要用同 id 来源去覆盖它。
- HTTPS-only:http(s) 目录 URL 只允许 https(localhost 可用 http),无主机的 URL 在写入时即被拒绝;本地路径会被规范化为绝对路径再存储,因此
remove可以按当时添加的相对路径反查。 - discovery-only 的来源只能 search/info,不能 install:
install与update解析到 discovery-only 来源会直接报错。内置 community 源就是 discovery-only,按 id 安装需要先显式添加一个 install-allowed 目录(显式目录的默认优先级高于内置 community 源)。 - 信任指标:org 维护目录条目显示
verified,其余显示community(_trust_level())。
注意命令的作用域差异:
search和info在任何位置都可用——没有项目时回退到 built-in/user 目录栈。而改变状态的命令(list、update、remove、catalog)要求已用specify init初始化过的项目;install和init在目录未初始化时会按需自动初始化。
5.1 远端下载的安全约束
当info/install走目录解析时,清单从条目的download_url下载。CLI 层 对此有一整套硬约束:file://、裸文件系统路径、无 scheme 的值一律拒绝(从磁盘安装请直接传路径参数);非 HTTPS 下载直接拒绝(重定向目标也要逐个校验);下载有字节上限(MAX_DOWNLOAD_BYTES);目录条目若带sha256则下载后逐字节校验。对 GitHub release 下载链接,会先解析为 REST API 资产 URL 以兼容私有/SSO 仓库。这些约束在--offline下依然先行检查,避免离线模式报出误导性的"网络已禁用"。
6. 底层机制速览:来源记录、解析门控与冲突检测
来源记录(provenance):每次成功安装后,records.py 把InstalledBundleRecord(bundle_id、version、contributed_components、installed_at)写入.specify/bundle-records.json,schema 版本1.0。文件读取路径带防符号链接/路径穿越的收敛检查;schema 主版本不匹配时快速失败而不是错误解析——这是 remove/update 能"精确只碰本 bundle 组件"的数据基础。
解析门控:resolver.py 的resolve_install_plan()把清单展开为InstallPlan,并执行两道硬门控:Spec Kit 版本门控(satisfies()检查requires.speckit_version,不满足即拒绝安装)与集成兼容性检查。info的预览与install的执行共享这同一个计划来源,保证"你看到的正是将装上的"。
冲突检测:conflict.py 确认文档的说法——唯一的跨 bundle 硬冲突点是活动集成:bundle pin 的集成与项目活动集成不一致即中止。组件级重叠(例如另一个 bundle 也提供了同名 preset)只是信息性提示,实际由原语机制自己的优先级规则裁决。install前打印的黄色!提示即来自这里。
执行与回滚:installer.py 的install_bundle()对计划逐组件执行is_installed → install/skip,全部成功后才 upsert 记录;任何异常触发_rollback(),逆序移除本次新装组件(尽力而为、吞掉移除错误),与文档"On failure, no provenance record is written"完全对应。
7. 实战走查:从示例 bundle 到安装
以仓库内置的 business-analyst 示例走一遍完整链路:
# 1. 检索并预览(任何位置可用) specify bundle search business specify bundle info business-analyst # 2. 在干净目录一条命令初始化 + 安装(目录未初始化时自动 init) specify bundle install business-analyst # 或从本地源安装(目录 / bundle.yml / .zip 均可,不查目录栈) specify bundle install ./examples/bundles/business-analyst specify bundle install ./business-analyst-1.0.0.zip # 3. 查看已安装与来源记录 specify bundle list # 4. 需要升级时,把组件刷新到清单新 pin 的版本 specify bundle update business-analyst # 5. 卸载(其他 bundle 仍需要的组件会保留) specify bundle remove business-analyst作者侧则是对称的:
# 1. 校验清单良构与引用可达 specify bundle validate --path ./my-bundle # 2. 产出 <id>-<version>.zip specify bundle build --path ./my-bundle --output ./dist # 3. 托管 dist 下的产物 + 目录条目;非默认目录依赖需随提交附解析证据 # 4. 在干净项目上按 5.1 节的 HTTPS/策略约束实测安装8. 小结
| 维度 | 结论 |
|---|---|
| 定位 | 分发与组合层,零新增运行时行为,复用各原语自身安装机制 |
| 清单 | bundle.yml,schema 1.0;id 为安全 slug;extensions/presets/workflows 必须 pin semver;presets 必须声明 priority + strategy |
| 安装 | 幂等(按 id);失败不留记录 + 有界回滚;集成冲突是唯一直止点 |
| 更新 | update才保证按 pin 版本重新应用所有自有组件 |
| 移除 | 引用计数,无连带删除 |
| 发现 | project > user > built-in 优先级目录栈;install-allowed / discovery-only 策略;verified / community 信任指标 |
| 分发 | build 出可复现<id>-<version>.zip;HTTPS-only + sha256 校验;本地路径安装不走目录栈 |
本文所有行为描述均以当前仓库src/specify_cli/bundler/与 docs/reference/bundles.md 为准;清单字段级细节可对照 manifest.py,安装/回滚/引用计数逻辑可对照 installer.py 与 records.py。
【免费下载链接】spec-kit💫 Toolkit to help you get started with Spec-Driven Development项目地址: https://gitcode.com/GitHub_Trending/sp/spec-kit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考