006 / 024 · 开发 · v0.17.1 · MIT
ship
ship 把一次改动从工作区送到一个已合并的 PR:开分支、跑项目自己的测试、提交、开 PR、等 CI、合并,最后清理 worktree。哪一步跑了、哪一步跳过、为什么,都写在报告里。
- 参数
都是可选的。
[branch-name][--dry-run][--no-test][--draft][--base=<branch>][--bump=<version|major|minor|patch>][--merge-strategy=<squash|merge|rebase>]
- 什么时候触发
这些是 CI 用来评测这个技能的 prompt,每次发布都会跑。
会触发
- Ship it.
- 开个 PR 合了
- Get this merged once CI is green.
不触发
- Tag a release and publish the package.
- Just pull the latest main.
- 邮件写好了,帮我发出去
- 这样发出去会不会太冲,帮我改客气点
预检与执行计划
第 0 步是预检。它先读仓库的状态——当前在哪个分支、工作树干不干净、有没有未推送的提交、这个分支是不是已经有一个开着的 PR——然后打印一份执行计划,把后面每一步标成 RUN 或 SKIP,之后严格照这份计划走,不执行标了 SKIP 的步骤,也不临时增加计划里没有的步骤。--dry-run 只打印计划就停。
这样做是因为「发布」在不同状态下意味着不同的事:在基线分支上有未提交的改动、在功能分支上已经有 PR、树是干净的但本地基线有远端没有的提交,这是三条不同的路径。把状态先摊开,后面每一步做什么就不需要再猜。
如果在基线分支上、工作树干净、没有未推送的提交、也没有开着的 PR,它在第 0 步就退出,报告「没有东西可发」。有一种情况看起来像这样但不是:有人直接往 main 上提交过,本地基线因此带着远端没有的提交。这时它会开一个分支把这些提交带走,而不是报告无事可做。
测试、lint 和版本号从哪里来
测试命令、lint 命令和版本号工具,都是从项目自己声明的地方读出来的——package.json 里的 script、justfile 的 target、Makefile 的规则,也包括 CI 配置。按顺序匹配,取第一个命中的;一个都没有的项目就跳过这一步,并在报告里写明是跳过而不是通过。测试失败时它最多尝试修两次,然后停下来问你。
版本号是例外:即使检测到了项目的 bump 工具,不传 --bump 它也不会动。一次改动算不算一个发布,是作者的判断,不是 diff 的属性。检测到而没有执行,报告里会写成 skipped。
提交前的凭据扫描
暂存之前有一道扫描,检查的是改动的内容而不是文件名,并且覆盖未跟踪的文件。未跟踪文件必须单独查,因为 git diff 不会提到它们,而一个刚建出来、装着真 token 的 config.local.json 恰好就属于这一类。
命中之后它停下来问,不自己下结论:fixture 和文档里合法地存在长得像凭据的字符串,所以它给出的是文件、行号和一个问题。
暂存按显式路径进行,不用 git add -A。拿不准的未跟踪文件会被收集成一次提问,一个来回问完。
等 CI
「没有检查报告」在相隔几分钟的两种情况下含义不同:刚开几秒的 PR 显示没有检查,是因为 GitHub 还没登记工作流;而一个根本没配 CI 的仓库看起来一模一样。按后者理解就合并,等于绕过了项目写的全部检查。
所以预检那一步已经先确认了仓库有没有声明工作流。声明了就最多重试一分钟再下结论,始终没登记的话报告里写 CI skipped 并附上声明了几个工作流;没声明就直接跳过这一步。
合并状态里需要人判断的几种——缺必需的 review、非必需检查失败、和基线有冲突——都会停下来问。冲突不会被自动解决。
它不做的事
它不打发布标签、不发包、不部署,这些是合并之后的另一件事。它不会只写提交信息而不推送。它不 force-push 基线分支,不替你解决合并冲突,也不删除本次运行所在的那个 worktree——那个会报告为保留,因为在里面执行不了这个删除。
开发 里的其它技能
还有 6 个 · 一次安装sync
/dev:syncsync 只做一件事:把本地和远端的差异摊开给你看,并且只执行其中绝对安全的那部分。
cleanup
/dev:cleanup一次改动发布之后,残留会留下来:本地分支、它在远端的副本、当时用的 worktree,有时还有一个只装着被忽略文件的目录。
retitle
/dev:retitle对话列表乱,不是因为名字不好听,是因为名字里没有可排序的信息。
steward
/dev:stewardsync、ship、cleanup 都只管你当下所在的那一个仓库。
reunite
/dev:reunitesessions 解决的是这一幕:你用另一个账号登录,侧边栏突然只剩下寥寥几条。
handoff
/dev:handoffhandoff 让同一台电脑上的 Claude Code 和 Codex 互相看到可读的会话历史。