Files
deepseek-harness/docs/development.zh.md
2026-07-23 13:09:52 +08:00

12 KiB
Raw Blame History

开发指南

English | 中文

本指南覆盖参与 DeepSeek Harness 开发所需的本地环境搭建、日常工作流与 CI 流程;设计动机与技术权衡请查阅相应 Agent Note。

前置条件

  • Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26Node 引擎下限 Agent Note
  • 启用了 Corepack 的 pnpm。仓库在 package.json 中固定使用 pnpm@11.7.0;如果 pnpm --version 无法通过 Corepack 解析,请先运行 corepack enable
  • Git。
  • 可选:一个 DeepSeek API key用于 TUI/Headless/ACPAgent Client Protocol agent智能体演示和真实 API 的 e2e 测试。

首次搭建

在仓库根目录安装依赖:

pnpm install

安装过程同时会运行根目录的 postinstall 脚本,该脚本通过 scripts/install-lefthook.mjs 从仓库 dev 依赖安装 lefthook。包装脚本使用 lefthook 经过评审的 --force 模式,确保已存在 core.hooksPath 的关联 worktree 不会导致正常的 pnpm run … 命令失败。

如果依赖是从缓存恢复或 postinstall 被跳过而导致缺少钩子,请手动安装:

pnpm exec lefthook install --force

新克隆后请先运行一次类型检查:

pnpm run typecheck

首次类型检查会执行全仓 tsc -b tsconfig.json 图:发射每个 package/vendor 的 lib/types,并通过下述两个 no-emit 聚合检查示例、测试和脚本。

TypeScript 项目布局

仓库的 TypeScript 配置只有三种角色;每个 tsconfig 文件恰好扮演其中一种。

文件 角色 是否构成 program
tsconfig.json solution 根:extends base、files: []、引用两个聚合。全仓 tsc -b tsconfig.json 图、tsserver 发现入口,并经继承的 paths 充当 tsx 运行 examples/scripts/ 时的解析配置(它们最近的 tsconfig 就是此文件)。
tsconfig.host.json host 聚合host 侧各包(经 references、示例、测试、脚本、website。排除 packages/client
tsconfig.client.json client 聚合:packages/client/* 各包及其测试、apps/web
tsconfig.base.json 共享 compilerOptions 与源码 paths 映射。同时是各 vitest 配置让 vite-tsconfig-paths 指向的解析门面:它没有 include,因此其 paths 适用于任何 importer。
tsconfig.base.client.json 浏览器编译形状(jsx、DOM lib、types: []),由 client 聚合和每个 packages/client/* 包 extends。

host 与 client 保持两个聚合 program是因为两侧在相同键下以不同服务对 cordis Context 接口做声明合并;单一 program 同时看到两份合并会报冲突。这种冲突只存在于 ts.Program 内部——模块解析永远不会触发它——所以 solution 可以同时引用两个聚合,一个 paths 门面也可以横跨两侧。由此推出两条纪律:

  • tsconfig.base.json 永不添加 includefiles:它们会泄漏进每个 extends 它的包项目,并收窄门面的全匹配范围。
  • 构造全仓 ts.Program 的脚本显式种子 tsconfig.host.jsontsconfig.client.json——永不种子根 solution因为把两个聚合展平进一个 program 会撞上 Context 合并冲突。基于 program 的生成器与门禁(scripts/ts-project.ts 的消费者、doc-typecheck standalone 模式)按决策仅覆盖 host 侧client 侧只在出现真实需求时再获得基于 program 的工具。

静态分析和测试通过 base 的 paths 映射把工作区 import 解析到 src,且必须在干净树上通过;消费构建产物 lib/ 的门禁显式声明该依赖。决策记录:solution-root notetsc-first 发射管线见 ts-build-config note

如果相关的本地检查需要使用构建后的包产物,请先构建一次:

pnpm run build

pnpm run hygiene 包含 publint(用构建出的 lib/*.js 文件校验 package 入口点)和 verify-node-next-types(用一个临时的 NodeNext 消费方校验构建出的声明文件)。新 worktree 在 pnpm run build 运行之前没有打包的 JS 和声明文件;普通提交和推送无需构建,除非所选检查会使用这些产物。

环境变量

真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 .env 文件读取凭证:

DEEPSEEK_API_KEY=sk-...
DEEPSEEK_BASE_URL=https://... # optional

DEEPSEEK_BASE_URL 可选,默认为公开 API。请勿提交真实凭证。未设置 DEEPSEEK_API_KEY 时,真实 API 的 e2e 套件会自动跳过。

Git 钩子

lefthook 在 lefthook.yml 中配置了快速的提交级检查和全面的发布检查:

  • pre-commit 运行对暂存文件的 ESLint 修复,检查暂存 diff 中的空白错误,并运行 vendor manifest元数据清单守卫
  • pre-push 调用 pnpm run check:pre-push;该命令从 scripts/run-gates.ts 中选择与 pnpm run check:ci 相同的主 Node 门禁清单。

vendor manifest 守卫检查 vendor/*/src 下的改动是否连同对应的 vendor/README.md manifest 更新一起暂存。请在编辑 vendor 代码前先阅读 vendor/README.md

贡献者在迭代过程中运行与改动行为相关的检查。正常推送会在本地完整运行一次主 CI 聚合,并在任何检查失败时阻止发布;不要在推送前立即重复运行同一个聚合。

pre-push 的结果覆盖当前主机上的 keyless 主 Node lane但不能代替受支持版本与平台矩阵、Python SDK 测试、真实 API e2e 或沙箱工作流。pnpm run check:all 仍是一份广泛的可选开发检查清单;它独立于两个 Git 钩子,也不属于发布契约。

CI 门禁

keyless CI 工作流 将独立门禁分组到若干宽粒度 lane并在受支持的 Node 版本上运行一组较小的兼容性检查。产物消费方在各自 lane 内等待一次 build。check:pre-push 与主 CI job 共用一份门禁清单;单独的真实 API 工作流按其配置的 worker 上限运行 pnpm run test:e2e。当前门禁和 job 清单以 scripts/run-gates.ts 和工作流文件为准。

日常命令

在仓库根目录使用:

pnpm run test           # unit tests
pnpm run test:coverage  # unit tests with per-file coverage gates
pnpm run test:e2e       # real-API tests; self-skips without DEEPSEEK_API_KEY
pnpm run check:pre-push # primary Node CI inventory; runs automatically before push
pnpm run check:all      # broad opt-in development gate set; not wired to Git hooks
pnpm run typecheck      # tsc -b over the root solution: emits package/vendor lib/types, checks both aggregates
pnpm run lint           # eslint .
pnpm run lint:fix       # eslint . --fix
pnpm run doc-typecheck  # compile checked TypeScript snippets in Markdown docs
pnpm run gen-cordis-catalog     # regenerate docs/cordis-catalog/events.md + services.md from source
pnpm run verify-cordis-catalog  # fail if either cordis catalog is stale
pnpm run verify-export-jsdoc    # fail if a module-level package export lacks complete JSDoc
pnpm run gen-doc-graphs     # regenerate generated relationship docs from source and curated graph definitions
pnpm run verify-doc-graphs  # fail if generated relationship docs are stale
pnpm run verify-md-wrap  # fail on hard-wrapped prose paragraphs in docs/README markdown
pnpm run verify-mermaid  # fail if a ```mermaid diagram has invalid Mermaid syntax
pnpm run verify-type-equiv  # fail if a ```ts type-equiv doc block drifts from its source type
pnpm run verify-doc-budgets  # fail if a budgeted standing doc exceeds its word ceiling
pnpm run doc-sync       # all Markdown/doc gates, scheduled concurrently; the doc-sync leaf list in scripts/run-gates.ts is the full list
pnpm run gen-module-graph     # regenerate docs/module-graph.md from package peerDeps
pnpm run verify-module-graph  # fail if docs/module-graph.md is stale
pnpm run build          # emit lib/types intermediates, then bundle lib/index.* runtime files
pnpm run verify-node-next-types  # fail if built declarations are not NodeNext-consumable
pnpm run hygiene        # knip, publint, workspace constraints, and NodeNext declaration check

修改 package 的公开行为时,请在同一个变更中更新相关 README 或 JSDoc。pnpm run doc-sync 能检测到被检查的 TypeScript 片段、生成文档的新鲜度、Markdown 换行/链接漂移、type-equiv、翻译配对、Mermaid 语法和文档预算,但更广泛的行文/API 同步仍需评审把关。

演示

单次运行的 Headless coding agent 需要环境变量或仓库根目录 .env 中的 DEEPSEEK_API_KEY

pnpm run demo:headless "summarize this workspace"

全屏交互式 coding agent 需要环境变量或仓库根目录 .env 中的 DEEPSEEK_API_KEY

pnpm run demo:tui

自指的 cordis-agent 演示可以检查并修改其实时插件运行时,并需要相同的凭证:

pnpm run demo:cordis

ACP 服务器 agent 演示通过 JSON-RPC stdio 暴露 agent同样需要 DEEPSEEK_API_KEY

pnpm run demo:acp

TODO 标记

请使用以下三种注释标签之一标记代码中的已知问题,按紧急程度排序:

  • FIXME:应当阻塞新版本发布的问题。除非评审者明确同意该更改可以合并,否则发布版本不应包含未解决的 FIXME
  • TODO:应当尽快修复的问题,等资源到位即可处理;
  • XXX:也许某天会修复的问题,优先级最低,不作承诺。

请选择与紧急程度匹配的标签,让浏览代码的人一眼分清「发布阻塞」和「有空再说」。

逐字记录类型(ts type-equiv

核心数据结构文档会把与源码等价的声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ```ts type-equiv(而不是 ```ts),并在 scripts/type-equiv.manifest.json 中登记它镜像的源文件和符号:

{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" }

pnpm run verify-type-equivdoc-sync 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc并断言代码块同时匹配两者。对于不应把实现体写进目录的类请使用 ```ts public-api 并设置 "projection": "public-api";门禁检查的投影会保留公共字段、构造函数、访问器、方法以及类和成员的原始 JSDoc同时省略实现体和私有或受保护成员。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc包括成员文档让读者同时看到源码契约和确切形状。该门禁还按文档、符号和投影强制 1:1 对应,因此不会有块被静默漏检,也不会有陈旧条目滞留。doc-typecheck 跳过两种围栏(它们不能独立编译),并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个块时,请在同一个变更里更新 manifest。

架构上下文

在修改 packages/ 目录下的任何内容之前,请先阅读 docs/architecture.md。这套代码围绕 Cordis 插件、事件溯源的会话、类型化的服务 seam 与显式扩展点构建。