Strapi 源码协作指南:从 AGENTS.md 看懂 Strapi Monorepo 的架构约定、测试体系与 AI 工具链
本篇技术文章基于 Strapi 官方仓库根目录下的 AGENTS.md 展开——这是一份专为 AI 编码代理(Agent)设计的仓库协作指南,但它本质上也是人工贡献者理解 Strapi Monorepo 的权威速查手册。读完本文,你将掌握 Strapi 的代码目录布局与核心架构约定(DI 容器、Server/Admin 双入口、Document Service)、从克隆到本地开发的完整命令链路(yarn setup、yarn develop)、覆盖单元测试到 Playwright E2E 的测试体系,以及仓库独创的 .ai/skills 技能同步机制(yarn ai:sync)。
文档定位:一份写给 Agent、也适合人的协作契约
AGENTS.md 位于仓库根目录,开篇即给出三条基本事实:Strapi 是一个开源 headless CMS;仓库采用 Yarn workspaces + Nx 组织的 Monorepo;目标分支是 develop(不是 main),所有 PR 一律提交到 develop。
根目录的 CLAUDE.md 只有一行内容 @AGENTS.md——这是一种常见的做法:Claude 系列的工具链会自动加载该文件并跟随引用,从而把全部协作规则收敛到 AGENTS.md 这一处单一事实来源,避免多套 Agent 配置互相打架。
从文档的分节结构(Repository Structure / Architecture / Monorepo Setup / Development / Build / Testing / Quality Gates / Security / PR Guidelines / Notes for Agents)可以推断,它的写作目标是“让 AI Agent 在仓库中安全、高效地工作”,而其中每一条规则对人工贡献者同样成立。
仓库结构与核心包
AGENTS.md 给出的顶层目录职责划分如下:
packages/core/ # 框架本体:strapi, admin, database, content-manager, types, utils…
packages/plugins/ # 官方插件:users-permissions, i18n, graphql, documentation…
packages/providers/ # 邮件与上传 provider 实现
packages/utils/ # 共享工具:logger, eslint-config, tsconfig, vitest-config
packages/cli/ # CLI 工具:create-strapi-app, cloud-cli
examples/ # 仅供开发沙箱使用——不发布,不用于生产问题修复
docs/ # 贡献者文档
tests/ # 集成、E2E 与 CLI 测试基础设施
这份划分与根 package.json 中声明的 Yarn workspaces 完全对应:packages/*、packages/*/*、examples/*、examples/plugins/* 等目录全部纳入 workspace 管理,packageManager 字段锁定为 yarn@4.12.0。
AGENTS.md 进一步列出最重要的几个包(文档明确说明该表不穷尽,完整清单可运行 yarn workspaces list 查看):
| Package | 职责 |
|---|---|
@strapi/strapi |
主框架入口(Koa server) |
@strapi/admin |
React 18 管理后台 |
@strapi/core |
核心业务逻辑 |
@strapi/database |
数据库抽象(MySQL、PostgreSQL、MariaDB、SQLite) |
@strapi/content-manager |
内容管理 UI |
@strapi/types |
共享 TypeScript 类型定义 |
@strapi/permissions |
RBAC 权限引擎 |
@strapi/plugin-users-permissions |
JWT 认证 |
框架入口可以在 packages/core/strapi/src/index.ts 找到,其 package.json 通过 exports/main(./dist/index.js)对外发布构建产物。
架构核心:DI 容器、双入口与 Document Service
AGENTS.md 的 Architecture 一节浓缩了 Strapi 框架最重要的六条设计约定,每一条都有明确的“应该/不应该”:
Strapi类是 DI 容器与中枢。它通过工厂模式注入为strapi参数(如createService(strapi)),文档明确禁止使用global.strapi,始终要求走依赖注入。它对外暴露strapi.documents、strapi.db、strapi.log等能力。生命周期为 Register → Bootstrap → Start → Destroy,其中start()内部会调用load(),而load()负责执行 register + bootstrap。- Server / Admin 分离。Koa.js HTTP 服务在
@strapi/strapi,React/Redux 管理后台在@strapi/admin。同时涉及两端的包会导出双入口:strapi-server(Node.js 逻辑)与strapi-admin(UI 组件)。 - Document Service 是一级内容 API(
strapi.documents),取代了已弃用的 Entity Service。读写内容一律用它,除非你正在@strapi/database内部工作,否则禁止直接写裸数据库查询。 - 多态关联(morph* relations):文档指向 docs/docs/docs/01-core/database/01-relations/polymorphic-relations.md(该文件确实存在于仓库中),讲解存储方式、DB populate,以及
getDeepPopulate/relation 遍历如何与morphToOne和基于 join 的 morph 交互。 - 插件系统:插件通过同样的
strapi-server/strapi-admin双结构注册 routes、controllers、services、content types 和 middleware;官方插件统一放在packages/plugins/。 - Content Types 用 JSON 记法定义(注意:不是 JSON Schema 规范),每个内容类型有一个
schema.json文件,数据库层会据此自动生成表结构;文档强调永远不要为内容类型变更手写 raw migration。 - EE / CE 分离:部分功能仅 Enterprise Edition 可用,在运行时门控,测试时需配合下文 EE 开关。
@strapi/types是共享类型的单一事实来源:类型应从它导入,并去改进它,而不是在本地重复定义。
这些约定共同决定了“改 Strapi 源码应该改在哪里、怎么改”:内容逻辑走 Document Service、插件逻辑走双入口注册、类型统一收敛到 @strapi/types。
AI 工具链:.ai/skills 与 yarn ai:sync 同步机制
AGENTS.md 中一个区别于普通贡献指南的特色章节是 Skills directories,它定义了一套“仓库级技能(skill)分发”机制:
.ai/skills/是唯一的提交源:其中每个包含SKILL.md的子目录就是一个技能。当前仓库中该目录下有一个技能 .ai/skills/git-conventions/SKILL.md,内容是 Strapi 提交规范(允许的 commit type 列表、subject 规则、type 选择决策指南)。- 三个 AI 工具的 well-known 目录是符号链接目标:
.agents/skills/、.claude/skills/、.cursor/skills/,由yarn ai:sync维护。 - 新增或删除技能后必须运行
yarn ai:sync保持三个目标目录一致。链接仅存在于本地(目标目录被 gitignore,只提交.ai/skills/的内容)。 - Windows 兼容性:CLI 在 Windows 上创建目录 junction(无需额外配置);而目录符号链接需要开发者模式(Developer Mode)或管理员 shell。
三个命令及其语义(引自 AGENTS.md):
yarn ai:sync # 幂等——在 3 个工具目录中创建/清理 .ai 链接
yarn ai:unlink # 仅移除 .ai 来源的链接(保留 brain 的链接)
yarn ai:status # 只读报告:linked / missing / conflict / stale
对照仓库实现可以印证文档的每一句话:
- package.json 中三条脚本均指向同一个入口:
tsx scripts/ai-tooling/index.ts <sync|unlink|status>,入口脚本 scripts/ai-tooling/index.ts 还支持--force(仅对sync有效)。 - scripts/ai-tooling/links.ts 定义了
SOURCE_PATH = '.ai/skills'与TARGET_PATHS = ['.agents/skills', '.claude/skills', '.cursor/skills'],并用process.platform === 'win32'决定链接类型为junction还是dir——这正是文档中 Windows 说明的源码依据。classify()函数把目标位置状态分为absent/ours/foreign-link/real四类,sync()据此执行 linked/relinked/forced/pruned 动作,并在遇到外来链接(foreign-link)或真实目录(real)时跳过并告警——这就是ai:unlink“只移除 .ai 来源链接、保留 brain 链接”的实现原理。 .gitignore中.claude/、.cursor/(保留!.cursor/worktrees.json)、.agents/、.brain*均被忽略,验证了“目标目录不进版本库”的说法。setup脚本的收尾步骤是tsx scripts/ai-tooling/post-setup.ts,它会调用hasMissingLinks()检测缺失链接,并打印提示横幅“AI tooling missing initialization: run yarn ai:sync”——即 AGENTS.md 所说 setup “会在链接缺失时提示运行 ai:sync”。
Monorepo 环境搭建与 Worktree 引导
环境要求:Node ≥22 且 ≤26(以 AGENTS.md 为准;注意根 package.json 的 engines 字段写的是 >=20.0.0 <=26.x.x,二者口径略有差异,开发时以 AGENTS.md 的更严格约束执行)、Yarn 4。
文档给出的初始搭建命令(克隆后运行一次):
yarn install
yarn setup # clean + 构建全部包;链接缺失时提示运行 ai:sync
yarn ai:sync # 将 .ai/skills 链接进 .agents/ .claude/ .cursor/
对照 package.json,setup 实际执行 yarn && yarn clean && yarn build --skip-nx-cache && tsx scripts/ai-tooling/post-setup.ts,即“安装 → 清理 → 全量构建(绕过 Nx 缓存)→ 检查 AI 工具链链接”。
对于新创建的 git worktree,AGENTS.md 给出一套明确的引导流程:
- 检查
.claude/skills/git-conventions是否能解析到仓库的.ai/skills/git-conventions;链接缺失或过期则运行yarn setup:worktree(即yarn install && yarn build && yarn ai:sync)。 - 若
.brain已存在,不要初始化或刷新它。 - 若
.brain不存在,寻找用户级brain-start技能:不可用时静默继续(这是外部贡献者的正常路径);可用时读取它、解析 CMS Brain CLI 的规范宿主检出位置,并运行"$BRAIN_CLI" refresh --project strapi/strapi。 - 若 CMS Brain 已安装但刷新失败,需报告失败并遵循 bootstrap 技能的补救步骤;只有“可选技能缺失”这一种情况允许无操作通过。
本地开发:沙箱运行与数据库切换
AGENTS.md 的 Development 一节给出了标准的开发回路,全部围绕 examples/getstarted 沙箱展开:
# 运行开发沙箱
cd examples/getstarted
yarn develop # SQLite(默认)
# 启动非内存数据库(postgres/mysql)
docker-compose -f docker-compose.dev.yml up -d
DB=postgres yarn develop # PostgreSQL
DB=mysql yarn develop # MySQL
# 监听全部包 + 沙箱 admin watch(两个终端)
yarn watch # 终端 1,仓库根目录
cd examples/getstarted && yarn develop --watch-admin # 终端 2
这些命令都有仓库内的直接依据:
- examples/getstarted/package.json 中
develop脚本就是strapi develop; - 仓库根的 docker-compose.dev.yml 提供本地数据库容器;
- 根 package.json 中
watch脚本为nx watch --all -- 'nx run-many --targets build:code,build:types --projects $NX_PROJECT_NAME',即监听源码变更并增量重建对应包的 code 与类型产物,与“终端 1 跑 watch、终端 2 跑沙箱”的双终端工作流对应。
构建命令同样简洁:
yarn build # 全部包(code + types)
yarn build:code # 更快——跳过 .d.ts 生成
yarn nx build @strapi/admin # 单个包
测试体系:从单测到 Playwright E2E 的完整分层
AGENTS.md 将测试按“运行成本从低到高”组织为六个层次,这一优先级(先单测、后前端、再类型、再集成、最后 E2E)本身就是可操作的调试方法论。
单元测试(最快,最先跑)
单元测试文件位于各包内部的 __tests__/ 子目录:
yarn test:unit
yarn test:unit:watch
yarn test:unit:update # 更新快照
前端测试(管理后台)
yarn test:front # 以 IS_EE=true 运行(启用 EE 功能)
yarn test:front:ce # 以 IS_EE=false 运行(仅社区版)
yarn test:front:update # 更新快照(EE)
yarn test:front:update:ce # 更新快照(CE)
对照 package.json,test:front 的实现是 cross-env IS_EE=true nx run-many --target=test:front --nx-ignore-cycles -- --runInBand,EE 开关正是通过环境变量注入——这与 AGENTS.md 的 EE toggles 一节互相印证。
类型检查
yarn test:ts # 全部包 + front + back
API 集成测试
集成测试位于 tests/api/。文档特别强调:测试应用由工具自动生成,必须用 yarn test:generate-app 重新生成,不要复用过期应用(陈旧应用会引发误导性失败)。
yarn test:api # SQLite
yarn test:api --db=postgres
yarn test:api --db=mysql
yarn test:api -u # 更新快照
CLI 测试
CLI 测试位于 tests/cli/:
yarn test:cli
yarn test:cli:debug # 带调试输出
yarn test:cli:update # 更新快照
E2E 测试(Playwright)
E2E 测试位于 tests/e2e/tests/,按业务域组织(如 admin、content-manager、i18n):
yarn playwright install # 一次性安装浏览器
yarn test:e2e --setup --concurrency=1 # 串行跑全部域
yarn test:e2e --domains content-manager admin # 只跑指定域
yarn test:e2e --concurrency=3 # 3 个域并行
EE 开关与 Pre-PR 检查清单
IS_EE=true:前端测试中启用 Enterprise 功能(对应yarn test:front);RUN_EE=true:E2E 测试中启用 Enterprise 功能。
所有测试必须通过后才能合并。最低限度的本地检查是:
yarn test:unit && yarn test:front && yarn test:ts && yarn lint && yarn prettier:check
E2E(yarn test:e2e)按 CONTRIBUTING.md 是必需的,但因耗时长,通常依赖 CI 在每个 PR 上强制执行。
关于“何时该写测试”,文档给出了克制而实用的原则:Bug 修复必须加测试(先复现 Bug);功能开发在测试覆盖“有意义的行为”时才加,不为凑覆盖率;行为变更时同步更新受影响的测试。
质量门禁:提交规范、TypeScript 与格式化
Conventional Commits
提交信息必须遵循 Conventional Commits(由 Husky commit-msg 钩子与 CI 的 commitlint 双重强制执行)。格式为 <type>(<optional-scope>): <description>,允许的 type 为:feat fix chore ci docs enhancement test revert security future release。
文档给出的示例:
feat(content-manager): add bulk delete action
fix(database): preserve relation order during publish
chore(admin): migrate data-fetching to react-query
这些规则在仓库中有完整的配置对应:
- .husky/commit-msg 钩子执行
yarn exec commitlint --edit "$1"; - .commitlintrc.ts 继承
@commitlint/config-conventional,type-enum规则恰好列出上述 11 个 type(Error 级别),并禁用body-max-line-length、放行Merge branch '...'类合并提交; - 交互式提交可用
yarn commit(commitlint prompt);改动过任何package.json时应运行yarn version:check(实现为node scripts/check-package-versions.mjs && syncpack lint,检查各包版本一致性)。
此外,仓库把这套提交规范进一步沉淀为 Agent 技能 .ai/skills/git-conventions/SKILL.md,其中补充了 AGENTS.md 未展开的细节:type 与 subject 必须小写、subject 末尾不加句号、fix 的 subject 必须描述“Bug 是什么”而非“修复方案”、以及 perf/refactor/style/build 不在允许列表中(此类工作应归入 enhancement 或 chore)。
TypeScript 规则
- 类型从
@strapi/types导入——扩展或改进它,而不是本地重复; - 当存在(或可以合理定义)合适的类型时禁止
any,否则优先unknown; - push 之前跑
yarn test:ts。
Lint 与格式化
yarn lint # ESLint 全包
yarn lint:fix # 自动修复
yarn format # Prettier(2 空格缩进、单引号、分号、尾逗号、箭头参数带括号、100 字符宽、LF)
yarn prettier:check # 仅检查
根 package.json 中 lint 的实现是 nx run-many --target=lint --nx-ignore-cycles && yarn lint:other,即 Nx 逐包执行 lint,再对文档类文件做 Prettier 检查;lint:oxlint 则指向 packages/utils/oxlint-config/oxlint.config.ts。
安全规范
AGENTS.md 的 Security 一节列出五条不可逾越的底线:
- 永不提交密钥、凭据或 API key;
- 永不禁用或削弱认证/授权检查;
- 使用参数化查询——绝不把用户输入插值进原始 SQL 或数据库查询;
- 在 controller/service 边界校验并清洗所有用户输入;
- 处理 EE 门控功能时不得绕过 license 检查。
这些约束与 Strapi 作为 headless CMS 的定位一致:数据库层(@strapi/database)、RBAC 引擎(@strapi/permissions)和 JWT 认证(@strapi/plugin-users-permissions)正是上述规则在代码层面的落点。
PR 流程与 Agent 专属注意事项
PR 规则四条:从 develop 拉分支、目标只能是 develop;PR 描述中关联要修复的 issue;合并前所有测试必须通过;PR 描述必须遵循 .github/PULL_REQUEST_TEMPLATE.md 的结构,不得自创章节。
文档最后专设 Notes for Agents 一节,给出四条针对自动化/代理协作的关键提醒,其中两条涉及容易被误用的仓库区域:
examples/只是沙箱——用于复现和验证修复,除非被明确要求,绝不提交对其的修改。例外是examples/complex:它是 migration 测试 fixture(schema、seeds、validate-migration.js、数据库工具),CI 通过 tests/migration/ 对其运行migration_v5场景;文档还注明它未来可能迁入tests/migration/下。- workspace 依赖不用
workspace:*——packages/内部包之间互相引用时使用锁定的 semver 版本(如"5.42.0");workspace:*协议只出现在examples/应用和少量根 devDependencies 中(根 package.json 的devDependencies中@strapi/admin-test-utils等确为workspace:*,可佐证)。 - Entity Service 已弃用——内容操作一律使用 Document Service(
strapi.documents)。 - 生命周期阶段——访问任何 service 之前
strapi.isLoaded必须为true;插件和数据库要等load()阶段完成后才可用。
小结
AGENTS.md 用不到 300 行文字定义了与 Strapi 源码库协作的完整契约:develop 分支策略、Yarn workspaces + Nx 的目录与包划分、DI 优先的架构纪律、.ai/skills 单一来源 + 三目录符号链接的 AI 工具链(scripts/ai-tooling/ 提供幂等的 sync/unlink/status 实现)、以 examples/getstarted 为中枢的开发回路、六层测试金字塔与 EE 开关、commitlint 强制的提交规范,以及面向 Agent 的边界提醒(沙箱不入库、Document Service 替代 Entity Service、strapi.isLoaded 生命周期约束)。对希望深入 Strapi 源码的开发者,这份文档与 CONTRIBUTING.md 构成了“先读 AGENTS.md 建立全局观、再进具体包动手”的可靠路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00