首页
/ Twenty Codex 插件贡献指南:Skill 扩展、Reference 文档、校验器编写与版本发布规范

Twenty Codex 插件贡献指南:Skill 扩展、Reference 文档、校验器编写与版本发布规范

2026-09-07 16:09:40作者:咎竹峻Karen

本文以 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-appdevelop-appmanage-apppublish-appuse-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 定义:

  • validate target 执行 node ./scripts/validate.js,其缓存 inputs 覆盖 .codex-plugin/.mcp.jsonassets/package.jsonreferences/scripts/skills/templates/ 八个目录——也就是说,这些目录下的任何内容变更都会重新触发校验;
  • test target 执行 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/ 下按职责划分的模块"(libmetadataassetsskillsreferencescross-doc-contractssetup-helper),这正是后文"编辑校验器"一节要求新断言"放进正确的模块"的历史原因。

门禁中还有一条明确的依赖约束:scripts/ 目录禁止引入任何新的运行时依赖,校验器只能使用 Node.js 内置模块。从源码可以印证这一约定——skills.jsreferences.js 等校验模块的 require 仅包含 node:fsnode:path 和本地 ./lib,没有第三方依赖;测试文件同理只使用 node:testnode:assert。这让校验脚本在任何环境下都无需安装依赖即可运行。

新增一个 Skill:四步操作规范

插件当前维护五个"规范 Skill"(canonical skills),新增第六个需要按以下顺序完成四个步骤。

第 1 步:创建 SKILL.md 与 agents/openai.yaml

skills/<name>/ 下创建两个文件:

  • skills/<name>/SKILL.md:YAML frontmatter 只允许 namedescription 两个字段,且 name 必须与目录名一致;
  • skills/<name>/agents/openai.yaml:包含 display_nameshort_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,触发词必须"互不重叠"才能避免路由冲突。自动化层面,assertSkillTriggerPhrasesskills.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 结果格式化契约等),新增一份遵循三步:

  1. 放置:按领域放入 references/<area>/<name>.md(当前领域目录包括 concepts/design/develop-app/manage-app/publish-app/use-twenty-mcp/),并在需要它的 SKILL.md 中添加链接;
  2. 注册:把相对路径加入 references.jsREQUIRED_REFERENCES 数组。当前该数组维护 14 条必备路径,assertReferences 会逐一检查文件存在性;
  3. 跨文档契约:如果该文档参与"跨文档契约"(即某个关键指引必须在多个文件间保持一致的表述),必须在同一个提交中更新 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)。

这一模式可以从现有代码中完整还原:

  1. 模块化落点:七个 validators/*.js 模块按被校验对象划分——metadata.js 管清单/版本/安全,skills.js 管 Skill 形态,references.js 管文档存在性,cross-doc-contracts.js 管文档间一致性,setup-helper.js 管 shell 助手脚本,assets.js 管市场资产,lib.js 提供 readTextlistFilesparseSkillFrontmatter 等共享工具函数。新断言应遵循"单一被校验对象"原则放入对应模块,而非塞进入口文件;
  2. 入口注册:在 validate.js 中追加一行 module.assertXxx(fail) 调用,失败信息经统一的 failures 数组汇总后一次性打印并 process.exit(1)
  3. 双向测试夹具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 的要求是:

  1. 一个 PR 只解决一个关注点(one concern per PR)——这与校验器的模块化设计相互呼应:一次只动一个 validators/*.js,评审边界清晰;
  2. 标题前缀使用 feat|fix|docs|chore(codex-plugin):
  3. 提及 CHECKLIST.md 中被触及的行号

第三点依赖 CHECKLIST 作为"权威合规矩阵":它把 Codex 插件构建指南的每条要求映射为 [automated](对应某个 validate.js 断言函数,如 assertSkillsassertNoLegacySkillReferences)或 [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_SKILLSREQUIRED_REFERENCES、版本三同步,还是某条跨文档契约——然后按"新函数 + 入口调用 + 正反夹具"的标准动作补齐测试,最后用 validatetest 双门禁收尾。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388