Context Hub 架构全解:最小依赖 CLI、双模输出与 E2E 测试设计
【免费下载链接】context-hub项目地址: https://gitcode.com/gh_mirrors/co/context-hub
Context Hub(chub)是一个面向 AI 编程智能体的文档 CLI 工具:它让 Agent 能搜索并拉取经过人工整理、带版本号的 API 文档与技能文件,而不是靠训练数据"猜" API。本文带你快速看懂它的三大架构亮点:最小依赖的 CLI 设计、双模输出机制、以及可复现的 E2E 测试体系。
一、整体架构:一个 CLI,两种入口
Context Hub 的仓库结构非常清晰,核心代码全部位于cli/目录:
- 命令层cli/src/commands/:
search、get、build、annotate、feedback、cache、update各自独立注册 - 核心库层cli/src/lib/:注册表合并、BM25 检索、缓存、配置、输出等无副作用逻辑
- MCP 服务层cli/src/mcp/:把同样的能力包装成 MCP 工具
- 内容区content/:所有文档均为纯 Markdown(YAML frontmatter + DOC.md),按"项目/主题/语言"组织
package.json中声明了两个可执行入口(chub与chub-mcp),也就是说同一个代码库同时提供命令行和 MCP Server 两种接入方式,Agent 既可以直接执行命令,也可以通过 MCP 协议调用工具,能力完全复用。
二、最小依赖设计:7 个运行时依赖撑起整个 CLI
在 cli/package.json 中,运行时依赖只有 7 个:
| 依赖 | 用途 |
|---|---|
commander | 命令行参数解析 |
chalk | 终端彩色输出 |
zod | MCP 工具参数校验 |
@modelcontextprotocol/sdk | MCP Server 协议实现 |
posthog-node | 匿名遥测(可用CHUB_TELEMETRY=0完全关闭) |
tar | 解压完整文档包 |
yaml | 解析~/.chub/config.yaml配置 |
这种"极简依赖"带来三个好处:
- 安装快、攻击面小——npm 包体积可控,供应链风险低
- 充分利用 Node 18+ 内置能力——cli/src/lib/cache.js 直接使用原生
fetch+AbortController做带超时的网络请求,没有引入任何 HTTP 库 - 启动即主路径清晰——cli/src/index.js 中用
preAction钩子统一处理"欢迎语 → 遥测 → 注册表就绪检查",命令失败时会给出可操作的修复提示(如chub update)
💡 对新手来说,这是学习"如何用最少第三方库写一个生产级 CLI"的很好范例。
三、双模输出:同一条命令,人读友好 + 机器可解析
Context Hub 的双模输出设计堪称优雅,全部逻辑集中在 cli/src/lib/output.js:
output(data, humanFormatter, opts) ├── opts.json 为真 → stdout 只输出格式化 JSON(供脚本/Agent 解析) └── 默认 → 调用 humanFormatter,用 chalk 渲染人类友好的彩色文本以chub get命令为例(cli/src/commands/get.js):
- 人类模式:直接打印文档 Markdown 正文,末尾附上"可用附加文件"和反馈提示
- JSON 模式:输出
{ id, type, content, path, additionalFiles }结构化数据,方便管道处理或程序断言
关键细节:
- JSON 模式下 stdout 保证纯净——确认类信息(如"已写入文件")一律走 stderr,避免污染机器可读输出
- 错误也分双模——
error()在 JSON 模式输出{"error": "..."},普通模式输出Error: ...到 stderr 并以退出码 1 结束 - 这种"单一数据源、两种渲染"的设计,让每条命令只需要写一次格式化逻辑
四、检索与缓存:BM25 打分 + 本地优先的多级回退
chub search的搜索能力由 cli/src/lib/bm25.js 实现:
- 索引在
chub build时预构建(倒排索引 + IDF),搜索时只打分,速度快 - 四个字段加权:
id权重 4.0 >name3.0 >tags2.0 >description1.0,让"搜包名"永远优先于"搜描述" - 无索引时自动回退到关键字匹配;cli/src/lib/registry.js 还会叠加前缀/包含/编辑距离的模糊救场打分
缓存侧(cli/src/lib/cache.js)采用本地优先的多级回退:本地源码 → npm 包内置dist内容 → 远程 CDN 拉取后写回缓存,并配合meta.json时间戳实现refresh_interval自动过期刷新。断网时依然可用,这对 Agent 的稳定运行至关重要。
五、E2E 测试设计:真实进程 + 隔离环境 + 内置 Fixture
cli/test/e2e.test.js 是理解项目测试理念的关键文件,它的设计有三个亮点:
1. 测试真实二进制,而非内部函数
测试通过execFileSync('node', [CLI, ...args])启动真实的 CLI 入口bin/chub,覆盖参数解析、双模输出、退出码、错误文案等完整链路——这正是"测试用户实际会用的东西"。
2. 完全隔离,绝不污染用户环境
- 用
mkdtempSync创建临时目录并注入CHUB_DIR环境变量,~/.chub全程无感 - 设置
CHUB_TELEMETRY=0与CHUB_FEEDBACK=0,确保测试不产生任何网络副作用 - 测试数据完全来自内置 Fixture(cli/test/fixtures/):
acme/widgets多文件文档、multilang/client多语言文档、acme/versioned-api多版本文档、testskills/deploy技能,一套数据覆盖全部核心场景
3. 先 build 再断言,验证完整流水线
beforeAll中先执行chub build <fixtures>生成registry.json,随后断言注册表计数(3 docs + 1 skill)、文件拷贝、模糊搜索、--lang自动选择、--version回退、--file增量拉取、注释注入与清除等 40+ 条用例。
🎯 一句话总结:测试不 mock 网络、不 mock 文件系统,只用环境变量做隔离——简单、可信、可复现。
六、安全细节:值得抄作业的两处防御
- MCP 传输保护:cli/src/mcp/server.js 开头把所有
console.log重定向到 stderr——因为任何依赖库的意外打印都会破坏 stdio 上的 JSON-RPC 协议 - 注释默认不注入:
chub get只有在显式传--with-annotations时才会附带本地注释,且输出中明确标注"untrusted input",防止提示注入风险
写在最后
Context Hub 用7 个运行时依赖、一套双模输出、一个全隔离 E2E 套件,把"给 AI 喂文档"这件小事做到了架构级严谨。如果你想动手研究,建议按这个顺序阅读源码:cli/src/index.js 看命令装配 → cli/src/lib/output.js 看输出双模 → cli/src/lib/bm25.js 看检索算法 → cli/test/e2e.test.js 看测试设计。配套的 Agent 技能文件 cli/skills/get-api-docs/SKILL.md 也值得参考,它是"让 Agent 学会自动查文档"的提示词范本。
【免费下载链接】context-hub项目地址: https://gitcode.com/gh_mirrors/co/context-hub
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考