goose 文档自动化管线:用「确定性脚本 + AI Recipe」让 CLI 参考文档与代码自动同步
【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose
本文基于 goose 仓库 documentation/automation/ 目录的官方文档与配套脚本源码,讲解 goose 如何构建一套文档自动化管线:在新版本发布时,通过「构建二进制 → 解析--help→ 确定性 diff → AI 合成变更说明 → AI 外科手术式更新文档」五步流程,自动检测 CLI 命令与选项的变更,并把 CLI Commands Guide 保持与代码一致。读完后你可以完整理解这套「脚本负责确定性、AI 负责语义合成」的混合管线设计,并能在本地或 GitHub Actions 中复现整条管线。
文档自动化的总体设计
goose 的 documentation/automation/ 目录存放一组自动化管线,目标是「让 goose 文档与代码变更保持同步」。每个自动化项目追踪特定类型的代码变更,并更新对应的文档:
| 项目 | 状态 | 追踪对象 | 更新对象 |
|---|---|---|---|
| cli-command-tracking | Planned | CLI 命令与选项 | CLI 文档 |
| provider-tracking | Planned | 受支持的 AI Provider | Provider 文档 |
| extension-tracking | Planned | 内置扩展 | 扩展文档 |
目前仓库中真正落地的完整案例是cli-command-tracking,其余项目仍在规划中。所有自动化项目遵循统一的标准目录结构:
project-name/ ├── README.md # 项目专属文档 ├── TESTING.md # 该自动化如何测试 ├── config/ # 配置文件 ├── scripts/ # 确定性的抽取/diff 脚本 └── recipes/ # AI 驱动的合成/更新 recipe其设计原则可以概括为四点:模块化(每个项目自包含)、可测试(每阶段输入/输出清晰)、透明(中间文件可人工检查)、可复用(跨项目共用同一模式)。而贯穿所有项目的核心手法是混合架构(Hybrid Approach):
- Shell/Python 脚本:负责确定性的抽取与比对——构建二进制、运行
--help、解析输出、JSON 结构比对,全程不做任何解释与推断; - AI Recipe:负责语义合成与文档更新——解释变更影响、生成迁移指引、以正确的格式更新文档。
之所以这样划分,是因为「抽取什么变了」必须是可复现的事实问题,而「这变更对用户意味着什么、文档该怎么改」是需要语言理解的能力问题。两者通过 JSON/Markdown 中间文件解耦,每一步都可以单独重跑、单独检查。
CLI 命令追踪管线的架构
cli-command-tracking/README.md 描述了管线的四阶段流水线,目标是让 CLI Commands Guide 始终与代码同步:
┌─────────────────────────────────────────────────────────────────┐ │ EXTRACTION (确定性) │ ├─────────────────────────────────────────────────────────────────┤ │ extract-cli-structure.sh → extract-cli-structure.py │ │ ↓ │ │ cli-structure.json (commands, options, subcommands, aliases) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ DIFFING (确定性) │ ├─────────────────────────────────────────────────────────────────┤ │ diff-cli-structures.py │ │ ↓ │ │ cli-changes.json (added, removed, modified commands/options) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ SYNTHESIS (AI 驱动) │ ├─────────────────────────────────────────────────────────────────┤ │ synthesize-cli-changes.yaml │ │ ↓ │ │ cli-changes.md (人类可读的变更文档) │ └─────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────┐ │ UPDATE (AI 驱动) │ ├─────────────────────────────────────────────────────────────────┤ │ update-cli-commands.yaml │ │ ↓ │ │ goose-cli-commands.md (已更新) + update-summary.md │ └─────────────────────────────────────────────────────────────────┘各阶段之间全部通过output/目录下的 JSON/Markdown 文件通信,这是整个管线「透明、可测试」的关键:
| 文件 | 生产者 | 消费者 | 用途 |
|---|---|---|---|
old-cli-structure.json | extract-cli-structure.sh | diff-cli-structures.py | 旧版本 CLI 结构 |
new-cli-structure.json | extract-cli-structure.sh | diff-cli-structures.py | 新版本 CLI 结构 |
cli-changes.json | diff-cli-structures.py | synthesize-cli-changes.yaml | 检测到的变更(结构化) |
cli-changes.md | synthesize-cli-changes.yaml | update-cli-commands.yaml | 人类可读的变更文档 |
update-summary.md | update-cli-commands.yaml | 人工审查 | 文档更新摘要 |
版本如何被确定
管线支持自动版本检测,逻辑实现在 run-pipeline.sh 中:
- 旧版本:通过
gh release list取最近第二个 release tag;gh不可用时回退到git tag --sort=-v:refname取第二个匹配vX.Y.Z的 tag; - 新版本:取最近的 release tag,或读取 CI 注入的
RELEASE_TAG环境变量; - 测试未发布变更:显式传
HEAD即可从当前代码构建二进制。
实操:本地运行整条管线
环境变量
| 变量 | 是否必需 | 默认值 | 说明 |
|---|---|---|---|
GOOSE_REPO | 本地运行时必需 | 无(脚本默认$HOME/Development/goose) | goose 仓库根目录路径 |
CLI_COMMANDS_PATH | 否 | $GOOSE_REPO/documentation/docs/guides/goose-cli-commands.md | 目标文档文件完整路径 |
RELEASE_TAG | 否 | 无 | GitHub Actions 指定新版本时使用 |
前置条件(来自 TESTING.md):Python 3.7+、Rust 工具链(构建 goose 时)、jq(JSON 处理)、已安装 goose CLI(运行 recipe)、可访问 goose 仓库的 Git。
一键运行
# 设置 goose 仓库路径 export GOOSE_REPO=/path/to/goose # 自动检测版本,跑完整管线 ./scripts/run-pipeline.sh # 或显式指定新旧版本 ./scripts/run-pipeline.sh v1.17.0 v1.19.0 # 测试未发布的变更 ./scripts/run-pipeline.sh v1.19.0 HEADrun-pipeline.sh 的执行流程是:先对旧版本、新版本分别抽取结构并统计命令数(jq '.commands | length'),再运行 diff 脚本得到cli-changes.json;只有当has_changes为true时才继续执行两个 AI recipe 阶段,否则直接输出「No Changes Detected」并结束。AI 阶段调用goose run --recipe ...时,脚本会用sed/grep过滤掉 ANSI 转义和会话日志行(starting session、session id:、text_editor等),避免噪声混入输出;同时用PIPESTATUS[0]检查 goose 进程本身是否失败,而不是被grep的退出码误导。
分步手动执行
# 1. 抽取 CLI 结构 ./scripts/extract-cli-structure.sh v1.17.0 > output/old-cli-structure.json ./scripts/extract-cli-structure.sh v1.19.0 > output/new-cli-structure.json # 2. 检测变更 python3 scripts/diff-cli-structures.py output/old-cli-structure.json \ output/new-cli-structure.json \ > output/cli-changes.json # 3. 生成人类可读的变更文档 cd output && goose run --recipe ../recipes/synthesize-cli-changes.yaml # 4. 更新 goose-cli-commands.md cd output && goose run --recipe ../recipes/update-cli-commands.yaml跳过命令配置
有些命令被有意排除在抽取与文档追踪之外,配置在 skip-commands.json:
{ "description": "Commands to skip during extraction (not documented intentionally)", "skip_commands": [ { "name": "term", "reason": "Terminal integration documented via @goose/@g aliases" } ] }增删跳过命令只需编辑该配置,无需改代码——抽取脚本启动时通过load_skip_commands()读取该文件并据此跳过对应子命令(见 extract-cli-structure.py)。
确定性抽取阶段:脚本在做什么
获取指定版本的二进制
extract-cli-structure.sh 是抽取阶段的入口,按版本类型走两条路:
- release tag(
vX.Y.Z格式):通过官方download_cli.sh下载对应版本预构建二进制(is_release_tag()用正则^v[0-9]+\.[0-9]+\.[0-9]+$判断),省去编译时间; - HEAD 或其他 git ref:在
GOOSE_REPO中构建。其中非 HEAD 的 ref 会先用git rev-parse校验版本存在,再通过git worktree add把该版本检出到临时目录执行cargo build --release,构建完成后把二进制拷出并移除 worktree,保证不污染主工作区。
拿到二进制后,脚本打印--version输出确认版本,最后调用python3 extract-cli-structure.py <binary> <version>完成真正的解析。
解析--help输出为命令树
extract-cli-structure.py 是抽取的核心:它递归地对命令树中每个节点执行<command> --help,用正则把 clap 风格的帮助文本解析为结构化 JSON。关键解析函数包括:
parse_about():取Usage:行之前的第一行作为命令描述;parse_aliases():匹配[aliases: x, y]模式提取别名(从帮助文本前 500 字符中找);parse_options():定位Options:段,按「行首为-的缩进行」切分选项块;parse_option_block()再对每个块提取短标志(-f)、长标志(--format)、值名(<FORMAT>)、帮助文本(含首行内联帮助)、默认值([default: ...])、可选值([possible values: ...])六个字段;parse_subcommands():解析Commands:段中的子命令名与别名,自动跳过 clap 生成的help子命令;extract_command_structure():递归入口,对每个子命令先检查是否在SKIP_COMMANDS列表中,再深入其子树。
最终输出的 JSON 顶层结构为{version, source_version, extracted_at, binary_path, commands: [...]},每个命令节点包含name / about / aliases / usage / options / subcommands。所有--help调用带 10 秒超时,超时只会告警并返回空串而不中断整个抽取。
确定性 diff:变更如何被分类
diff-cli-structures.py 的算法分为三步:
- 展平:
flatten_commands()把嵌套的命令树按全路径(如session list)展开为字典,便于按路径逐一对比; - 逐字段比对:
compare_commands()对同一路径的旧新命令比较about、aliases、usage,选项层面由compare_options()按「长标志优先、否则短标志」作为键,逐一比对short / long / value_name / help / default / possible_values六个字段,任一字段不同即记入modified; - 破坏性变更归类:
categorize_breaking_changes()将变更打上类型与严重级别标签。
| 变更类型 | 严重级别 | 判定逻辑 |
|---|---|---|
command_removed | high | 命令路径从新版本消失 |
option_removed | high | 选项标志键从选项中消失 |
option_renamed | high | 短/长标志发生变化 |
default_changed | medium | 默认值改变(行为可能隐性变化) |
enum_values_removed | high | [possible values]中出现值被移除 |
alias_removed | medium | 命令别名被移除(可能破坏用户肌肉记忆) |
输出 JSON 包含has_changes布尔值、summary(各计数,其中 breaking 只统计 high 级别)、changes.commands.{added,removed,modified}与breaking_changes数组。summary.breaking_changes的计数口径在 main() 中可以确认:只统计severity == 'high'的条目。
AI 阶段:两个 goose Recipe
管线的后两步用 goose 的 recipe 机制实现,recipe 文件即提示词工程——instructions定义系统级约束,prompt触发执行,均依赖内置的developer扩展(提供text_editor等工具)。
synthesize-cli-changes.yaml:生成变更说明
synthesize-cli-changes.yaml 读取三份输入(cli-changes.json变更 diff + 新旧两份结构 JSON 作上下文),产出cli-changes.md。recipe 指令中规定输出必须包含:摘要统计、Breaking Changes(每条附影响说明与迁移指引,破坏性变更永远排在最前)、New Commands(含用途与关键选项)、Removed Commands(含替代方案)、Modified Commands(描述/选项/别名的逐项对比)、Non-Breaking Changes。
它的分析准则也很具体:从用户影响而非实现细节的角度解释变更;为破坏性变更给出新旧用法对照示例;利用命令名与选项名推断变更意图;跳过纯排版级的帮助文本微调;对空类别整节跳过。
update-cli-commands.yaml:外科手术式更新目标文档
update-cli-commands.yaml 是整条管线中约束最严格的一步。它的核心目标是:文档永远描述 CLI 的当前状态,而不是变更历史——选项被删就从文档删掉,选项被加就补上,绝不写「已移除」「已新增」这类措辞。为此 recipe 列出了一组硬性禁令(ABSOLUTE PROHIBITIONS):
- 只有当
cli-changes.md明确写「命令 X 被移除」时才删除整节命令; - 绝不改分区标题(如
### Task Execution、### Session Management); - 绝不在没有明确记录的情况下重命名选项;
- 绝不重复创建已存在的分区,只原地更新;
- 绝不删除分区之间的水平线
---; - 绝不重写示例,只更新实际变化的那个标志/选项。
更新策略上,它要求按「读cli-changes.md识别全部变更 → 逐条施加最小化编辑(surgical edits,用str_replace精确匹配)→ 保持目标文档既有风格(####命令标题、加粗选项名的 bullet 列表、带语言标识的代码块、admonition 提示框)→ 自查确认」的顺序执行,并在完成后额外生成update-summary.md供人工审查(含「已应用变更清单 + 更新分区 + 验证清单」)。目标文档路径优先取CLI_COMMANDS_PATH环境变量,否则回退为$GOOSE_REPO/documentation/docs/guides/goose-cli-commands.md。
从源码结构看,run-pipeline.sh在调用该 recipe 前会显式export CLI_COMMANDS_PATH="${GOOSE_REPO}/documentation/docs/guides/goose-cli-commands.md",保证 CI 与本地行为一致。
追踪范围与 GitHub Actions 集成
追踪什么
该管线检测的完整范围(来自 cli-command-tracking/README.md):
命令层面:新增/删除命令、描述变更、别名增删、子命令增删。选项层面:新增/删除选项、帮助文本变更、默认值变更、可选值(枚举)变更、短/长标志变更。破坏性变更:按上文严重级别表自动归类。
GitHub Actions 工作流
自动化通过 docs-update-cli-ref.yml 接入 GitHub Actions:
- 触发:新版本发布时自动触发,或手动触发用于测试;
- 流程:为两个版本分别构建 goose、抽取 CLI 结构、检测变更、更新文档;
- 输出:检测到变更时创建一个包含更新后
goose-cli-commands.md的 PR; - 工件:
old-cli-structure.json、new-cli-structure.json、cli-changes.json、cli-changes.md、pipeline.log都会作为 artifacts 上传,可下载检查。
工作流支持三个输入参数:
| 输入 | 说明 | 默认值 |
|---|---|---|
old_version | 旧版本 tag | 从 release 自动检测 |
new_version | 新版本 tag | HEAD |
dry_run | 只生成文件、不创建 PR | true |
在 fork 中测试时,需要在 fork 的 Actions 设置里配置ANTHROPIC_API_KEYsecret,可选配置GOOSE_PROVIDER(默认 anthropic)与GOOSE_MODEL变量;手动触发时建议dry_run: true,跑完后从 workflow run 页面下载 artifacts ZIP 检查中间产物。
用已知变更做回归验证
TESTING.md 给出了三种典型测试用例,可直接复用:
# 用例 1:版本间新增了命令 ./scripts/run-pipeline.sh v1.13.0 v1.14.0 jq '.changes.commands.added' output/cli-changes.json # 用例 2:版本间修改了选项 ./scripts/run-pipeline.sh v1.14.0 v1.15.0 jq '.changes.commands.modified' output/cli-changes.json # 用例 3:同版本对比(应无变更) ./scripts/run-pipeline.sh v1.14.0 v1.14.0 jq '.has_changes' output/cli-changes.json # 期望输出 false抽取阶段的验证则用jq直接抽查结构文件:jq '.commands[] | select(.name == "session")' output/test-extraction.json检查特定命令、jq '.commands[].name' | grep -v term确认跳过命令已排除。
常见问题排查
TESTING.md 沉淀了几个典型故障的排查路径:
- macOS Keychain 提示:goose 启动时可能尝试读取已存凭据而触发钥匙串访问提示;CI runner 没有 keychain,可能需通过
keyring: false之类的配置或环境变量禁用凭据加载(文档中标注为待调查项); - 旧版本构建失败:先
git tag确认版本存在,再手动git worktree add /tmp/goose-test v1.14.0 && cargo build --release定位依赖问题; - 抽取超时:调大 extract-cli-structure.py 中
run_help_command的timeout=10; - diff 出现意外变更:可能是帮助文本排版格式变了,直接对两版二进制的原始
--help输出做diff对比; - AI recipe 失败:先用
ls -lh与jq empty确认三份输入 JSON 存在且格式合法。
扩展指南与维护建议
按照总 README 的约定,新增一个自动化项目只需四步:创建documentation/automation/your-project/子目录、遵循标准结构(README、TESTING、config、scripts、recipes)、按需创建 GitHub Actions 工作流、最后更新 automation 总 README 的项目表格。
维护cli-command-tracking本身时,README 给出的纪律是:先用测试版本本地跑./scripts/run-pipeline.sh;将生成文件与实际 CLI 变更逐一核对;在 fork 中用 dry-run 模式验证工作流;把设计决策回写进 README。此外 TESTING.md 还建议保存「已知正确」的输出作为回归测试数据(如test-data/v1.14.0-to-v1.15.0-changes.json),防止后续脚本改动破坏既有行为。
这套管线值得借鉴的地方在于:它没有把「文档更新」整体交给 AI,而是把可复现的事实提取(构建、--help、解析、diff)留给脚本、把需要语言判断的合成与编辑留给 AI,中间用可检查的 JSON/Markdown 文件交接——每一步失败都可以定位到具体阶段,每一步的产物都可以人工复核,这正是它能在发布流程中无人值守运行的前提。
【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考