首页
/ spec-kit 的 agent-context 扩展:用 speckit.agent-context.update 让编码 Agent 始终读对最新的 plan

spec-kit 的 agent-context 扩展:用 speckit.agent-context.update 让编码 Agent 始终读对最新的 plan

2026-09-04 09:24:12作者:裴锟轩Denise

在 Spec-Driven Development 流程中,speckit.specify / speckit.plan 产出的 plan.md 是编码 Agent 最重要的上下文入口之一,但 Agent 自身的上下文文件(CLAUDE.md.github/copilot-instructions.mdAGENTS.md 等)并不会自动感知 plan 路径的变化。spec-kit 的 agent-context 扩展通过 speckit.agent-context.update 命令解决这一问题:它把一段带标记的“受管区块”写入(或更新)Agent 上下文文件,并始终指向最新的 plan。本文基于该扩展的命令文档 speckit.agent-context.update.md,结合 Bash 脚本Python 实现 与配套配置,完整讲解其配置项、plan 路径解析策略、路径安全约束和实际执行方式。

命令定位:一个只更新“受管区块”的上下文同步命令

speckit.agent-context.update 的功能定义非常克制:只刷新指定 Agent 上下文文件中由标记符包围的受管 Spec Kit 区块,文件内其余内容一律不动。该命令由 extension.yml 注册提供:

provides:
  commands:
    - name: speckit.agent-context.update
      file: commands/speckit.agent-context.update.md
      description: "Refresh the managed Spec Kit section in the coding agent context file"

除手动触发外,extension.yml 还声明了两个可选钩子,使区块能随开发流程自动保持同步:

hooks:
  after_specify:
    command: speckit.agent-context.update
    optional: true
    description: "Refresh agent context after specification"
  after_plan:
    command: speckit.agent-context.update
    optional: true
    description: "Refresh agent context after planning"

这带来一个清晰的自动化链路:speckit.specify 结束 → after_specify 钩子触发 → 更新上下文文件;speckit.plan 结束 → after_plan 钩子触发 → 再次更新,此时受管区块中的 plan 路径指向刚生成的 plan.md

值得一提的是设计边界:Spec Kit 本体从不触碰用户的 Agent 上下文文件,只有显式安装该扩展后才有此行为。从 extension 的 README 可以看到,specify init 默认不安装它,需通过 specify extension add agent-context 显式安装;禁用后(specify extension disable agent-context),模板中的 __CONTEXT_FILE__ 占位符保持原样,扩展配置也永远不会被读取。这一“opt-in”边界在 CLI 帮助函数 的注释中也有对应说明:CLI 侧与该扩展的上下文文件解析互不干涉。

配置文件与核心参数

命令的行为完全由扩展配置驱动。脚本读取位于 .specify/extensions/agent-context/agent-context-config.yml 的配置文件(安装后位于项目内;仓库中的模板见 agent-context-config.yml),涉及三个关键字段:

配置项 说明
context_file 单个 Agent 上下文文件的项目相对路径,例如 CLAUDE.md
context_files 多个上下文文件的可选列表。非空时逐项更新,且优先级高于 context_file
context_markers.start / .end 受管区块的起止定界符;字段缺失时回退为 <!-- SPECKIT START --> / <!-- SPECKIT END -->

仓库自带的配置模板完整展示了这三项及注释掉的示例:

context_file: ""
context_files: []
context_markers:
  start: "<!-- SPECKIT START -->"
  end: "<!-- SPECKIT END -->"

未显式配置时的自播种(self-seed)逻辑

从源码看,context_file / context_files 均为空时并非直接放弃,而是有一条回退链:Python 实现的 _collect_context_files 会依次尝试——

  1. context_files 列表(去重,Windows/Cygwin/MSYS 平台下按大小写不敏感去重);
  2. 读单个 context_file
  3. 读取 .specify/init-options.json 中记录的活动集成键(integrationai 字段),再通过内置映射表 agent-context-defaults.json 反查该集成默认对应的上下文文件。

该映射表覆盖了主流集成,例如 claudeCLAUDE.mdcopilot.github/copilot-instructions.mdcursor-agent.cursor/rules/specify-rules.mdccline.clinerules/specify-rules.mdkilocode.kilocode/rules/specify-rules.md,而 codexgeminigoose 等大量集成则统一指向 AGENTS.md(完整映射见 agent-context-defaults.json)。若映射表中也没有对应键,脚本会向 stderr 提示“请显式设置 context_file”并跳过更新。

plan 路径的解析策略:feature.json 优先,mtime 兜底

受管区块的核心内容是“当前 plan 在哪里”。当调用命令时显式传入 plan_path 参数时直接使用;省略时,解析顺序在 Bash 脚本 L258-L345Python 的 _resolve_plan_path 中一致:

第一步:优先读 .specify/feature.json 该文件由 speckit.specify 写入,其中的 feature_directory 字段指向当前特性的目录。脚本会将其拼接为 <feature_directory>/plan.md 并检查文件是否存在。这里有几个实现细节值得关注:

  • 路径中的反斜杠(PowerShell 在 Windows 下写入)会先归一化为正斜杠;
  • feature_directory 允许相对或绝对路径,Windows 下还兼容 C:/... 这种驱动器前缀形式;
  • 在比较与输出前会先 resolve() 符号链接,使 macOS 上 /var/.../private/var/... 被视为等价;
  • 最终写入区块的是项目相对路径relative_to(root).as_posix()),保证区块内容跨机器可读。

第二步:feature.json 缺失或其 plan 尚不存在时,回退到 mtime 启发式。 脚本对 specs/ 目录做递归搜索(rglob("plan.md")),而不是旧的单级 specs/*/plan.md 通配——这一步专门兼容通过 SPECIFY_FEATURE_DIRECTORY 创建的作用域嵌套布局 specs/<scope>/<feature>/plan.md,然后按修改时间倒序取最新的一份:

candidates = []
for p in specs.rglob("plan.md"):
    rel = _resolved_rel(p)
    if rel is not None:
        candidates.append((p, rel))
candidates.sort(key=lambda pr: pr[0].stat().st_mtime, reverse=True)

注意 _resolved_rel 会先 resolve() 符号链接再判断是否落在项目根内——注释中解释了原因:relative_to() 是纯词法比较,若 specs/ 下存在指向项目外部的符号链接,不先解析就可能把项目外的 plan 当成“最新”选入。这与命令文档中“any plan.md under specs/, including nested scoped layouts”的描述完全吻合。

路径安全约束:只允许项目内相对路径

命令文档明确声明:“Context file paths must stay project-relative; absolute paths, Windows drive paths, backslash separators, and .. path segments are rejected.” 源码中对应的强制检查在 _validate_context_file(Bash 侧等价逻辑见 update-agent-context.sh L219-L252),逐层拦截:

  1. / 开头的绝对路径,或 ^[A-Za-z]: 形式的 Windows 盘符路径 → 直接报错退出(退出码 1);
  2. 路径中含反斜杠 → 拒绝,强制统一使用正斜杠;
  3. 任一路径段为 .. → 拒绝,防止相对路径穿越;
  4. 兜底:把 项目根/相对路径resolve() 后仍须满足 relative_to(root),否则视为“解析后逃出项目根”拒绝。

四层校验同时覆盖了词法规则与真实文件系统的解析结果。而 context_filescontext_file 均为空且自播种也失败时,命令打印 “nothing to do” 并以成功码退出(退出码 0),保证钩子链不会因为无目标而中断。

受管区块的更新语义:创建、替换、追加

区块内容本身由 _build_section 生成,形如:

<!-- SPECKIT START -->
For additional context about technologies to be used, project structure,
shell commands, and other important information, read the current plan
at specs/001-feature/plan.md
<!-- SPECKIT END -->

若解析不到 plan 路径,则省略 at <plan_path> 一行,其余引导语保留。写回逻辑在 _upsert_section 中,对目标文件按四种情形分别处理,这解释了命令文档中“creates, replaces, or appends”的表述:

  • 起止标记都找到且顺序正确:整体替换标记之间的旧区块(含 CRLF 换行收尾的精确截断);
  • 只有起始标记:从起始标记处截断,插入新区块;
  • 只有结束标记:新区块插到文件开头,旧内容保留在区块之后;
  • 都没有:追加到文件末尾(空文件则直接写入区块);文件不存在则创建(父目录不存在时先 makedirs)。

另外两个针对 Cursor 的特殊处理值得了解:写入前统一把 \r\n\r 归一化为 \n;而文件名以 .mdc 结尾的规则文件,还会经过 ensure_mdc_frontmatter——Cursor 只自动加载带 alwaysApply: true frontmatter 的 .mdc 规则,该函数会在缺失时补上 frontmatter,或在已有 frontmatter 中修复/追加 alwaysApply: true,且尽量保留原有注释与格式。

如何执行该命令

在已初始化且已安装扩展的项目根目录下,按平台选择入口(见命令文档的 Execution 一节):

# Bash
.specify/extensions/agent-context/scripts/bash/update-agent-context.sh [plan_path]

# PowerShell
.specify/extensions/agent-context/scripts/powershell/update-agent-context.ps1 [plan_path]

plan_path 为可选参数;省略时走上文所述的 feature.json → mtime 两级解析。在 Agent 中则按各集成语法调用规范命令名 speckit.agent-context.update(点号语法 /speckit.agent-context.update、连字符语法 /speckit-agent-context-update 等,具体见 extension 的 README)。Bash 脚本执行时会先探测可用解释器:优先 SPECKIT_PYTHON 环境变量指向的解释器,其次 python3python,要求该解释器是 Python 3 且可导入 yaml;找不到时打印提示(pip install pyyaml)并以 0 退出跳过,而非失败——这让钩子在缺依赖时不阻塞主流程。

验证与回归:测试覆盖了什么

仓库为这条命令链提供了成体系的测试,便于读者核对上文行为是否如描述一致:

这些测试与 scripts 目录下的三端实现共同构成了该命令的“实现事实”边界:配置解析、自播种、路径校验、plan 解析、upsert 与 .mdc frontmatter 修复,均可在对应文件中按本文给出的相对路径逐条对照。

小结

speckit.agent-context.update 是一个职责单一的上下文同步命令:以 context_file/context_files/context_markers 三项配置为输入,以 feature.json 优先、mtime 兜底的策略定位最新 plan,以“创建/替换/追加”的幂等语义维护标记区块,同时用四层校验保证只写项目内相对路径。对使用者而言,记住三件事即可:显式安装扩展(specify extension add agent-context)、按需编辑 .specify/extensions/agent-context/agent-context-config.yml、在 specs/ 结构变化后手动或经钩子触发一次更新——受管区块内的 plan 指针就会指向正确的位置,而你在上下文文件里手写的其余内容不会被改动。

登录后查看全文
热门项目推荐
相关项目推荐