首页
/ Reactive Resume 的 AGENTS.md 详解:为 pnpm + Turborepo 简历构建器编写可执行的 AI Agent 协作手册

Reactive Resume 的 AGENTS.md 详解:为 pnpm + Turborepo 简历构建器编写可执行的 AI Agent 协作手册

2026-09-05 11:11:27作者:凤尚柏Louis

本篇以 Reactive Resume 仓库根目录的 AGENTS.md 为主体,拆解这份"面向 AI Agent 的工程手册"的完整结构:从 monorepo 架构总览、各包职责地图、包边界规则与决策树,到数据库迁移、环境变量、常用命令与陷阱清单。读完你可以掌握在大型 pnpm + Turborepo 项目中如何为 Agent(及人类贡献者)组织一份"可直接执行、可验证"的工程说明文档,并能按其中的命令表在本仓库完成开发环境搭建。

一、AGENTS.md 的定位与整体结构

AGENTS.md 是放在仓库根目录的 Agent 协作约定文件(配套的 CLAUDE.md 与其内容一致,供不同 Agent 客户端读取)。它不是产品文档,而是一份"施工规范":告诉任何接手代码的 Agent 项目长什么样、规则是什么、命令怎么跑、坑在哪里。

从文档结构看,它由几个用 HTML 注释标记的"可插拔区块"加上主体内容组成:

  1. <!-- intent-skills --> 区块(Skill Loading):要求在开始实质性修改前,先运行 pnpm dlx @tanstack/intent@latest list 查看本地可用技能,若技能与任务匹配则先 pnpm dlx @tanstack/intent@latest load <package>#<skill> 加载对应 SKILL.md 再动手;跨包任务优先加载被修改包对应的本地技能。
  2. <!-- caveman --> 区块:一组可选的"精简回复风格"规则(保留全部技术实质、去掉冗余修饰),并明确边界——代码、commit、PR 一律正常书写;安全警告、不可逆操作时自动恢复清晰模式。
  3. <!-- graphify --> 区块:约定代码库问答优先使用知识图谱工具。当 graphify-out/graph.json 存在时,先用 graphify query "<question>" 获取作用域子图;用 graphify path "<A>" "<B>" 查关联、graphify explain "<concept>" 查概念;仅在宽泛架构审查时才读 GRAPH_REPORT.md;改完代码后运行 graphify update . 保持图谱最新(纯 AST,无 API 成本)。
  4. 主体# AGENTS.md 标题之后的工程约定,包括 Agent skills、Cursor Cloud 专属说明(架构总览、前置条件、代码库地图、Web 应用约定、包边界、数据库、环境、常用命令、陷阱)。

这种"注释区块 + 主体"的分层写法,让不同 Agent 工具可以只识别自己关心的部分,主体规则对所有工具生效。

二、架构总览:两个可部署应用 + 一批 source-consumed 内部包

文档"Overview"一节给出的架构判断,可与仓库实际文件相互印证:

  • Reactive Resume 是一个 pnpm monorepo(Turborepo),工作区定义在根 package.jsonworkspaces 字段:apps/*packages/*tooling(见 pnpm-workspace.yaml)。
  • 两个可部署应用:
    • apps/web:TanStack Start / React 19 / Vite 前端,负责路由、简历编辑器 UI、PWA 与 oRPC 浏览器客户端;
    • apps/server:Hono / Node.js 生产服务,负责挂载 API / auth / MCP / OpenAPI / 静态上传路由,并直接托管构建好的 web 产物。
  • 生产 Docker 镜像运行单 Node.js 进程、监听 3000 端口,由 apps/server 同时承载 API 与静态站点。这一点可从 Dockerfile 印证:ARG NODE_VERSION=24、runtime 阶段基于 node:24-slim,启动脚本为 node apps/server/dist/index.mjs(见根 package.jsonstart 脚本)。
  • 一个关键约定:内部包通过 package.json 的 export map 直接指向 src 源码进行消费,不要假设存在包本地 dist 产物(除非该包显式添加)。这解释了为何仓库中大量测试直接引用源码路径。

三、前置条件:Node 24、Docker 与 pnpm

文档 Prerequisites 一节要求:

  • Node.js 24——与 DockerfileARG NODE_VERSION=24 对齐;可用 nvm install 24 && nvm use 24 切换。
  • Docker——用于运行 PostgreSQL(开发基础设施整体由 compose.dev.yml 编排,见下文"数据库"一节)。
  • pnpm——文档写的是 11.21.0;需要注意的是,当前根 package.jsonpackageManager 字段已声明 pnpm@11.24.0,实际执行时以 packageManager 声明(corepack/直装 pnpm 会遵循它)为准。

四、Codebase map:一份可以据此定位任意改动的"职责地图"

这是 AGENTS.md 信息密度最高的部分,逐包说明了所有权。以下按文档原意整理,并标注仓库中可对应的实体:

位置 职责
apps/web TanStack Start 路由、Vite 配置、PWA、oRPC 浏览器客户端接线、web 功能与简历编辑器 UI
apps/server 生产 Hono 应用、路由组合、auth/RPC/MCP/OpenAPI 处理、静态上传、schema JSON、web-dist 回退托管、启动检查
packages/api oRPC routers、DTO、限流;按功能组织于 packages/api/src/features/*@reactive-resume/api/routers 导出聚合这些功能路由供 /api/rpc 使用
packages/auth Better Auth 配置、认证辅助函数、导出类型;服务端适配层 apps/server/src/http/auth.ts 委托给 auth.handler
packages/db Drizzle 客户端与 schema(见 packages/db/src/client.ts);迁移文件位于仓库根 migrations/ 目录(当前含 26 个时间戳迁移目录)
packages/env 服务端环境变量校验,并为 app/server 代码自动加载根 .env(实现见 packages/env/src/server.ts:通过 process.loadEnvFile 加载根 .env,再经 @t3-oss/env-corecreateEnv 做 Zod 校验)
packages/schema Zod schema 与带类型的 resume/page/template 模型
packages/pdf React PDF 文档、字体注册、共享模板原语、各模板实现、浏览器/服务端 PDF 生成适配器;PDF.js 查看器 UI 留在 apps/web
packages/resume 纯简历领域行为:JSON Patch 辅助、社交网络图标映射
packages/docx DOCX 导出生成
packages/mcp MCP 工具、prompts、resources、server-card 生成与工具元数据
packages/ui 共享的 Base UI / shadcn 风格组件与 hooks
packages/fontspackages/emailpackages/importpackages/aipackages/utilspackages/config 聚焦的支撑面;优先复用其现有导出,不要新增跨包捷径
tooling/ 仅开发期脚本。放在 packages/ 之外,保证包内只有会被应用/运行时打包的代码

这张地图的实践价值在于:任何"这个改动应该放哪里"的问题,都能先查表再动手,避免在错误的包里堆代码。

五、Web 应用约定:SSR 边界与路由规则

文档对 apps/web 的约定逐条对应真实文件:

  • 基于文件的路由位于 apps/web/src/routesapps/web/src/routeTree.gen.ts 由 TanStack Router 工具生成,禁止手工编辑
  • 服务端 HTTP 行为集中在 apps/server/src/{http,rpc,mcp,openapi,static,startup};API/RPC/auth/MCP/静态路由的接线不得下沉到 web 路由里。
  • apps/web/src/router.tsx 初始化路由上下文,携带 queryClientorpcthemelocalesessionflags;应复用路由上下文,而不是在各处临时重复拉取这些数据。
  • SSR 边界:builder 壳位于 apps/web/src/routes/builder/$resumeId,其嵌套预览路由是纯客户端的(ssr: false);公开简历路由 apps/web/src/routes/$username/$slug.tsx 使用 ssr: "data-only"。浏览器专属的简历预览代码放在 apps/web/src/features/resume/preview,公开 PDF 查看器放在 apps/web/src/features/resume/publicPDF.js / canvas / 浏览器 API 不得进入 SSR 路径,也不得进入 packages/pdf
  • 同构 oRPC 客户端位于 apps/web/src/libs/orpc/:服务端调用走进程内 router 客户端,浏览器调用走携带 credentials 的 /api/rpc
  • 组件 props 风格:显式 props 的 React 组件优先使用具名 TypeScript props 类型,而非在函数签名里内联对象标注(props 超过一个字段或涉及泛型时尤其如此)。文档给出的示例:
type IntentSelectFieldProps<TValue extends string> = {
	label: string;
	id: string;
	value: TValue | undefined;
	options: readonly ComboboxOption<TValue>[];
	onChange: (value: TValue | undefined) => void;
};

function IntentSelectField<TValue extends string>(props: IntentSelectFieldProps<TValue>) {
	// ...
}

六、包与功能边界:turbo boundaries、导出子路径与决策树

这部分是 AGENTS.md 中最"可执行"的规则集,且能与根 turbo.json 的配置一一对上:

  1. 工作区依赖必须走包名 + export map。禁止通过仓库路径、@reactive-resume/*/src/* 或 TypeScript path alias 直接 import 其他工作区的 src 树。

  2. turbo boundaries 是可执行的边界检查。各工作区的 turbo.json 声明粗粒度 tag,根 turbo.jsonboundaries.tags(约 L14-L46)定义了依赖拒绝规则,例如:

    • app:serverruntime:server:deny app:webruntime:browser
    • runtime:browser:deny app:serverruntime:server
    • runtime:universal:deny app:webapp:serverruntime:server
    • role:domain:deny 两个 app、role:adapterrole:infrarole:ui 同理。

    也就是说,"universal 包不得依赖任何运行时专属包"这类约束不是口头约定,而是 pnpm exec turbo boundaries 会拦截的硬性规则。

  3. 运行时专属代码放在显式导出子路径后,如 @reactive-resume/pdf/browser@reactive-resume/pdf/server@reactive-resume/env/server;根导出保持环境中立(除非包本身就是 server-only)。

  4. 通配导出仅限"文件型"叶子库:当前只有 @reactive-resume/ui/components/*@reactive-resume/ui/hooks/* 以及 schema 的 resume 模型文件;拥有运行时行为的包优先显式导出。

  5. 新功能归属规则(文档原文要点):

    • 新 API procedure 与业务逻辑放进所属 packages/api/src/features/* 模块,按功能聚合路由接线、DTO、辅助函数与服务,只通过 packages/api/package.json 暴露有意为之的公开面;认证 procedure 优先用 packages/api/src/context.tsprotectedProcedure
    • 新增数据库列/表先改 packages/db/src/schema/*,再用 dotenvx run -f .env.local -- pnpm db:generate 生成根级迁移。
    • 简历数据形状变更先在 packages/schema/src/resume/*,再更新 API DTO、importer、PDF 渲染与 web 表单。
    • 新增/重命名模板要同步四处:packages/schema/src/templates.tspackages/pdf/src/templates/index.tspackages/pdf/src/templates/<name>/ 模板源码、apps/web/public/templates/{jpg,pdf} 静态预览。
    • 简历 JSON Patch 行为归 @reactive-resume/resume/patch,DOCX 导出归 @reactive-resume/docx——都不要放进 @reactive-resume/utils
    • 共享 PDF 分区过滤在 packages/pdf/src/templates/shared/filtering.ts;模板特例留在模板目录内。
    • packages/pdf/src/hooks/use-register-fonts.ts 独占 React PDF 字体注册、标准字体处理、CJK 回退栈与全局连字行为。
    • MCP 实现归 @reactive-resume/mcp,app 包不得从另一个 app 的源码树 import MCP 实现。
    • packages/utils 只暴露窄导出;需要工具时添加显式导出路径,而非 import 私有文件。
  6. 放置决策树(Placement decision tree),文档给出的 9 步判断顺序:

    1. web 路由 / route loader / 面向用户的 web 工作流 → apps/web/src/routesapps/web/src/features
    2. 服务端 HTTP 路由/适配、启动检查、静态处理、MCP 传输、OpenAPI/well-known → apps/server/src
    3. 需要认证的 API 行为 → 所属 packages/api/src/features/* 模块;
    4. 不依赖 DB/HTTP/DOM/PDF 渲染器的纯简历数据行为 → packages/resume
    5. 渲染简历 PDF → 共享 React PDF/模板代码进 packages/pdf,PDF.js 查看器/canvas UI 进 apps/web/src/features/resume
    6. DOCX 导出 → packages/docx
    7. MCP 工具/prompts/resources → packages/mcp
    8. 通用 UI 原语/hook → packages/ui,工作流专属 UI 留在所属 web feature;
    9. 窄的横切辅助函数 → 确认没有更合适的领域包之后,才加显式 packages/utils 导出。

七、数据库:compose 起 Postgres,drizzle-kit 不读 .env

文档 Database 一节的关键点:

  • PostgreSQL 通过 Docker Compose 启动:
sudo docker compose -f compose.dev.yml up -d postgres
  • 开发默认连接串:postgresql://postgres:postgres@localhost:5432/postgrescompose.dev.ymlpostgres 服务(postgres:latestPOSTGRES_USER/PASSWORD=postgres,仅绑定 127.0.0.1:5432)与之对应;同文件还编排了 redisredis://redis:6379,appendonly 模式)与 seaweedfs(S3 兼容存储,S3_ENDPOINT=http://seaweedfs:8333)及一次性建桶任务 seaweedfs_create_bucket
  • 重要陷阱drizzle-kitpnpm db:migrate 的底层)直接从 process.envDATABASE_URL不会自动加载 .env 文件。因此迁移命令要通过 dotenvx 执行,例如 dotenvx run -f .env.local -- pnpm db:migrate,确保变量进入进程环境。
  • 生产服务器在启动阶段先跑迁移再对外服务。这一点可由源码印证:apps/server/src/startup/checks.ts 第 34 行附近调用 migrate(db, { migrationsFolder: resolveWorkspaceFolder("migrations") }),迁移目录即仓库根 migrations/。因此手动 pnpm db:migrate 主要用于首次初始化、迁移调试,或不启动应用直接应用迁移。

八、环境配置:.env.example、必填三项与可选依赖

文档 Environment 一节的完整要求如下:

  1. .env.example 复制为 .env.local三个必填变量
    • APP_URL(默认 http://localhost:3000);
    • DATABASE_URL(默认 postgresql://postgres:postgres@localhost:5432/postgres);
    • AUTH_SECRET(任意非空字符串;.env.example 注释建议用 openssl rand -hex 32 生成)。
  2. S3 / SeaweedFS 可选:当 S3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYS3_BUCKET 全部设置时启用 S3 兼容存储。仓库检入的 .env.example 预置了 SeaweedFS 默认值,因此要么把 seaweedfs compose 服务一起启动,要么注释掉这些 S3 变量以使用 <workspace>/data 下的本地文件系统存储。LOCAL_STORAGE_PATH 若设置必须是绝对路径——这与 packages/env/src/server.ts 中的校验一致(z.string().min(1).refine(isAbsolute, ...),约 L66),而 packages/env/src/server.ts 同时展示了整套服务端变量的 Zod 校验规则(如 DATABASE_URL 必须为 postgres 协议 URL、SERVER_PORT 默认 3001)。
  3. REDIS_URLENCRYPTION_SECRET 对核心简历流程是可选的,但已保存的 AI provider 与认证后的 /agent 工作区两者都要求。开发这些功能时启动 redis compose 服务并在 .env.local 中设置两者;宿主机开发用 REDIS_URL=redis://localhost:6379,容器内应用用 REDIS_URL=redis://redis:6379(与 compose.dev.yml 中 app 服务的 REDIS_URL 一致)。源码层面可印证强度要求:packages/env/src/server.ts 约 L76 规定 ENCRYPTION_SECRET 至少 32 个字符。
  4. dotenvx 前缀规则:跑开发服务器或迁移命令时加 dotenvx run -f .env.local -- 前缀,例如 dotenvx run -f .env.local -- pnpm dev;测试、typecheck、lint、边界检查与 pnpm build 默认不需要该前缀,若因缺某个环境变量失败,再补前缀重试。

补充一个机制细节:packages/env/src/server.ts 在模块加载时会用 process.loadEnvFile 自动加载根 .env(已有进程环境变量优先),这正是文档 Codebase map 中"packages/env 自动加载根 .env"一句的出处。

九、常用命令表(完整继承自原文档)

任务 命令
安装依赖 pnpm install
仅启动 Postgres sudo docker compose -f compose.dev.yml up -d postgres
启动 Postgres + SeaweedFS sudo docker compose -f compose.dev.yml up -d postgres seaweedfs seaweedfs_create_bucket
启动完整开发基础设施 sudo docker compose -f compose.dev.yml up -d postgres redis seaweedfs seaweedfs_create_bucket
生成迁移 dotenvx run -f .env.local -- pnpm db:generate
执行迁移 dotenvx run -f .env.local -- pnpm db:migrate
开发服务器 dotenvx run -f .env.local -- pnpm dev(监听 3000 端口)
仅 web 开发服务器 dotenvx run -f .env.local -- pnpm dev:web
Lint/格式化 pnpm check(Biome)
边界检查 pnpm exec turbo boundaries
测试 pnpm test(Vitest)
构建 pnpm build
类型检查 pnpm typecheck

命令表与根 package.jsonscripts 完全对应(build/typecheck/test 均委托 turbo rundb:generate/db:migrate 通过 --filter=@reactive-resume/db 定向到 db 包)。

聚焦验证时优先用包过滤器而非全仓命令

pnpm --filter web typecheck
pnpm --filter @reactive-resume/pdf test
pnpm --filter @reactive-resume/api test
pnpm exec turbo boundaries

通过 pnpm --filter <package> test -- <path> 运行 Vitest 时,测试路径是包内相对路径

十、Gotchas:从启动行为到 Turborepo 严格环境模式

文档陷阱清单逐条都有仓库证据可查:

  • 启动即迁移:服务端启动路径会在对外服务前自动执行迁移,所以 pnpm db:migrate 主要用于首次设置、迁移调试或不启动应用直接应用迁移(见上文 apps/server/src/startup/checks.tsmigrate 调用)。
  • 邮件需要 SMTP 配置:未配置时邮件被打印到控制台,开发阶段可接受——验证链接会出现在服务器日志里。
  • pre-commit hooklefthook.yml 的 pre-commit 会对暂存文件跑 biome check,提交前先跑 pnpm check 以免 hook 失败。
  • pnpm check 具有写能力:根 package.json 中定义为 biome check --write --unsafe . && markdownlint-cli2 --fix && github-actionlint ...,会实际修改文件;需要只读检查时应使用更窄的 Biome 命令。
  • Biome 风格约定(见 biome.json):tab 缩进、双引号、行宽 120、整理 import 分组、对 clsx/cva/cn 排序 Tailwind 类。
  • 多数包用 tsgo --noEmit 做类型检查、vitest run --passWithNoTests 跑测试。
  • 工作树里可能有无关的本地改动:动手前先 git status --short,避免回滚你没动过的文件。
  • 新增环境变量必须在 turbo.json 中登记。Turborepo 2.x 默认运行在严格 env 模式——会过滤未列入 globalEnv(或任务级 env/passThroughEnv)的变量。任何加到 packages/env/src/server.ts 的新变量,都必须同步加入根 turbo.jsonglobalEnv 数组(约 L47-L91,当前已包含 DATABASE_URLAUTH_SECRETREDIS_URLENCRYPTION_SECRET、全部 S3 变量与 FLAG_* 特性开关等),否则变量在子进程运行时为 undefined——即使它在 OS/容器环境中已正确设置。

十一、Agent skills:Issue tracker 与多上下文领域文档

文档还引用了两份 Agent 技能文档,构成"工程上下文检索"的配套机制:

  • Issue trackerdocs/agents/issue-tracker.md 规定 issue 与 spec 统一跟踪在 GitHub Issues,全部操作走 gh CLI——创建(heredoc 多行 body)、阅读(gh issue view <number> --comments)、列表(--json + jq 过滤)、评论、加/删标签、带评论关闭;并约定 PR 不作为外部 feature request 的受理面,以及 /wayfinder 场景下 map/child ticket/blocking/frontier/claim/resolve 的完整操作语义。
  • 领域文档docs/agents/domain.md 定义多上下文 domain-doc 布局——仓库根 CONTEXT-MAP.md 是上下文边界的权威来源,各上下文可有 CONTEXT.md 词汇表与上下文内 docs/adr/ 决策记录;全局系统级决策在 docs/adr/(当前含 0001-workspace-boundaries.md0002-agent-ai-sdk-adoption.md 两篇)。要求:探索前先读相关上下文文档、输出使用词汇表术语、与既有 ADR 冲突时显式声明而不是静默覆盖。

十二、小结:这份 AGENTS.md 值得借鉴的三件事

  1. 一切规则都挂到可执行物上:包边界规则对应 turbo boundaries 的真实 deny 配置(turbo.json),命令表对应根 package.json 的真实 scripts,架构描述对应 Dockerfilecompose.dev.ymlapps/server/src/startup/checks.ts 的源码事实——Agent 可以逐条验证,而不是照单全收。
  2. 用"所有权地图 + 决策树"替代口头架构:Codebase map 回答"东西在哪",Placement decision tree 回答"新东西放哪",两者配合消除了 monorepo 最常见的分歧来源。
  3. 陷阱清单比功能清单更有价值:drizzle-kit 不读 .envpnpm check 会写文件、Turborepo 严格 env 模式会吞变量——这三条都是只会在运行时暴露的隐性故障,写在文档里直接省掉一轮排障。

适用前提说明:文中版本与命令以当前仓库状态为准(仓库根 package.json 声明版本 5.2.9、packageManagerpnpm@11.24.0turbo 2.x);AGENTS.md 中"pnpm 11.21.0"为文档写作时点的固定要求,与当前 packageManager 字段存在版本差,实际以 package.json 声明为准。

登录后查看全文
热门项目推荐
相关项目推荐