mcpField
S
ship-a-feature

ship-a-feature

github.com/zernie开发1578 次安装2 天前更新
代码仓库

关于 ship-a-feature

面向使用 `CLAUDE.md` 和 npm 脚本的项目的功能交付 Skill,检查功能是否可访问、已记录且易于查找,并验证稳定性标记及冻结代码树上的门禁。

主要功能

  1. 01检查导出入口是否可访问
  2. 02发现未记录的 API 变更
  3. 03验证文档与可发现性
  4. 04检查稳定性标记
  5. 05在冻结代码树上运行门禁

适用场景

  1. 01验证新导出的功能
  2. 02发现缺失的公开文档
  3. 03检查功能是否链接自相关文档
  4. 04合并前运行项目检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zernie/vigiles --skill ship-a-feature

安装后在 Claude Code 中输入/ship-a-feature确认已加载。

附带文件scripts/ship-check.mjsship-a-feature.harness.mjs

详细介绍

发布一个功能(确保它真正发布,而不是只完成一半)

九行的完成定义已存在于 CLAUDE.md 的 cohesive-feature-delivery 中。2026-09-02,equivalentDisasters 经过测试、门禁检查并提交后发布,却在无人察觉的情况下未满足其中 四 行:所有公开入口都无法访问、邻近代码的 JSDoc 被悄悄窃取、没有文档、没有稳定性标记。写在一份 2000 行文件中的说明不会触发检查,下面这些会。以下每项检查都对应着它旨在防范的失败;绝非纸上谈兵。

1. 编写代码前——只有你能做出的三个决定

决定标准
选哪个入口?(package.json exports 子路径)免费且确定性强 → .(src/test.ts)。会消耗模型费用 → ./eval(paid_ 前缀)。Hook 词汇 → ./hook。未从某个入口的 barrel 重新导出的符号,对于 require("vigiles") 来说就不存在(失败 1)。
稳定还是 experimental_?STABILITY.md:已导出但不稳定 ⇒ 添加 @experimental 标签,并使用 experimental_ 名称。只有一天历史、只有一个使用方,并不意味着稳定。ESLint local/experimental-name 会检查标签与名称是否匹配;它无法写入标签(失败 4)。
公开的原因说明放在哪里?(document-the-why)在与相关声明相邻的 docs/*.md 小节中——对于 equivalentDisasters,应放在 docs/compiled-hooks.md 中“blocks 7/7”旁边。不放在 README(为保持简洁),也不放在本文(面向贡献者)。

请将答案写入 PR 正文。在你说明稳定性之前,下面的脚本不会通过稳定性检查。

2. 写完之后——一条命令,五项检查

npm run build
node .claude/skills/ship-a-feature/scripts/ship-check.mjs <symbol> [--stable "<why it is stable>"]
检查项执行内容针对的已验证故障
REACHABLE对构建后的 dist/ 中每个 exports 子路径执行 import();该符号必须是至少 1 个对象的键src/guardrail-check.ts 中有 equivalentDisasters,src/test.ts 中却没有 → undefined(1)
SURFACE重新生成 api-surface/(api:report),然后读取 git diff:任何 +… (undocumented) 都会失败插在 verifyGuardrail 的 JSDoc 和声明之间;diff 已经说明了这一点,但没人看(2)
DOCUMENTED\b<symbol>\b 必须出现在 docs/**/*.md 或 README.md 中直到有人提出要求,才有公开的说明页面(3)
FINDABLE必须有两个页面提及它——或者一个页面提及它,另一个页面链接到该章节的锚点只记录在一处,而且是在介绍另一项功能的指南中,没有任何入站链接(5)
MARKED声明的 JSDoc 中有 @experimental,或名称带有前缀,或你传入了 --stable "<why>"导出仅一天,没有标签,也没有前缀;lint 规则无从判断(4)

DOCUMENTED 和 FINDABLE 是两个不同的问题。 前者问的是某处是否记录了这项功能。后者问的是,不知道该打开哪个文件的读者能否找到它。experimental_equivalentDisasters(后来更名为 experimental_alternateSpellings)通过了第一项,却没通过第二项:它只在介绍另一项功能的指南中、以自己的标题解释了一次,而且没有任何链接指向那里——因此,读者查看测试指南、想知道“我的 guard 是否真的会拦截?”时,完全找不到它。添加指向它的链接即可满足 FINDABLE;不必重复解释。

✗ 表示需要修复并重新运行;脚本会在每项发现旁打印修复方法。✓ MARKED stable 行会附上你的理由——将其粘贴到 PR 正文中。

3. 门禁——在冻结的代码树上,于前台运行,并使用实际退出码

node .claude/skills/ship-a-feature/scripts/ship-check.mjs --gate     # = npm run check, frozen

运行前后会对代码树进行哈希校验;如果门禁运行期间有任何更改,就会失败。以下是已验证的三种门禁误报情况,按这种方式运行即可杜绝:

  • 运行期间编辑(5):npm run check 对只写了一半的文件执行 lint,并因某种中间状态下的错误而失败。退出前不要触碰代码树。
  • 使用 until ! pgrep -f 'check.mjs' 等待(6):等待进程的 shell 命令行本身就匹配该模式,因此永远不会结束。等了二十分钟,显示的仍是“仍在运行”。在前台运行门禁;如果必须在后台运行,就对自己启动的 PID 执行 wait。绝不要对自己的命令中包含的字符串使用 pgrep -f。
  • npx vitest run … | tail -8(7):管道的状态码是 tail 的状态码。三个测试失败,退出码却是 0。门禁之后不要接管道——重定向到文件,再查看文件。

已有机制覆盖——调用即可,不要重述

属性负责人(不要重复)
标签 @experimental ⇒ experimental_ 名称ESLint local/experimental-name(在 npm run lint 中)
./eval 符号带有 paid_npm run exports:check(在 npm run check 中)
与已提交快照相比的表面差异npm run api:check(在 npm run check 中)
文档的 ts 代码块导入真实导出npm run docs:check — 仅在文档实际存在时(3)
docs/ 中提及的每个 CLI 动词src/doc-command-coverage.ts — 仅检查动词,不检查 API 符号
完整命令列表npm run check(node scripts/check.mjs --list 会打印)

4. 完成的定义

五行 ✓、一行 ✓ GATE,以及注明入口 · 稳定性决策 · 文档章节的 PR 正文。没人能 import 的功能就不算功能,没人能导航到的功能就不算有文档记录。

评论

支持 Markdown