diagram-design 多宿主插件发布架构:ADR 0008 单一插件根与同步版本门禁实践
2026/9/10 20:08:35 网站建设 项目流程

diagram-design 多宿主插件发布架构:ADR 0008 单一插件根与同步版本门禁实践

【免费下载链接】diagram-design38 editorial diagram types for Claude Code, Codex, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop.项目地址: https://gitcode.com/GitHub_Trending/di/diagram-design

Diagram Design 同时服务 Claude Code、Codex、Pi 与 Factory Droid 四个 AI 宿主,而本文要解析的 ADR 0008(accepted,v2.5.14)正是这套多宿主打包与发布体系的架构基石:它规定每个原生宿主只携带"最小的原生清单 + marketplace 元数据",所有 marketplace 统一解析到仓库根目录,让skills/diagram-design/commands/被全部宿主共享而不产生任何副本。读完本文,你将理解该 ADR 的决策动因、清单字段契约、同步版本门禁的校验逻辑、新宿主接入的 bootstrap 特例,以及如何在 Claude Code / Codex / Factory Droid 中完成安装与更新。


1. 背景:多宿主时代的发布困境

Diagram Design 是一个以单一 Skill 同时面向多个 AI 编程宿主分发的项目:Claude Code、Codex、Factory Droid,以及通过标准skills/目录发现的 Pi。多宿主分发天然会面临两条歧路:

  1. 依赖翻译兜底:Factory Droid 本身可以翻译 Claude 的插件布局,但依赖这一兜底意味着 Droid 的安装方式既没有文档化,也没有进入"包版本门禁"(package-version gate)的管控范围——版本发布时无法验证 Factory 侧是否真的可用、是否真的升级。
  2. 逐宿主复制:把 Skill 或 commands 分别拷贝进各宿主专属目录,会造成多个"事实来源"(sources of truth)。同一份行为在不同宿主上漂移、修 bug 时漏改某一份,都是必然结局。

此外还有一个更隐蔽的诉求:行为必须单一来源(single-sourced)。Diagram Design 的 Skill 本体、commands(如export-diagram.mdimport-drawio.md)应当是仓库中唯一的一份,所有宿主引用同一份,而不是各自维护一份。

2. 决策:最小原生清单 + 单一插件根

ADR 0008 给出的答案是"分而治之 + 归一化":

Claude、Codex、Factory 各自接收其宿主所需要的最小的原生清单(native manifest)与 marketplace 元数据;每一个 marketplace 都解析到仓库根目录,三个宿主在根目录下复用同一个skills/diagram-design/commands/,不产生任何复制。Pi 继续使用同一个根级包面(root package surfaces)。

这句话可以拆成三层:

  • 清单按宿主定制,但保持最小:每个宿主的插件清单只包含该宿主识别的字段,不强行统一成一个"万能清单";
  • marketplace 全部指向仓库根:三个 marketplace 文件中的插件 source 都指向.(仓库根目录),于是所有宿主共享根下的同一份内容;
  • Pi 走标准目录:Pi 通过仓库标准skills/包目录发现 Skill(README 中明确说明"Pi discovers it through the repo's standardskills/package directory"),与其余宿主看到的是同一份文件。

2.1 仓库中的清单与 marketplace 布局

从仓库根目录看,这套布局清晰可辨:

宿主原生清单marketplace 元数据插件源指向
Claude.claude-plugin/plugin.json.claude-plugin/marketplace.json./(仓库根)
Codex.codex-plugin/plugin.json.agents/plugins/marketplace.json{"source":"local","path":"./"}
Factory.factory-plugin/plugin.json.factory-plugin/marketplace.json./(仓库根)

而共享面则始终是仓库根下的两份内容:

  • skills/diagram-design/:Skill 本体(SKILL.md +references/参考文档 +scripts/辅助脚本 +assets/模板);
  • commands/:插件命令(doctor.mdexport-diagram.mdimport-drawio.mdimport-mermaid.mdprofile.md)。

验证器代码也印证了这一布局:verify-plugin-package.py中硬编码了MANIFEST_PATHS = {Claude: .claude-plugin/plugin.json, Codex: .codex-plugin/plugin.json, Factory: .factory-plugin/plugin.json}与三份 marketplace 路径,并检查skills/diagram-design/SKILL.mdcommands/目录必须存在于这个"被所有 marketplace 共同指向的插件根"中——从源码结构看,单一插件根不是一个口头约定,而是被校验脚本强制执行的布局事实。

3. 三份原生清单:共享身份元数据的字段契约

"最小清单"不等于"内容随意"。ADR 0008 明确规定三份原生清单必须携带完全相同的共享身份元数据:identity、description、version、author、repository、license、keywords。

verify-plugin-package.py中的SHARED_MANIFEST_FIELDS元组给出了这份契约的精确字段集合:

SHARED_MANIFEST_FIELDS = ( "name", "description", "version", "author", "homepage", "repository", "license", "keywords", )

校验逻辑(verify_manifest_identity)以 Claude 清单为参照系,逐字段比对其余清单:任一字段不一致即报错"{label} manifest {field!r} must match Claude"。仓库当前版本(2.6.5)下,三份plugin.json的共享字段确实逐字一致,例如:

  • name:diagram-design
  • version:2.6.5
  • author:{"name": "Cathryn Lavery", "url": ...}
  • license:MIT
  • keywords:["diagrams", "svg", "architecture", "flowchart", "visualization", "editorial", "drawio", "mermaid", "import", "animation", "semantic-patterns"]

Codex 清单是唯一"超出共享面"的:它额外携带skills: "./skills/"interface(displayName、category、capabilities、defaultPrompt、brandColor 等 Codex 生态需要的展示与能力声明)。这正是"最小原生清单"的体现——各宿主只加载自己认识的字段,公共身份字段统一、宿主特有字段按需存在。

4. 同步版本门禁:验证器如何拒绝漂移

ADR 0008 的核心保障是 scripts/verify-plugin-package.py 实现的"包版本门禁"。它以一个 git ref(base ref,如HEAD或发布基线)为参照,执行以下几类检查,任何一项失败都会让main()以非零退出码失败并打印FAIL plugin package

4.1 版本必须同步且必须前进

verify_versions的规则:

  • 三份清单的version必须完全一致,否则报"plugin manifest versions must match: Claude=..., Codex=..., Factory=..."
  • 每个版本必须是严格MAJOR.MINOR.PATCH语义化版本(正则SEMVER全匹配,1.2这类不完整版本会被拒绝);
  • 相对 base ref,只要该清单在基线处存在,当前版本就必须严格大于基线版本,否则报"must increase relative to <base-ref>: 1.2.3 -> 1.2.3"
  • 若 base ref 处不存在任何已同步清单(base_manifest_count == 0),则无法证明包版本前进过,同样判失败。

4.2 身份字段零漂移

verify_manifest_identity以 Claude 为参照,逐字段比对共享身份字段(见第 3 节),防止某一次发布只改了 Claude 清单而漏改其他宿主。

4.3 marketplace 路径安全与一致性

verify_marketplaces对三份 marketplace 文件做严格检查,这部分直接落实 ADR 中"拒绝不安全 marketplace 路径"的要求:

  • 插件源必须是本地相对路径source必须等于././或以./开头;含..、绝对路径、或解析后逃出 marketplace 根目录的路径一律拒绝(resolve_local_pathPath.resolve()+is_relative_to双重确认);
  • Codex 条目有专属契约source必须是{"source": "local", "path": "./"}形态,policy.installation必须为AVAILABLEpolicy.authentication必须为ON_INSTALL,且必须提供非空category
  • Factory 的 source 必须是原生本地路径字符串:测试test-plugin-package.py专门构造了把 Factory source 改成{"source":"local","path":"./"}的用例,验证器会以"must be a non-empty local path string"拒绝——这是因为 Factory 走 Claude 翻译兜底时不需要本地 source,而 ADR 的意图恰恰是让 Factory 拥有原生路径;
  • 三个 marketplace 必须打包同一个插件根:解析出的 claude_root / codex_root / factory_root 必须全部相等,否则报"must package the same plugin root"
  • 打包面必须完整:插件根下必须存在skills/diagram-design/SKILL.md与至少一个commands/*.md,缺任一即失败(对应测试"packaged skill is missing""packaged commands are missing")。

4.4 SKILL.md 元数据版本跟踪

verify_skill_metadata_version是一个容易踩坑的隐性门禁:SKILL.md frontmatter 的metadata.version必须跟踪清单版本的MAJOR.MINOR。例如当前清单为 2.6.5 时,SKILL.md 的 frontmatter 必须是metadata: version: "2.6"。注释中记载了这条规则的历史原因:清单由脚本统一 bump,而 SKILL.md 的 metadata 是手工维护的,曾出现过 2.4 对 2.5.0 清单的静默漂移,直到合流清扫才被发现。为此验证器实现了一个不依赖 YAML 运行时的 frontmatter 解析器(parse_frontmatter_metadata_version),只接受该包使用的映射子集,遇到重复键、未闭合引号、非法转义、越界 Unicode 转义等一律"fail closed"。

5. 同步版本升级:bump-plugin-version.py

既然版本必须同步,就不该靠手工改三份 JSON。scripts/bump-plugin-version.py 把"同步 bump"固化成单一入口:

python3 scripts/bump-plugin-version.py # patch:2.6.5 → 2.6.6 python3 scripts/bump-plugin-version.py --minor # minor:2.6.5 → 2.7.0 python3 scripts/bump-plugin-version.py --major # major:2.6.5 → 3.0.0

其行为要点:

  • 先读取三份清单并断言版本已同步,若发现Claude=2.6.5, Codex=2.6.6这类漂移,直接抛出PackageVersionError("manifest versions are not synchronized")拒绝写入;
  • 在同步前提下按major/minor/patch计算新版本,并一次性写回三份清单,返回新版本号;
  • 成功后输出Updated Claude, Codex, and Factory plugin manifests to <version>

test-plugin-package.pytest_bumper验证了三个档位的 bump 结果(1.2.3→ patch1.2.4/ minor1.3.0/ major2.0.0),并确认漂移清单会被拒之门外。注意:bump 脚本只改版本号,SKILL.md 的metadata.version(MAJOR.MINOR)仍由验证器把关。

6. Bootstrap 特例:新宿主接入的准入条件

ADR 0008 为"某个原生清单首次被纳入跟踪"设计了一个边界情况:一个刚被纳入跟踪的原生清单,在 base ref 处可能并不存在(例如 Factory 清单是本次发布才新增的)。此时"相对 base ref 必须前进"无从谈起,但门禁不能因此放水。ADR 给出的规则是:

仅在 bootstrap 期间,新跟踪的原生清单可以缺席于 base ref;但其当前元数据与版本必须与已确立的清单(established manifests)一致,且这些已确立的清单必须前进。

test-plugin-package.pyinclude_factory=False的夹具精确复现了这一场景:

  • 合法 bootstrap:已有 Claude/Codex 清单(1.2.3 → 1.2.4),新增 Factory 清单且版本同为 1.2.4、字段与 Claude 一致 → 通过(OK: synchronized Factory bootstrap accepted);
  • 无版本前进的 bootstrap:已有清单未 bump,仅新增 Factory 清单 → 报"must increase"失败;
  • 字段漂移的 bootstrap:新增 Factory 清单版本对齐但 description 被改 → 报"must match Claude"失败;
  • 清单被删除:任何已跟踪清单缺失 → 报"could not read"失败。

由此可见,bootstrap 不是"新宿主免检入场券",而是"对齐既有清单 + 既有清单必须按正常节奏 bump"的受控例外。

7. 后果与发布模型

ADR 0008 的 Consequences 部分定义了该决策的长期约束:

  • 每个原生宿主都有显式的安装路径,而行为仍单一来源:安装方式可查、可测、可进门禁,但 Skill 与 commands 只有一份;
  • 新增宿主是一套完整的准入清单:需要原生元数据(native metadata)、包门禁覆盖(package-gate coverage)、文档,以及一次同步版本 bump——永远不构成复制 Skill 或命令面的理由
  • Git 型 Factory 安装由 marketplace 提交驱动更新:Droid 按 commit 而非清单显示版本跟踪插件,因此同步清单版本的角色是"发布元数据 + 评审门禁"(release metadata and a review gate),真正的更新传播靠 marketplace 的新提交。

这与 README 中的安装指令相互印证:Factory Droid 侧执行droid plugin marketplace update diagram-design后再droid plugin update diagram-design@diagram-design --scope user即可拉取合入的更新并开启新会话生效。

8. 各宿主安装与更新实操

结合 README.md 中的安装章节,四个宿主的使用路径如下:

Claude Code(通过 marketplace 添加并安装):

/plugin marketplace add cathrynlavery/diagram-design /plugin install diagram-design@diagram-design

随后在/plugin的 Marketplaces 面板中为 diagram-design 开启Enable auto-update(Claude Code 默认对第三方 marketplace 关闭自动更新),之后会在启动后后台刷新 marketplace 与已装插件,并按提示/reload-plugins或于下一会话加载更新。

Codex

codex plugin marketplace add cathrynlavery/diagram-design codex plugin add diagram-design@diagram-design

Codex 在启动时刷新已配置的 Git marketplace;如需立即拉取,执行codex plugin marketplace upgrade diagram-design并开启新会话。

Factory Droid

droid plugin marketplace add https://github.com/cathrynlavery/diagram-design droid plugin install diagram-design@diagram-design --scope user

Droid 按 commit 跟踪 Git 插件;合入新版本后执行droid plugin marketplace update diagram-design、再droid plugin update diagram-design@diagram-design --scope user,并开启新会话。

Pi:无需 marketplace,直接经由仓库标准skills/包目录发现 skills/diagram-design/ 这份共享 Skill。

迁移提醒(README 中的一次性迁移说明):已通过独立npx skills add安装的副本不会自动跟随 Codex marketplace——需移除独立副本后改用 marketplace 安装;Cowork 个人副本同理需卸载后改从组织 marketplace 安装,此后每次版本 bump 都会沿各客户端的原生更新路径流动。这一迁移路径之所以成立,正是 ADR 0008"单一插件根 + 原生更新路径"设计的直接收益。

9. 门禁的对抗性测试

scripts/test-plugin-package.py 是这套门禁的对抗性回归套件,覆盖了本文提到的几乎每个失败模式:有效同步 bump 通过、缺 bump 拒绝、双清单版本漂移拒绝、Factory 单独漂移拒绝、非严格 semver 拒绝、marketplace 目标缺失拒绝、Factory 非原生 source 拒绝、共享命令面缺失拒绝、bootstrap 合法/非法场景,以及 SKILL.md frontmatter 的一系列畸形输入(重复 metadata.version、正文中的 version 行不算数、未闭合引号、非法转义、\U00110000越界转义等)。任何一条规则被悄悄放宽,都会被这些用例当场戳穿。

10. 小结

ADR 0008 用一句话概括:每个宿主一份最小的原生清单与 marketplace 元数据,所有 marketplace 指向同一个仓库根,行为永远只有一份。它的工程价值体现在三个层面:

  1. 布局上:清单目录(.claude-plugin/.codex-plugin/.factory-plugin/)与共享面(skills/commands/)职责分明,从文件结构即可读出发布策略;
  2. 流程上bump-plugin-version.py保证三份清单版本同步,verify-plugin-package.py保证身份、路径、打包面与 SKILL.md 元数据零漂移,bootstrap 特例为新宿主接入提供了受控通道;
  3. 发布上:Git 型宿主(Factory)按提交更新,同步清单版本退化为发布元数据与评审门禁,真正驱动用户更新的是 marketplace 的新提交。

对于任何"一个 Skill 分发到多个 AI 宿主"的项目,这份 ADR 与其配套脚本、测试构成了一套可以直接借鉴的完整方案:最小原生清单、单一插件根、同步版本门禁、受控 bootstrap。

延伸阅读:仓库中其余 ADR 记录着同一套治理思路的姊妹决策,例如 ADR 0001(静态输出默认 + 唯一受审控的 motion 控制器),它们共同勾勒出 Diagram Design "把行为收敛到单一来源、用脚本与测试封死漂移"的工程哲学。

【免费下载链接】diagram-design38 editorial diagram types for Claude Code, Codex, and Pi. Self-contained HTML + SVG. No shadows. No Mermaid slop.项目地址: https://gitcode.com/GitHub_Trending/di/diagram-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询