spec-kit 扩展系统实战指南:Extension 的搜索、安装、目录信任模型与 Hooks 机制
本文围绕 spec-kit(Spec Kit 扩展包管理工具)的官方参考文档 docs/reference/extensions.md 展开,系统讲解 specify extension 命令族的完整用法(search / add / remove / list / info / update / enable / disable / set-priority)、扩展目录(Catalog)的分层信任模型、四层配置合并机制以及 .specify/extensions.yml 中扩展注册与 Hook 系统的底层实现。读完本文,你可以独立完成扩展的发现、安装、治理与安全审计,并理解命令优先级、Hook 条件表达式在 src/specify_cli/extensions/init.py 中的实际执行逻辑。
一、扩展是什么:给 Spec Kit 增加领域能力
扩展(Extension)为 Spec Kit 添加新的能力——领域专属命令、外部工具集成、质量门禁等。它们引入超出内置 Spec-Driven Development 工作流的新命令与新模板。从源码结构看,扩展管理由 src/specify_cli/extensions/init.py 中的 ExtensionManager、ExtensionManifest、ExtensionCatalog 等类实现:扩展以 extension.yml 清单描述自身(命令、脚本、模板、Hook、配置默认值),安装后其命令会自动注册到当前已安装的 AI 编码代理集成中。
一个真实的官方扩展例子是 git 扩展 extensions/git/extension.yml:它声明了 5 个命令(speckit.git.feature、speckit.git.validate、speckit.git.remote、speckit.git.initialize、speckit.git.commit),要求 speckit_version: ">=0.2.0",并声明了覆盖 before_* / after_* 全部核心命令事件的 Hook(如 before_specify 创建特性分支、before_implement 可选自动提交)。清单还包含 requires.tools(本例中 git 为可选依赖)、tags 与 config.defaults 等字段。
注意: 所有扩展命令都要求项目已经过
specify init初始化(源码中对应_require_specify_project守卫,见 src/specify_cli/extensions/_commands.py)。
二、扩展生命周期:搜索与安装
2.1 搜索可用扩展
specify extension search [query]
| 选项 | 说明 |
|---|---|
--tag |
按标签过滤 |
--author |
按作者过滤 |
--verified |
仅显示已验证扩展 |
不带 query 时列出所有可用扩展。该命令会在所有激活目录(active catalogs)中搜索匹配项。对应实现是 ExtensionCatalog.search(query, tag, author, verified_only),搜索前会经过 _get_merged_extensions() 合并各目录条目并做缓存(缓存有效期为 CACHE_DURATION = 3600 秒,缓存目录位于 .specify/extensions/.cache/)。
2.2 安装扩展
specify extension add <name>
| 选项 | 说明 |
|---|---|
--dev |
从本地目录安装(用于开发) |
--from <url> |
从自定义 URL 安装,而非从目录安装 |
--force |
已安装时覆盖 |
--priority <N> |
解析优先级(默认 10;数值越小优先级越高) |
安装来源可以是目录、URL 或本地目录三种之一,命令在 src/specify_cli/extensions/_commands.py 的 extension_add 处理器中定义。从源码结构看,安装链路还包含若干安全加固:
install_from_directory/install_from_archive会先校验extension.yml清单(ExtensionManifest要求schema_version、extension、requires、provides必填字段,命令名必须匹配^speckit\.([a-z0-9-]+)\.([a-z0-9-]+)$,且不得与内置核心命令冲突——核心命令集合CORE_COMMAND_NAMES来自 templates/commands/ 目录发现);--from <url>下载路径强制 HTTPS(本地回环除外)、限制响应体大小、检测归档格式(ZIP 或 tar.gz/tgz),并做 TOCTOU 安全的临时文件处理(见 src/specify_cli/_download_security.py 与install_extension_from_url);- 安装后调用
_register_commands_for_active_agent把命令写入当前激活代理的命令目录;卸载/禁用时会unregister_agent_artifacts反向清理。
2.3 卸载、更新与启停
卸载:
specify extension remove <name>
| 选项 | 说明 |
|---|---|
--keep-config |
移除时保留配置文件 |
--force |
跳过确认提示 |
配置文件默认会被备份;--keep-config 让配置留在原处,--force 跳过确认。
列出已安装扩展(含状态、版本、命令数量):
specify extension list
| 选项 | 说明 |
|---|---|
--available |
显示可用(未安装)扩展 |
--all |
同时显示已安装与可用扩展 |
查看详情(描述、版本、命令、配置):
specify extension info <name>
更新(不指定名称时更新全部已安装扩展):
specify extension update [<name>]
启停(不删除,仅停用——被禁用的扩展不会被加载,其命令不可用):
specify extension enable <name>
specify extension disable <name>
设置解析优先级(多个扩展提供同名命令时,数值最小的优先):
specify extension set-priority <name> <priority>
三、目录(Catalog)管理:发现与安装的安全边界
3.1 信任模型:discovery-only vs. install sources
目录分两种,二者的区分是安全边界而非功能限制:
- Install sources(
install_allowed: true)——你信任的、可从中安装的目录。内置的default(官方)目录属于此类,你自己编写并审核过的目录也可以。 - Discovery-only 目录(
install_allowed: false)——只用于发现扩展的搜索面,不可安装。内置的community目录就是 discovery-only,开箱即用于search,无需手动添加。
community 被刻意设计为只发现,因为它是一个开放的、未经审核的列表;若把其中所有条目变成一条命令可安装,就等于在没有任何审查的情况下拉取任意第三方代码。
切勿把 discovery-only 目录改成
install_allowed。 那会破坏"发现与安装分离"的全部意义。正确做法有两种:
- 用
--from直接安装单个已审核的扩展(无需自建目录)。通过specify extension info <name>获取候选归档 URL(discovery-only 条目会打印 "Candidate archive" URL),审核该发布归档后再安装:在你审核之前,应视该 URL 为不可信——它来自未审核目录。specify extension info <name> # 查看候选归档 URL specify extension add <name> --from <archive-url>- 策展一个自己控制并审核的目录,并把那个目录标记为
install_allowed: true——适用于需要受治理、可复用安装源的场景(例如组织级)。
3.2 目录增删查
列出当前目录栈(含优先级与安装权限):
specify extension catalog list
添加目录(写入项目的 .specify/extension-catalogs.yml):
specify extension catalog add <url>
| 选项 | 说明 |
|---|---|
--name <name> |
必填。目录唯一名称 |
--priority <N> |
优先级(默认 10;数值越小优先级越高) |
--install-allowed / --no-install-allowed |
将目录标记为受信安装源。仅对你拥有并审核的目录启用;发现类目录保持关闭(默认)。绝不对未审核的公开目录启用 |
--description <text> |
可选描述 |
移除目录:
specify extension catalog remove <name>
从源码结构看,catalog_add 处理器(src/specify_cli/extensions/_commands.py)强制 URL 必须使用 HTTPS(localhost 的 HTTP 除外),URL 校验逻辑在 src/specify_cli/catalogs.py 的 CatalogStackBase._validate_catalog_url 中实现——它还显式校验端口语法与 hostname 非空,避免畸形 URL 在拉取阶段才以晦涩错误暴露。
3.3 目录解析顺序
目录按以下顺序解析(首个命中者生效),对应 ExtensionCatalog.get_active_catalogs() 的实现:
- 环境变量 —
SPECKIT_CATALOG_URL覆盖所有目录(此时返回单一custom条目,install_allowed=True,且使用非默认目录时会向 stderr 打印信任警告); - 项目配置 —
.specify/extension-catalogs.yml; - 用户配置 —
~/.specify/extension-catalogs.yml; - 内置默认 — 官方
default目录(允许安装,priority 1)+community目录(discovery-only,priority 2)。
拥有并审核的目录在 .specify/extension-catalogs.yml 中的示例:
catalogs:
- name: "my-org-catalog"
url: "https://example.com/catalog.json"
priority: 5
install_allowed: true
description: "Our approved extensions"
每个目录条目由 CatalogEntry 数据类描述(字段:url、name、priority、install_allowed、description),拉取时每个 URL 独立缓存并校验 payload 形状(_validate_catalog_payload 要求 JSON 对象且含 schema_version 与 extensions 字段,防止被污染缓存导致崩溃)。
四、扩展配置:四层合并模型
大多数扩展在安装目录中携带配置文件:
.specify/extensions/<ext>/
├── <ext>-config.yml # 项目配置(纳入版本控制)
├── <ext>-config.local.yml # 本地覆盖(被 gitignore)
└── <ext>-config.template.yml # 模板参考
配置按以下顺序合并(优先级从低到高,后者覆盖前者):
- 扩展默认值(来自
extension.yml的config.defaults) - 项目配置(
<ext>-config.yml) - 本地覆盖(
<ext>-config.local.yml) - 环境变量(
SPECKIT_<EXT>_*)
为新安装的扩展初始化配置,复制模板即可:
cp .specify/extensions/<ext>/<ext>-config.template.yml \
.specify/extensions/<ext>/<ext>-config.yml
这一模型由 ConfigManager 类实现(src/specify_cli/extensions/init.py 中 ConfigManager,含 _get_extension_defaults → _get_project_config → _get_local_config → _get_env_config 逐层 _merge_configs)。以 git 扩展为例,其 extensions/git/config-template.yml 展示了典型的可用配置项:
branch_numbering:sequential(001、002…)或timestamp(YYYYMMDD-HHMMSS);branch_template:分支名模板,支持{author}、app、{number}、{slug}令牌,要求{slug}不得出现在{number}之前;branch_prefix:简写命名空间,如features/{app};init_commit_message:仓库初始化时的提交信息(默认[Spec Kit] Initial commit);commit_style:fixed(使用固定提交信息)或conventional(让代理检查 diff 生成 Conventional Commit 消息);auto_commit:default全局开关 + 按命令(before_clarify、before_plan、before_implement、after_specify等)的enabled与自定义message。
扩展默认值同样声明在清单里,git 扩展的 extensions/git/extension.yml 中 config.defaults 即 branch_numbering: sequential、branch_template: ""、branch_prefix: ""、init_commit_message: "[Spec Kit] Initial commit"。
五、项目级扩展注册与 Hooks:.specify/extensions.yml
Spec Kit 把项目级扩展注册与 Hook 配置保存在 .specify/extensions.yml 中,包含已安装扩展列表、全局设置与在 Spec Kit 命令前后触发的 Hook:
installed:
- git
- my-extension
settings:
auto_execute_hooks: true
hooks:
before_implement:
- extension: git
command: speckit.git.commit
enabled: true
optional: true
priority: 10
prompt: "Commit outstanding changes before implementation?"
description: "Auto-commit before implementation"
after_implement:
- extension: my-extension
command: speckit.my-extension.verify
enabled: true
optional: false
priority: 5
description: "Run verification after implementation"
5.1 配置字段说明
顶层 installed 列表记录项目内已安装的扩展;settings 映射保存项目级扩展设置;hooks 按事件分组 Hook 注册。auto_execute_hooks 默认 true,但当前保留未用——Hook 呈现与调用时并不读取它。
每个 Hook 条目支持的字段:
| 字段 | 说明 |
|---|---|
extension |
注册该 Hook 的扩展 ID |
command |
与 Hook 关联的扩展命令 |
enabled |
Hook 是否激活;enabled: false 的 Hook 被跳过 |
optional |
是否可选。true 时 Hook 连同 prompt 一起呈现、可跳过;false 时作为自动 Hook 发出(含 EXECUTE_COMMAND 标记) |
priority |
Hook 优先级元数据。注册条目使用 >= 1 的整数;从 manifest 安装的条目未声明时默认 10。当前命令模板按 YAML 配置顺序呈现 Hook,不按 priority 排序 |
prompt |
询问是否运行可选 Hook 时显示的消息 |
description |
人类可读的 Hook 作用说明 |
condition |
可选表达式,由 HookExecutor 求值(使用 config.<path> 或 env.<VAR>,配合 is set、==、!=)。当前命令模板不求值 condition,带有非空 condition 的 Hook 会被跳过 |
Hook 事件名标识触发时机,通常形如 before_<command> / after_<command>,例如 before_implement、after_implement、before_tasks、after_tasks。git 扩展的清单即为范例:before_constitution 自动初始化仓库、before_specify 创建特性分支(两者 optional: false),其余 before_* / after_* 事件挂载可选的 speckit.git.commit 自动提交 Hook。
5.2 源码印证:优先级归一化与条件求值
优先级处理由 src/specify_cli/extensions/init.py 的 normalize_priority() 实现:布尔值、int() 无法转换的非数字值、小于 1 的值都回落到默认值 DEFAULT_HOOK_PRIORITY = 10;数字字符串与有限浮点数经 int() 强制转换,非有限浮点数则可能直接报错而非回落。扩展清单在安装时即拒绝非法 Hook 优先级。
HookExecutor.get_hooks_for_event() 返回按 priority 升序排列(数值小者在前、稳定排序保持插入顺序)的启用 Hook 列表;但正如文档所述,当前命令模板直接读取 Hook 列表并按其配置的 YAML 顺序呈现,并不使用优先级排序——理解这一差异有助于解释为什么手动调整 YAML 顺序比改 priority 更直接影响呈现顺序。
条件求值 _evaluate_condition() 支持的正则模式与文档描述一致:config.<key.path> is set、config.<key.path> == 'value' / != 'value'(布尔值会归一化为小写 true/false 再比较)、env.<VAR_NAME> is set、env.<VAR_NAME> == 'value' / != 'value';求值失败时默认不执行(should_execute_hook 捕获异常返回 False)。
此外,扩展 add / remove / enable / disable 之后会调用 refresh_integration_events 刷新原生事件配置;若刷新失败,CLI 会打印警告并提示重跑 specify integration upgrade <key>(见 _refresh_events_and_warn),以避免"被禁用扩展的 Hook 仍然残留生效"。
六、常见问题(FAQ)
为什么 search 找不到某个扩展?
检查扩展名拼写。扩展可能尚未发布,或位于你未添加的目录中。用 specify extension catalog list 查看当前激活的目录。
为什么扩展命令没有出现在我的 AI 编码代理中?
用 specify extension list 确认扩展已安装且启用。如果显示已安装,重启你的 AI 编码代理——它可能需要重新加载才能生效。
如何设置扩展配置? 复制扩展自带的配置模板:
cp .specify/extensions/<ext>/<ext>-config.template.yml \
.specify/extensions/<ext>/<ext>-config.yml
各配置层与覆盖规则见上文"扩展配置:四层合并模型"。
如何解决版本不兼容错误?
把 Spec Kit 更新到扩展所要求的版本(由清单 requires.speckit_version 约束,如 git 扩展要求 >=0.2.0)。
扩展由谁维护? 大多数扩展由各自作者独立创建与维护。Spec Kit 维护者不审查、不审计、不背书、不支持扩展代码。安装前请审查扩展源码,使用风险自负;针对特定扩展的问题请联系其作者或在其仓库中提交 issue。
七、小结
spec-kit 的扩展体系由三块组成:命令层(specify extension search/add/remove/list/info/update/enable/disable/set-priority)、目录信任层(install_allowed 区分安装源与发现面,四级解析顺序,HTTPS 强制)与项目注册层(.specify/extensions.yml 的 installed / settings / hooks + 四层配置合并 + 条件化 Hook)。实践中的关键纪律是:保持 community 目录只读发现、用 --from 安装单个已审核扩展或自建受信目录,并在安装任何第三方扩展前审查其 extension.yml 与命令模板——这也是官方文档在 FAQ 中明确给出的使用建议。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00