Twenty Codex 插件贡献指南:Skill 扩展、Reference 文档、校验器编写与版本发布规范
本文以 packages/twenty-codex-plugin 的维护者文档 CONTRIBUTING.md 为核心,系统讲解 Twenty 官方 Codex 插件(用于通过 Codex 创建、开发、部署、发布和查询 Twenty 应用的插件包)的完整贡献流程。读完后你将掌握:合并前必须通过的 validate/test 双门禁、新增 Skill 的四步操作规范、Reference 文档的注册机制、三文件同步的版本号提升流程,以及"校验器也要被测试"的校验器扩展模式,并了解每项规则背后的自动化断言如何实现。
插件定位与维护者边界
CONTRIBUTING.md 开篇即明确了读者边界:这份文档面向插件本身的维护者(maintainers);而使用插件的 Agent 则应查阅 AGENTS.md(其中包含跨 Skill 操作规则与 Skill 路由表)。
该插件包是发布到 Codex Marketplace 的插件源码,由五个聚焦的 Skill 组成(create-app、develop-app、manage-app、publish-app、use-twenty-mcp),并捆绑 twenty-docs 公共文档 MCP 服务与一条命令的 workspace MCP 配置助手。理解维护者的工作本质:这个包不只是"文档 + 提示词",而是一套由自动化校验脚本严格约束的内容工程——每一次对 Skill 文案、Reference 文档或清单文件的修改,都可能触发 validate.js 中数十条断言。
合并前门禁:validate 与 test 双检查
CONTRIBUTING.md 规定合并前必须同时通过以下两条命令:
npx nx run twenty-codex-plugin:validate
npx nx run twenty-codex-plugin:test
两条命令均可在 project.json 中找到对应的 Nx target 定义:
validatetarget 执行node ./scripts/validate.js,其缓存 inputs 覆盖.codex-plugin/、.mcp.json、assets/、package.json、references/、scripts/、skills/、templates/八个目录——也就是说,这些目录下的任何内容变更都会重新触发校验;testtarget 执行node --test ./scripts/__tests__/*.spec.js,即直接用 Node.js 内置测试运行器跑校验器自身的单元测试。
从源码结构看,validate.js 已被重构为一个极薄的入口文件(共 39 行):它 require 七个校验模块,然后逐行调用断言函数,最后统一收集失败信息并以非零退出码终止。当前入口文件注册的断言调用链为:
metadata.assertJsonMetadata # 版本同步、manifest 路径、files 完整性
metadata.assertNoBundledMcpConfig # 禁止捆绑 workspace MCP / 密钥 / 非占位 URL
metadata.assertInterfaceFields # interface 字段格式(brandColor、category 等)
metadata.assertMarketplaceTemplate # 市场模板与插件版本一致
assets.assertAssets # logo/screenshot 资产完整性
skills.assertSkills # 规范 Skill 集合与文件形态
skills.assertSkillTriggerPhrases # "When To Use" 触发词章节
skills.assertNoLegacySkillReferences # 禁止遗留 Skill 名残留
references.assertReferences # 必备 Reference 文件存在性
references.assertHowAppsWork # 基础概念文档完整性与链接关系
crossDocContracts.*(4 条) # 跨文档契约(MCP 格式、front component 指引等)
setupHelper.assertSetupHelper # setup-mcp.sh 语法与 URL 归一化
CHANGELOG.md 中记录了这次重构的背景:scripts/validate.js 从单个 760 行文件拆分为"薄入口 + scripts/validators/ 下按职责划分的模块"(lib、metadata、assets、skills、references、cross-doc-contracts、setup-helper),这正是后文"编辑校验器"一节要求新断言"放进正确的模块"的历史原因。
门禁中还有一条明确的依赖约束:scripts/ 目录禁止引入任何新的运行时依赖,校验器只能使用 Node.js 内置模块。从源码可以印证这一约定——skills.js、references.js 等校验模块的 require 仅包含 node:fs、node:path 和本地 ./lib,没有第三方依赖;测试文件同理只使用 node:test 与 node:assert。这让校验脚本在任何环境下都无需安装依赖即可运行。
新增一个 Skill:四步操作规范
插件当前维护五个"规范 Skill"(canonical skills),新增第六个需要按以下顺序完成四个步骤。
第 1 步:创建 SKILL.md 与 agents/openai.yaml
在 skills/<name>/ 下创建两个文件:
skills/<name>/SKILL.md:YAML frontmatter 只允许name和description两个字段,且name必须与目录名一致;skills/<name>/agents/openai.yaml:包含display_name、short_description(不超过 64 字符)、default_prompt(必须提及$<name>形式的 Skill 引用)。
以现有的 create-app skill 为例:
interface:
display_name: "Create App"
short_description: "Scaffold and develop Twenty apps."
default_prompt: "Use $create-app to scaffold and run a new Twenty app."
注意 default_prompt 中的 $create-app——这正是校验器要求的 $<skill-name> 引用格式。这些规则并非纸面约定,而是由 skills.js 中的 assertSkills 逐条强制:
- frontmatter 字段若超出
name/description白名单,直接失败("frontmatter should only include name and description"); short_description超过 64 字符失败;default_prompt不含$<skillName>字符串失败。
第 2 步:编写 "When To Use" 触发词章节
SKILL.md 正文必须包含一个 ## When To Use 章节,列出 4~6 条用户语言风格的触发短语,并附带"do not use this skill for X"的边界声明(明确指向其余 Skill)。以 create-app 的 SKILL.md 为样板:
# When To Use
Pick this skill when the user wants to start a brand-new Twenty app from scratch.
Representative triggers:
- "I want to build a Twenty app"
- "scaffold a new Twenty app"
- "start a new Twenty plugin / extension / integration"
...
Do not use this skill when the app already exists — use `develop-app` to add
features, `manage-app` for sync/deploy/troubleshooting, `publish-app` for
marketplace prep, or `use-twenty-mcp` to query workspace data.
这一章节的必要性在于 Skill 路由:Codex 根据用户输入决定加载哪个 Skill,触发词必须"互不重叠"才能避免路由冲突。自动化层面,assertSkillTriggerPhrases(skills.js)用正则 /^#+\s+When To Use\s*$/m 检查该章节存在;而"描述互不重叠、职责单一"则属于 CHECKLIST.md 中标记为 [manual] 的 S6/S8 行,需要人工评审并在签核日志中记录(现有日志显示 2026-05-28 曾完成过一次全量 Skill 描述对照评审)。
第 3 步:注册到 EXPECTED_CANONICAL_SKILLS
把新 Skill 名加入 skills.js 中的 EXPECTED_CANONICAL_SKILLS 数组(当前为五个名字)。这个数组是双向约束的:
- 数组中的名字如果缺少对应目录,
assertSkills报 "canonical skill is missing"; skills/下实际存在但不在数组中的目录,会绕过规范集检查但依旧受到 frontmatter/openai.yaml形态断言约束。
同时要注意 assertNoLegacySkillReferences 的存在:它会扫描插件包内所有 .md/.yaml/.yml 文件,禁止出现 LEGACY_SKILL_NAMES 中的遗留 Skill 名(如 setup-mcp 这类已更名技能)——即使是在描述文字中提到也不行(setup-mcp.sh 脚本名除外)。这意味着新增文档时也不要引用任何已废弃的旧 Skill 名。
第 4 步:跑门禁
完成上述修改后执行 npx nx run twenty-codex-plugin:validate,确认 assertSkills 对新目录的断言全部通过。
新增一份 Reference 文档
Reference 文档是 Skill 的"深度资料层"(如数据模型、布局规则、CLI 语义、MCP 结果格式化契约等),新增一份遵循三步:
- 放置:按领域放入
references/<area>/<name>.md(当前领域目录包括concepts/、design/、develop-app/、manage-app/、publish-app/、use-twenty-mcp/),并在需要它的SKILL.md中添加链接; - 注册:把相对路径加入 references.js 的
REQUIRED_REFERENCES数组。当前该数组维护 14 条必备路径,assertReferences会逐一检查文件存在性; - 跨文档契约:如果该文档参与"跨文档契约"(即某个关键指引必须在多个文件间保持一致的表述),必须在同一个提交中更新 cross-doc-contracts.js。
第三点值得展开。所谓跨文档契约,是把"多份文档之间必须共享同一条规则"固化为代码断言。例如 validate.spec.js 中的 assertCliGuidanceSplit 相关测试展示了契约的具体形态:
- 把
references/manage-app/cli-and-sync.md中的yarn twenty apply改写为已废弃的yarn twenty dev --once,断言立即捕获; - 在
skills/manage-app/SKILL.md末尾追加一句 "Run yarn twenty dev --once after editing.",断言同样失败,因为 CLI 语义只允许在专属 Reference 文档中出现。
CHANGELOG.md 还记录了另一条契约:assertTestingGuidance 确保"集成测试必须针对 2021 端口的独立测试实例(TWENTY_API_URL=http://localhost:2021)"这一规则不会从 manage skill 或 references 中漂移。这类机制保证插件文档体系不会出现"两处说法打架",也解释了为什么 CONTRIBUTING.md 强调跨文档契约修改必须与文档本身同一提交完成——文档改了而契约没改,validate 会直接失败。
版本号提升:三文件同步 + Changelog 迁移
CONTRIBUTING.md 规定版本号提升时必须同时移动三处版本声明:
| 文件 | 作用 | 校验断言 |
|---|---|---|
| package.json | npm 工作区包版本 | assertJsonMetadata 检查与 plugin.json 一致 |
| .codex-plugin/plugin.json | Codex 插件清单版本(SemVer) | 同上 |
| templates/marketplace.example.json | 用户本地市场安装模板 | assertMarketplaceTemplate 检查版本漂移 |
当前仓库三处均为 0.1.0,与清单文件互相印证。负向测试也覆盖了漂移场景:validate.spec.js 会把模板中的版本临时改成 0.0.0 并断言 assertMarketplaceTemplate 报 "version must match",同样会把 package.json 版本临时改成 99.99.99 验证 assertJsonMetadata 的捕获能力。
三处同步之后,还要把 CHANGELOG.md 中的 [Unreleased] 区块移动到新的 [X.Y.Z] - YYYY-MM-DD 标题下。该 Changelog 遵循 Keep a Changelog 格式,条目会显式引用五个规范 Skill 与 scripts/validate.js,便于读者按改动面定位影响。
SemVer 判定标准(原文照录):
- patch:文案或校验规则修复(copy/validation fixes);
- minor:新增 reference、新增校验规则、新增 skill 章节;
- major:重命名规范 Skill,或破坏 frontmatter /
agents/openai.yaml的字段形态。
注意 major 的判定对象不是代码 API 而是面向 Agent 的契约:Skill 名会被 default_prompt 以 $<name> 引用、会被 EXPECTED_CANONICAL_SKILLS 固化,重命名即破坏所有下游引用,因此升主版本。
编辑校验器:新断言 = 新函数 + 新调用 + 新夹具
CONTRIBUTING.md 给校验器扩展的定义非常精确:
新断言 = 在正确的
scripts/validators/*.js模块中新建一个函数 + 从scripts/validate.js调用它 + 在scripts/__tests__/validate.spec.js中至少一个通过夹具(passing fixture)和一个失败夹具(failing fixture)。
这一模式可以从现有代码中完整还原:
- 模块化落点:七个
validators/*.js模块按被校验对象划分——metadata.js管清单/版本/安全,skills.js管 Skill 形态,references.js管文档存在性,cross-doc-contracts.js管文档间一致性,setup-helper.js管 shell 助手脚本,assets.js管市场资产,lib.js提供readText、listFiles、parseSkillFrontmatter等共享工具函数。新断言应遵循"单一被校验对象"原则放入对应模块,而非塞进入口文件; - 入口注册:在 validate.js 中追加一行
module.assertXxx(fail)调用,失败信息经统一的failures数组汇总后一次性打印并process.exit(1); - 双向测试夹具:validate.spec.js 组织为"smoke + negative"两段——smoke 段对每个断言断言"当前插件状态下失败列表为空";negative 段通过三个测试助手模拟故障现场:
withJsonMutation(filePath, mutator, body):临时改写 JSON 文件并在finally中还原(用于版本漂移、brandColor非法值、非法category等场景);withFileMutation(filePath, mutator, body):临时改写文本文件(用于向SKILL.md注入废弃指引、删除 "When To Use" 章节等);withExtraFile(filePath, contents, body):临时落盘一个多余文件再删除(用于验证"包内不得出现.app.json、非占位 URL、Bearer Token"等负向断言)。
这种"在真实插件文件上做受控突变、事后还原"的夹具风格,使每个校验器都有可复现的正反两面证据,而不是仅靠人工审阅。CHECKLIST.md 中的 P2 行("validate.js 有自己的单元测试")正是对这一机制的流程性确认。
PR 规范与合规矩阵
CONTRIBUTING.md 对 Pull Request 的要求是:
- 一个 PR 只解决一个关注点(one concern per PR)——这与校验器的模块化设计相互呼应:一次只动一个
validators/*.js,评审边界清晰; - 标题前缀使用
feat|fix|docs|chore(codex-plugin):; - 提及 CHECKLIST.md 中被触及的行号。
第三点依赖 CHECKLIST 作为"权威合规矩阵":它把 Codex 插件构建指南的每条要求映射为 [automated](对应某个 validate.js 断言函数,如 assertSkills、assertNoLegacySkillReferences)或 [manual](需人工签核并在日志表登记日期、行号、评审人)两类检查,并定义了发布标准——validate 退出码为 0 且所有 [manual] 行均有最新签核日期。其中 P3 行直接要求确认"版本提升流程已记录在 CONTRIBUTING.md",即本文所依据的这份文档本身也是被校验对象之一,形成闭环。
关键文件速查
| 路径 | 用途 |
|---|---|
| CONTRIBUTING.md | 维护者指南(本文骨架) |
| AGENTS.md | 使用插件的 Agent 的跨 Skill 操作规则 |
| CHECKLIST.md | 合规矩阵与人工签核日志 |
| CHANGELOG.md | 版本变更记录 |
| scripts/validate.js | 校验入口(薄入口,注册全部断言) |
| scripts/validators/ | 按职责划分的校验模块 |
| scripts/tests/validate.spec.js | 校验器自身的正反夹具测试 |
| project.json | Nx validate / test / setup:mcp / fmt targets |
| .codex-plugin/plugin.json | 插件清单(版本三同步之一) |
小结
Twenty Codex 插件的贡献流程本质上是一套"文档即代码"的工程实践:Skill、Reference、清单模板中的每一条文案约定都被 scripts/validators/ 下的断言函数固化,断言函数本身又由 node:test 的正反夹具约束,而 CHECKLIST.md 把无法自动化的要求(描述不重叠、截图真实性)纳入人工签核流程。对维护者而言,掌握这套流程的核心心法是:任何内容改动都要想清楚它命中哪条断言——是 EXPECTED_CANONICAL_SKILLS、REQUIRED_REFERENCES、版本三同步,还是某条跨文档契约——然后按"新函数 + 入口调用 + 正反夹具"的标准动作补齐测试,最后用 validate 与 test 双门禁收尾。
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 StartedRust0627
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