跳转到内容

Monorepo 架构与包设计

Gemini CLI — Monorepo 架构与包设计

学习目标

  • 理解 Gemini CLI 的 npm workspaces monorepo 包组织
  • 掌握包间依赖关系和导出控制策略
  • 分析测试工具包的跨包复用模式

项目实践

包组织结构

gemini-cli/
├── packages/
│ ├── cli/ # @google/gemini-cli — CLI 入口和 TUI
│ ├── core/ # @google/gemini-cli-core — 核心 Agent 逻辑
│ ├── sdk/ # @google/gemini-cli-sdk — 编程 API
│ ├── test-utils/ # 测试基础设施
│ ├── a2a-server/ # A2A 协议服务器
│ └── vscode-ide-companion/ # VS Code IDE 插件
├── evals/ # 评估测试
├── integration-tests/ # 集成测试夹具
├── perf-tests/ # 性能测试
├── docs/ # 文档
└── scripts/ # 构建和发布脚本

各包职责

NPM 名称职责使用者
cli@google/gemini-cliCLI 入口、React Ink TUI、命令解析终端用户
core@google/gemini-cli-coreAgent 循环、工具、调度、配置cli、sdk、a2a-server
sdk@google/gemini-cli-sdkGeminiCliAgent / GeminiCliSession 封装开发者
test-utils不发布测试夹具、Mock 工具其他包的测试
a2a-server不发布A2A 协议实现,将 CLI 暴露为远程 Agent远程调用者
vscode-ide-companion不发布VS Code 扩展,IDE 集成VS Code 用户

包间依赖关系

关键设计core 是所有包的依赖中心,clisdk 不互相依赖,避免了循环依赖。

导出控制

每个包通过 index.ts 精确控制公开 API:

packages/core/index.ts
export { GeminiClient } from './src/core/client.js';
export { Config } from './src/config/config.js';
export { Scheduler } from './src/scheduler/scheduler.js';
// ... 仅公开需要外部使用的类

内部模块不通过 index.ts 导出,外部包无法访问。这使得 core 内部可以自由重构,只要公开 API 不变。

构建链

Vite + esbuild

  • esbuild.config.js:使用 esbuild 进行快速捆绑
  • 各包有独立的 tsconfig.json
  • vitest.config.ts:各包独立的测试配置

为什么选 esbuild

  • 极快的构建速度(Go 编写,非 TypeScript)
  • 对 TypeScript 和 ESM 的良好支持
  • 适合 CLI 工具的构建需求(不需要 tree-shaking 优化到极致)

测试基础设施共享

test-utils 包提供跨包复用的测试工具:

// test-utils 核心导出
export { TestRig } from './src/test-rig.js'; // 集成测试夹具
export { MemoryTestHarness } from './src/memory-test-harness.js'; // 内存基准测试
export { PerfTestHarness } from './src/perf-test-harness.js'; // 性能基准测试
export { createMockUtils } from './src/mock-utils.js'; // Mock 工具集
export { setupTestEnv } from './src/env-setup.js'; // 环境初始化

TestRig 示例

const rig = await TestRig.create({
config: { model: 'gemini-2.5-flash' },
tools: [readFileTool, shellTool],
});
await rig.initialize();
// 使用 rig.context 创建完整的 AgentLoopContext
// 进行集成测试

发布策略

哪些包发布到 npm

  • @google/gemini-cli(CLI)
  • @google/gemini-cli-core(core)
  • @google/gemini-cli-sdk(SDK)

哪些包不发布

  • test-utils:内部测试工具
  • a2a-server:作为 CLI 的一部分分发
  • vscode-ide-companion:通过 VS Code Marketplace 分发

发布流程

  • 每周发布稳定版(周二 UTC 20:00)
  • 每周发布预览版(周二 UTC 23:59)
  • 每夜发布 nightly(UTC 00:00)

陷阱与对策

包间版本同步

问题cli 依赖特定版本的 core,版本不同步可能导致运行时错误。

对策

  • monorepo 内使用 workspace:* 协议引用内部包
  • 发布时统一版本号(通过 scripts 自动化)
  • CI 中使用集成测试验证包间兼容性

循环依赖

问题:如果 core 依赖 cli,而 cli 依赖 core,形成循环。

对策

  • core 严格不依赖 clisdk
  • core 只定义接口,具体 UI/CLI 实现在 cli
  • 通过接口注入(如 EditorTypecli 注入到 core)实现反向依赖

设计取舍

Monorepo vs 多仓库

方案优势代价
Monorepo统一版本、原子提交、跨包测试仓库大、CI 构建慢、权限管理困难
多仓库独立发布、权限隔离版本同步困难、集成测试复杂

Gemini CLI 的选择:Monorepo。core/cli/sdk 需要紧密协作,统一版本简化用户理解和测试。

npm workspaces vs pnpm/turborepo

方案优势代价
npm workspacesNode.js 原生支持、无需.无需额外安装缺少高级缓存和并行构建优化
pnpm磁盘空间高效、严格依赖隔离需要额外安装、兼容性偶尔问题
turborepo远程缓存、增量构建需要配置、增加构建基础设施依赖

Gemini CLI 的选择:npm workspaces。项目使用标准 npm 生态,npm workspaces 足够满足需求,减少构建链复杂度。


参考来源