仓库全景:两层 workspace 与依赖方向

ZCode 仓库由产品一侧的 packages 与 Agent 一侧的 apps/zcode-cli 两层 workspace 组成:三十多个包各管什么、规模多大、依赖怎样单向流动,两层之间为何只靠一条进程边界相连,以及把边界写成规则的架构治理工具。

作者 David更新于 2 篇(共 47 篇)

打开 ZCode 仓库,第一眼看到的是两套目录:packages/ 下是桌面端、Web、服务端和共享界面,apps/zcode-cli/ 下是 Agent CLI 与运行时。它们在同一个 pnpm workspace 里,却几乎是两个项目——产品一侧的包没有一个依赖 Agent 一侧,两边靠一条进程边界相连。这一篇先把各包的分工和依赖方向理清楚,后面各部分的篇目都以这张图为地图。

两层 workspace

根目录的 pnpm-workspace.yaml 同时收录两层(pnpm-workspace.yaml:1):

packages:
  - packages/*
  - apps/zcode-cli
  - apps/zcode-cli/packages/*
  - apps/zcode-cli/tools/*

apps/zcode-cli 自己也是一个完整的 workspace:有自己的 pnpm-workspace.yamlpnpm-lock.yamlturbo.json,根包名叫 zcode-cli,版本 0.16.9。README 特意说明它“作为普通目录随本仓库一起克隆,无需单独拉取或初始化 Git submodule”(README.md:32),从这些痕迹看,它原先是一个独立仓库,公开时才并进来。两层对运行环境的要求也不一样:根 package.json 要求 node >=24.0.0package.json:78),CLI 则钉死 24.14.0apps/zcode-cli/package.json:46)。

版本号同样分两套:产品版本是根 package.json3.14.0,Agent CLI 是 0.16.9,大多数内部包写的是 0.1.0 或干脆没有版本号,只有 @zcode/rpc1.0.0)、@zcode/zcode-cua0.6.3)、@zcode/node-repl-host0.6.0)、@zcode/browser-use-plugin0.5.1)这几个带了独立版本。

Agent 一侧:apps/zcode-cli

规模口径同封面:非测试的 .ts.tsxwc -l 物理行。

职责文件行数
packages/coreAgent 内核:AgentRuntime、回合循环、工具执行器与全部内置工具、权限、钩子、压缩、记忆、子 Agent、专家工作流49595557
packages/bootstrap组装层:读配置、造 adapter、创建会话运行时;ZCode Protocol 的服务端实现也在这里22263533
packages/adaptersI/O 实现:模型客户端、文件系统、子进程、HTTP、MCP、插件、技能、SQLite 会话库、登录凭据20251637
packages/contracts端口接口、事件与配置的 schema(zod)、工具与工作流契约11121427
packages/dynamic-workflow动态工作流的门面、编译器与纯执行引擎,不做任何 I/O9019878
packages/tui终端界面,基于 OpenTUI(@mbears/opentui-* 发布的构建)的 React 渲染9113723
packages/cli命令行入口、子命令路由、无头模式、SEA 打包脚本8110609
packages/debug本地调试台:抓模型请求的 MITM 代理加时间线界面124779
packages/telemetryOpenTelemetry 指标与链路103874
packages/node-repl-hostnode_repl 的 MCP 宿主,Browser Use 与 Computer Use 的 bridge161519
packages/dynamic-workflow-runtime动态工作流的沙箱 harness:子进程加 vm 上下文51294
packages/i18n中英文界面文案51139
tools/prompt-trajectory录制与回看提示词轨迹的开发工具112401
packages/shared-typesswift-bridgebrowser-use-plugintools/typescript共享类型、Swift 互操作占位、Browser Use 内置插件、共享 tsconfig388

合计 1354 个文件、291458 行。几个小包的实情:swift-bridge 注释写明“Swift bridge placeholder”,两个函数都直接返回“不可用”(apps/zcode-cli/packages/swift-bridge/src/index.ts:1);superpowers-plugin 目录里只有一份 MIT 的 LICENSE,没有代码;browser-use-plugin 的源码只有一个 17 行的文件,内容主要是技能与文档(见 node_repl、Browser Use 与 Computer Use)。

这一侧的分层很清楚,名字就是职责:

图表加载中…

coreadapters 互不依赖,都只认 contracts;把两者接起来的是 bootstrap,它读配置、创建各个 adapter,再注入 AgentRuntimebootstrap:把运行时拼起来)。这是端口与适配器的写法,CLI 的 AGENTS.md 把它写成了硬规矩:业务模块“不得直接调用 fetchhttpfschild_processprocess.env 等底层 I/O API”(apps/zcode-cli/AGENTS.md:53)。实际执行得相当彻底,但并非没有例外:core/src 里仍有 15 个文件直接引用了 node:fsnode:httpnode:net,只是没有一个直接起子进程(例如 tool/handlers/webfetch.tsruntime/methods/bash-shell-snapshot.ts),3 处直接读 process.env(如 tool/handlers/task-output.ts:202)。

产品一侧:packages

职责文件行数
ui桌面端与 Web 共用的 React 界面:会话面板、工具调用卡片、设置页、Zustand 状态、中英文文案1476322555
services业务服务:Agent 子进程管理、会话与任务索引、Git、OAuth、插件与技能同步、用量统计等29887320
desktopElectron 的 Main、Host、Preload、Renderer 与定时调度器,内嵌浏览器26761295
shared共享契约层:ZCode Protocol 的类型与校验、各类领域类型21638370
serverWeb 模式的 HTTP 与 WebSocket 服务、stdio 服务、远程连接5110924
zcode-server-cli独立服务端的启动、守护与进程管理435766
providerProvider 与模型选择的纯逻辑234827
rpc仿 VS Code 的 IPC 框架:channel、代理、序列化、可重连协议204058
webWeb 客户端入口、登录与会话分享页172976
provider-node内置 Provider 配置的下载、缓存与个人配置文件读写171895
formal-proof产品行为的状态空间枚举器(带可视化)31835
model-option-map把统一的模型选项翻译成各家请求参数的小语言8867
clientAgent 客户端 SDK:MessagePort 与 WebSocket 两种连接6723
zcode-cuaComputer Use 的占位包12585

合计 2457 个文件、543996 行。packages/ui 一个包占了全仓近四成,其中 settings/ 就有 5.4 万行、v4/ 会话界面 5.2 万行。zcode-cua 的描述直说这是个“API-compatible placeholder”,开源版不带 Computer Use,所有入口一律返回不可用(packages/zcode-cua/package.jsondescription,以及根 NOTICE.md 第一节)。formal-proof 是一件很少见的工程工具,下一篇会单独介绍。

这一侧的依赖同样单向:

图表加载中…

两层之间:一条进程边界

用各包 package.json 里的 workspace: 依赖算一遍,结论很干脆:packages/ 下没有任何包依赖 apps/zcode-cli 的包;反过来,Agent 一侧只用了产品一侧的五个基础包——sharedproviderprovider-nodemodel-option-mapzcode-cua

那么桌面端和 Web 服务端怎样用上 Agent?答案是进程。services 里的 Agent 进程管理器把构建好的 CLI 当子进程拉起,参数是 app-server --stdiopackages/services/src/zcode-agent/zcodeAgentProcessManager.ts:369),之后双方在标准输入输出上说 ZCode Protocol;协议的类型与运行时校验放在两边都依赖的 packages/shared/src/zcode-protocol-v4/。根 AGENTS.md 把这条边界写成了规则:“Desktop app 通过 stdio 与 Agent 通信。协议改动同步更新 packages/shared/src/zcode-protocol/index.ts,提供严格类型与运行时校验”(AGENTS.md:59)。

图表加载中…

终端里的 zcode 则不经过这条边界:TUI 与运行时在同一个进程里,TUI 直接调用 bootstrap 导出的 createZCodeApp 创建会话(apps/zcode-cli/packages/cli/src/tui-prompt-handler-runtime.ts:50)。同一个运行时因此有两种接法,一条消息的旅程会把两条路都走一遍。

其他目录

目录内容
config/随客户端发布的默认配置 default.json,以及内置 Provider 规则集 provider/zcode-builtin.jsonschemaVersion 1、revision 30,约 18 万字节,见 Provider 规则
third-party/第三方声明材料:复制进仓库的组件清单、嵌入组件清单、原生搜索工具的许可证
apps/zcode-cli/dependencies/native-search/随桌面端与 SEA 分发的 bfs、ugrep、ripgrep 预编译包,共 18 个归档,带 SHA256SUMS
patches/三个 pnpm 补丁:@ai-sdk/anthropic@ai-sdk/openai-compatible 与桌面端的前端监控 SDK @arms/rum-electron
scripts/构建、开发、打包、第三方声明生成、架构检查与原生搜索工具准备
harness/remote/本地起一个 SSH 容器,用来测试远程工作区
.agents/skills/给在这个仓库里干活的编码 Agent 用的 8 个技能,和产品内置的技能是两回事

third-party/copied-components.json 值得翻一下,它记录了哪些代码是从别处复制进来的:packages/rpc 与协议 V4 的编解码文件 packages/shared/src/zcode-protocol-v4/wire-codec.ts 源自 VS Code 的 IPC 与通用工具代码;Bash 工具用来判断命令语义的命令注册表 core/src/tool/handlers/generated/bash-command-registry.ts,由 Fig 的 autocomplete 规格生成;界面组件用了 shadcn/ui 与 Vercel 的 ai-elements;另有从 obra/superpowers 改写的技能描述。这些线索在后面讲 RPCBash技能时会再碰到。

架构治理:把边界写成规则

仓库根目录的 architecture-policy.yaml 把代码划成 15 个模块,每个模块声明根目录、依赖、公开入口,受管模块还要声明分层(architecture-policy.yaml:4)。全局规则只有六条(architecture-policy.yaml:59):

global:
  maxFileLines: 400
  maxContractLines: 300
  maxPublicMethods: 12
  forbidCycles: true
  forbidDeepImports: true
  managedOnly: true

单文件不超过 400 行、契约文件不超过 300 行、契约公开方法不超过 12 个、禁止循环依赖、禁止绕过公开入口的深层导入。关键在最后一条 managedOnly:检查器遇到非受管模块的文件直接跳过(scripts/architecture/index.mjs:132),而 15 个模块里只有 packages/services/src/storage 一个标了 managed: truearchitecture-policy.yaml:28),这个模块只有 13 个文件、1121 行,其余模块的注释写着“存量模块先标记为 legacy”(architecture-policy.yaml:3)。基线文件 .architecture-baseline.json 里的违规列表是空的。

所以“400 行”在今天更像目标而不是现状。CLI 的 AGENTS.md 写的是“单个源文件默认不能超过 400 行”(apps/zcode-cli/AGENTS.md:12),根目录的 oxlint 也配了 max-lines 400(跳过空行与注释,.oxlintrc.json:6),但 oxlint 的 ignorePatterns 把整个 apps/zcode-cli 排除在外(.oxlintrc.json:63),另有 201 个文件用注释关掉了这条规则。按物理行数,全仓超过 400 行的非测试文件有 478 个,最大的 packages/services/src/zcode-agent/zcodeTaskServiceAdapter.ts 有 5737 行,文件头的豁免理由是“迁移期需要在一个门面里集中维护旧 task projection 到 ZCode session 的协议适配”(zcodeTaskServiceAdapter.ts:1)。反过来也看得出团队的方向:CLI 的核心代码确实被拆得很碎,core/src/runtime/methods 一个目录就有 97 个文件。

治理工具不只用来卡提交,也用来喂 Agent:pnpm architecture:context <module-id> 为指定模块生成一份“受控上下文”,列出它的契约与公开入口(scripts/architecture/architecture-check.mjs:15);.agents/skills/architecture-governance/SKILL.md 要求编码 Agent 动手前先跑检查、读这份上下文、为每块可变状态指定唯一的所有者。这个仓库从一开始就是写给人和 Agent 一起读的,下一篇接着说这一点。

下一篇:怎么读这份源码——怎样把它跑起来、仓库里的 AGENTS.md 立了哪些规矩、测试去了哪里,以及一条沿 Agent 主线的阅读路线。

本页目录