关于 send-it
send-it 是用于将未提交改动整理并推进到 Pull Request 的发布流程编排 skill,面向使用 GitHub 和相关协作流程的开发团队。它把提交、lint、changelog、PR 和后续分流交给配套 skills,并负责分支检查、发布类型判断和推送。
主要功能
- 01检查分支并解析工作树
- 02编排原子化 Conventional Commits
- 03运行按改动触发的 lint
- 04生成并校验日期化 changelog
- 05推送分支并创建或更新 PR
- 06将后续 CI 与评审分流交给 skill
适用场景
- 01将分支上的未提交改动整理提交
- 02为改动运行 lint 并记录 changelog
- 03创建或更新 GitHub Pull Request
- 04将关联 Linear issue 转为 In Review
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheged-studio/ agent-skills --skill send-it安装后在 Claude Code 中输入/send-it确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config.example.jsonpackage.jsonREADME.mdscripts/check-skill-bumps.mjsderive-bump.mjsscripts/lib/git.mjs详细介绍
send-it
将未提交的工作打包为原子提交(通过
commit 技能),运行变更门控 lint
preflight,编写或更新带日期的
changelog/<ts>-<slug>.md 条目(通过 changelog
技能),拟定 Conventional Commits PR 标题(供 CI 和人工使用;在双重合并策略下,功能 PR 会以合并提交的形式落地,release-please 会根据已落地的提交主题确定版本升级级别 — R-1176 / R-824),推送分支,针对基分支创建或更新拉取请求,并将任何关联的 Linear 问题转为 In Review(通过 linear-sync 技能)。
此技能是 发布流程的唯一事实来源。它是一个轻量级编排器:只负责其他同级技能不处理的衔接工作 — 分支保护、工作树解析、发布类型决策(按类别)、PR 标题拟定、推送和 PR — 其余工作委托给其他技能:
- 提交 →
commit技能(分类范围内与范围外的更改、原子 Conventional Commits、范围外更改保护)。 - Lint 门控 →
preflight技能(按变更门控;没有与 lint 相关的更改时不执行操作)。 - 变更日志 →
changelog技能(编写/更新并验证;每个 PR 都必须有一个条目,只有在config.json设置changelog: false时才完全跳过)。 - PR 正文 →
pr技能(Summary 视觉展示、Changes、Evidence、包含发布说明的 Merge Danger、Related Issues,以及手动编辑的保留区域)。 - Linear In Review →
linear-sync技能(按团队名称解析状态,以幂等方式转换状态)。 - PR 提交后的分流 →
triage-pr技能(Phase A CI 修复循环和转为已验证通过后的升级,然后是 Phase B 审查处理 — 当humanEnvelope为true时,以其人工处理说明结束;为false时,以 Step 13 报告结束 — Step 11,R-1151)。
委托的技能会自动检测各自的适用范围,因此单包仓库中,单体仓库功能(每个工作区分别运行 ESLint、变更日志 affected_packages)会正常跳过。send-it 不会配置这些功能。
请将委托的技能与
send-it一同安装。 此捆绑会通过相对路径调用并链接同级技能(../commit/SKILL.md、../preflight/SKILL.md、../changelog/SKILL.md、../pr/SKILL.md、../linear-sync/SKILL.md、../triage-pr/SKILL.md),因此仅安装--skill send-it会导致提交、lint、变更日志、PR 正文、Linear 和分流步骤不可用,且这些链接会失效。请将它们一同安装:npx skills add https://github.com/rheged-studio/agent-skills \ --skill send-it --skill commit --skill preflight --skill changelog --skill linear-sync \ --skill pr --skill triage-pr \ --agent claude-code --agent cursor --copy
此流程有意不运行类型检查、测试或格式检查 — 这些由 CI 处理。它运行的唯一门控是按变更门控的 preflight lint。
完成标准。 创建或更新 PR(Step 9)以及将 Linear 问题转为 In Review(Step 10)都处于流程中段 — 并非
/send-it的终点。只有当 Step 11 的triage-pr达到终止结果 — 其 Step 13 报告、人工处理说明(humanEnvelope: true),或有记录的停止原因(Phase-A 提前停止、硬阻碍、预算耗尽)— 或打印出明确说明原因的跳过/降级行(--skip-triage/triage: false时为ℹ️ triage chain skipped …,或同级技能缺失时为⚠️ triage-pr not installed …)后,此次运行才算完成。如果未出现上述任一行,而是以报告草稿 PR URL 作为最终结果,则此次运行失败。
配置
此文件旁边的 config.json 中有一些可调参数;请修改你复制的 config.json,使其适用于使用此配置的仓库(随附了中性的 config.example.json 作为模板):
| 键 | 含义 | 默认值 |
|---|---|---|
baseBranch | 分支差异所依据的主干(origin/<baseBranch>),也是 PR 的目标分支。 | "main" |
shippablePaths (仅供参考) | 构成已发布内容的路径前缀——为审核者提供的文档提示,不作为发布决策依据(R-598;参见步骤 6)。发布类型由更改的语义类别决定,因此这些路径不再决定标题。保留此项用于可选的发布内容范围交叉检查说明。 | ["skills/"] |
shippableManifestKeys (仅供参考) | 构成已发布 files 内容范围的 package.json 键——作用与 shippablePaths 一样,仅供参考,不再作为发布门槛。 | ["name", "version", "files", "publishConfig"] |
changelog (可选) | 是否撰写带日期的 changelog/ 条目(步骤 7–8)。对于没有变更日志流程的仓库,请设为 false——此类仓库没有 changelog/ 目录,也未安装 changelog 技能(例如没有发布流水线的 private 仓库)。设为 false 时,send-it 会完全跳过变更日志撰写,类别判定仍仅用于确定 PR 标题。只要安装了 changelog 技能,就省略此项(或设为 true)。 | true |
bundleVersioning (可选) | 为发布多个独立版本技能包的仓库启用每个技能包的版本升级检查(步骤 6)。这是一个 { root, manifest, skillFile } 对象,用于指定技能包父目录,以及每个技能包所包含的清单和技能清单文件名。单包仓库请完全省略此项——此时检查不会执行。 | 未设置(已禁用) |
triage (默认省略或设为 true) | PR 打开后,是否将本次运行接入 triage-pr 技能(步骤 11)——CI 修复循环、已验证绿灯后的提升切换,然后通过 triage-pr 的人工流程(humanEnvelope: true)或其步骤 13 报告(false)执行 Phase B。此键可以省略(默认为 true);默认运行中,此步骤不可选。仅当仓库有意在 PR 打开后停止,或未安装 triage-pr 时,才设为 false。单次运行可使用 --skip-triage 达到相同效果(R-1151)——请务必说明原因。 | true |
团队名称、问题 ID 前缀和工作区标识符不在此处配置——
它们位于 linear-sync 和 changelog 技能各自的 config.json 文件中,
由委派的步骤读取。
变更日志范围(原为
changelogScope)。 send-it 会为每个 PR 撰写带日期的条目——采用“全部记录,之后筛选”的模式。发布说明是在发布时筛选出带版本标记的(会触发发布的)条目生成,而不是在撰写时设置门槛。changelogScope参数(在 0.4.0 中添加)已移除(R-600);仅保留changelog: true|false主开关。
前置条件
- 已安装并完成身份验证的
ghCLI(gh auth status)。 - 已安装同级技能(
commit、preflight、changelog、pr)。 如果未安装pr,步骤 9 会改为写入最简备用正文。 linear-sync——可选;如果未安装该技能(或 Linear MCP 服务),则会静默跳过 In Review 回写(步骤 10)。triage-pr——默认流水线必需。 如果未安装该技能,步骤 11 的调用链会发出警告,并在 PR 打开后结束——这种软跳过属于降级结果,不算成功完成。这两个同级技能的行为刻意不同:跳过 Linear 回写不会改变 PR 的任何内容,而跳过 triage 调用链则会留下未完成的工作。
流程
步骤 0:工作树解析(仅当设置了 --worktree= 时)
如果传入了 --worktree=<branch-or-path>,请在运行任何其他步骤前解析并 cd 到该工作树。
否则跳过此步骤。
-
运行
git worktree list --porcelain,列出工作树及其路径和分支。 -
解析参数:
- 绝对路径(以
/开头):与worktree <path>字段匹配。 - 否则:将其视为分支名称,与
branch refs/heads/<name>字段匹配。
- 绝对路径(以
-
无匹配项 — 立即退出,并显示:
No worktree found for <arg>. Available: <comma-separated paths>。 -
匹配项 —
cd到解析出的工作树路径。cwd会在整个工作流中保持不变,因此后续所有git和gh调用都会在该工作树中运行。 -
确保依赖项已安装。 新创建的工作树没有
node_modules。如果该目录不存在,请立即运行pnpm install --frozen-lockfile,并确保在任何调用打包脚本或验证器的步骤之前完成,以便--worktree能够独立运行:[ -d node_modules ] || pnpm install --frozen-lockfile -
继续步骤 1。
省略 --worktree 时,此步骤不执行 — 不带参数的 send-it 会继续像之前一样,从当前会话所在目录运行。
步骤 1:分支检查
- 获取当前分支:
git branch --show-current。 - 如果当前位于基准分支(
config.json中的baseBranch;默认为main):- 运行
git status --porcelain。如果工作区干净,则退出并显示:"Nothing to ship from the base branch. Create a feature branch first." - 如果有未提交的更改:
- 检查差异(
git diff和git diff --cached)以及已更改的文件路径。 - 提炼一个简短的 kebab-case slug 来概括更改(约 3 个单词、小写、最多约 40 个字符)。示例:
add-readme-section、fix-config-typo。 - 按以下顺序确定分支名称:
--branch=<name>— 按原样使用。--issue=<ID>— 使用<ID>-<slug>并转换为小写(例如r-7-as-acquired),与 Linear 的gitBranchName保持一致。- 否则 — 仅使用
<slug>(不加wip/前缀)。
- 如果所选分支已存在于本地或
origin,则依次追加-2、-3……,直到名称未被占用。 - 运行
git checkout -b <branch>,将工作树切换到该分支。 - 告知用户:"之前位于基准分支且有未提交的更改;已创建
<branch>,将继续执行。"
- 检查差异(
- 在新分支上继续工作流的其余步骤。
- 运行
- **如果当前位于功能分支:**继续。
步骤 2:如果 package.json 有变动,则刷新锁文件
如果当前分支上没有修改 package.json,则跳过此步骤。
-
运行
git diff --name-only origin/<base>...HEAD | grep -E '(^|/)package\.json$'。如果 输出为空,则跳过。 -
运行
pnpm install --frozen-lockfile。如果成功,说明锁文件已同步 — 继续。 -
如果失败,则运行
pnpm install更新锁文件。 -
如果锁文件有变动,请先暂存并提交,然后再进行其他提交:
git add pnpm-lock.yaml git commit -m "chore: update lockfile"
这样可确保 CI 的 --frozen-lockfile 安装成功。(在不使用 pnpm 的仓库中静默跳过。)
第 3 步:提交未提交的更改 — 委派给 commit 技能
send-it 是一站式发布流水线:任何未提交的内容都应在开始变更日志/PR 工作之前提交——但只能提交属于_当前_分支的内容。
按照 commit 技能执行:以合并基点(git merge-base HEAD origin/<base>)为依据,将未提交文件分类为范围内与范围外,展示暂存计划并标出任何范围外文件(绝不使用 git add -A;其他分支/工作树中的杂散文件绝不能被悄悄暂存),并创建逻辑清晰、原子化的 Conventional Commits(类型 + 可选范围 +
英式英语正文;对于破坏性更改,使用 ! / BREAKING CHANGE:;标题须遵循该技能规定的 ≤72 字符限制,包括 send-it 后续自己的 git commit -m 行)。如果工作区干净,则跳过此步骤。要求 commit 技能根据本次 send-it 运行解析出的基准分支进行分类——<base> 是 baseBranch(来自 config.json),或传入的 --base——而不是 commit 技能自身 config.json 中的 baseBranch,后者在 --base 运行(堆叠 PR 场景)时会有所不同。范围分类和范围外保护必须基于 send-it 实际发布所针对的同一个基准分支,否则堆叠 PR 会导致文件分类错误。
此步骤写入的 Conventional Commit 类型是第 6 步发布决策的输入(derive-bump.mjs 会从提交中读取这些类型),因此必须使用准确的类型和 ! / BREAKING CHANGE: 标记。
此委派仅涵盖未提交工作的_初始_提交。send-it 后续自己的定向提交仍由此处处理:锁文件刷新(第 2 步)、可选的 bundle 版本递增(第 6 步)和变更日志条目(第 8 步)。
第 4 步:获取基准分支并确认有内容可发布
git fetch origin <base>
如果 git log origin/<base>..HEAD 结果为空,则退出并显示:“没有领先基准分支的提交。没有内容可发布。”
步骤 5:Lint 门禁 — 委托给 preflight 技能
--skip-preflight会跳过整个步骤。打印清晰的⚠️ lint gate bypassed (--skip-preflight)警告,然后跳转到步骤 6。仅在门禁误触发时使用; CI 仍会运行仓库的实际 lint 检查。
按照 preflight
技能运行按变更门控的 lint 预检:
node skills/preflight/scripts/preflight.mjs
根据其退出码约定采取行动;读取 .preflight-summary.json 来解读非零退出码:
- 退出码 0 — 通过。 没有引入违规;继续。
- 退出码 1,且
violations.introducedCount > 0— 引入了违规 (阻塞)。 运行node skills/preflight/scripts/lint-fix.mjs,重新运行预检, 并重复以上操作,直到引入的违规清除。继续之前,提交修复(使用style:/fix:提交;如果步骤 3 的相关提交尚未推送,也可以将修复合并进去)。 - 退出码 1,且
introducedCount == 0,并且results.failedLinters非空 — 某个 linter 无法运行(缺少其二进制文件),并非实际违规。 对于不使用该工具链的仓库,这种情况 是预期的(例如未安装 ESLint 或 markdownlint 的文档/技能仓库)。将其视为跳过,而非阻塞: 警告<linter>不可用,然后继续。仓库自身的 CI 负责运行实际使用的 lint 检查。 - 退出码 2 — 仅有既存违规。 这些违规并非由当前分支引入,不应阻止发布。说明这些违规并继续 (可以选择按照 preflight 技能的说明创建债务问题)。
- 任意退出码,且
lintSurface.count > 0— 分支上存在 lint 配置范围变更。 该分支修改了 lint 配置文件,或添加了忽略/禁用指令(参见 preflight 技能中的 lint 配置范围规则)。 这永远不会阻塞,也不会改变退出码。不要还原这些变更,也绝不要在上述循环中添加此类指令来清除违规。 将清单(configFiles,以及每条指令的file:line)加入 PR 正文的 Evidence 部分和最终报告开头添加的行, 作为待确认的开发者决策。
预检采用按变更门控:它只检查分支触及的类别,因此在没有 lint 相关变更时会直接跳过。
仅当未安装 preflight 时,才完全跳过此步骤。
步骤 6:按类别确定发布类型,并编写 Conventional Commits PR 标题
版本管理由 release-please 读取 Conventional Commits 来驱动。该项目采用双重合并策略(R-1176 / ADR-0005):
- 功能/发布 PR 以合并提交的方式合入。合并后,release-please 会根据主干上已合入的 提交主题来确定版本提升级别(R-824),而不仅仅看 PR 标题。
- release-please 版本 PR 和 扇出 PR 继续使用 squash(orchestrator / fanout-spine)。在这些路径中,squash 提交主题仍是版本提升声明。
allow_merge_commit和allow_squash_merge均保持启用(R-1177)——不会禁用 squash。
send-it 仍会编写正确的 Conventional Commits PR 标题(供 CI 的 PR 标题 lint 和人工检查;
变更日志完整性门禁仍依据可触发发布的标题),并写入带日期的变更日志条目(每个 PR 都会写入,见步骤 7)。
它不会提升版本号、写入任何 CHANGELOG.md,也不会打标签。
发布类型由变更的语义类别——即 send-it 自身所提交工作的 Conventional Commit 类型——决定,
而不是由 diff 涉及哪些路径决定(R-598)。仅修改文档的编辑属于 docs:(不发布),即使它位于
skills/ 这类已发布路径下;feat: 无论文件位于何处,都会触发发布。(早期版本依据
shippablePaths 判断,因此会把已发布路径内的文档编辑错误标为 feat:/fix:,并触发不必要的发布。)
-
从分支提交中推导 slug、正文、类型和类别,使用随附的辅助工具(零依赖——无需 tsx):
node skills/send-it/scripts/derive-bump.mjs它会输出 JSON:
{ "slug", "bump", "body", "type", "breaking", "category", "releaseTriggering" }:type— 所有分支提交中的主要 Conventional Commit 类型 (feat/fix/perf/docs/refactor/chore/ci/… — R-387);它是 PR 标题前缀。扫描时会排除合并提交(git log --no-merges)。breaking— 如果任何提交带有!或BREAKING CHANGE:页脚,则为true。category— 日期变更日志的category枚举值(feat→feature、fix→fix、perf→perf、docs→docs、refactor→refactor,其他所有类型 →chore)。releaseTriggering— 当且仅当breaking为真或type ∈ {feat, fix, perf}时为true。这是 发布决策:true会进行发布,false不会。bump—major/minor/patch,当releaseTriggering为真时表示发布幅度 (BREAKING CHANGE:/!→ major;主要类型为feat:→ minor;否则为 patch)。当releaseTriggering为false时忽略。
-
(建议)发布范围交叉检查。
config.json中的shippablePaths/shippableManifestKeys是对已发布范围的文档提示——它们不再决定发布类型。可以选择 据此检查类别是否合理:如果releaseTriggering为true,但差异(git diff --name-only origin/<base>...HEAD)未涉及任何shippablePaths前缀 (也未涉及package.json中的shippableManifestKeys键),请将此情况加入发布说明 (如下),以便审阅者确认是否确实有意发布;同样,如果变更涉及已发布路径,但releaseTriggering: false,也请注明。这只是软性提示;绝不要让它 覆盖类别决策或阻止发布。 -
检查每个 bundle 的版本升级——仅当
config.json设置了bundleVersioning时执行(适用于多产物仓库;未设置时完全跳过此步骤)。每个技能 bundle 都在其package.json和SKILL.md metadata.version中带有自己的版本,由人工升级,且与仓库发布 解耦。CI 会强制要求两者一致,但不会强制要求在 bundle 内容变更时升级版本——因此,已编辑的 bundle 可能会以过时的版本 标签发布。请补上这个缺口:node skills/send-it/scripts/check-skill-bumps.mjs它会输出
{ "configured", "unbumped": [{ name, currentVersion, suggestedBump, suggestedVersion, manifestPath, skillPath }], "bumped" }。对于每个unbumped条目,展示升级建议,并在确认后应用:skills/<name>已变更,但版本仍为<currentVersion>。建议的 升级:<suggestedBump>→<suggestedVersion>(与 PR 标题中的升级级别一致)。 应用吗?(yes / no / patch / minor / major)选择
yes(或明确指定级别)后,将manifestPath(version)和skillPath(metadata.version)都编辑为所选版本——保持两者同步,以确保 CI 检查的 一致性不变——然后仅暂存并提交这两个文件:git commit -m "chore(<name>): release <name>@<version>"。选择no时,保持不变并继续。在--dry-run下,仅打印建议,不做任何编辑。该提交主题必须不超过 72 个字符(首行中的类型、范围、冒号、空格及其余内容)。 -
撰写 PR 标题,使用单行 Conventional Commits 主题 — CI 的 PR 标题检查和变更日志完整性门禁仍然要求提供标题。对于功能 PR(合并提交),合并后的版本号更新依据已合入的提交主题(R-824);标题仍是面向人和 CI 的声明,应与主要类型一致。对于压缩合并路径(发布 + 扇出),压缩提交主题仍是版本号更新声明。如果传入了
--title,则原样使用(仍需运行上面的derive-bump以获取变更日志的category,并在提供的类型与推导出的type/releaseTriggering矛盾,或提供的标题超过 72 个字符时发出警告 — 不要阻止操作)。 否则,直接根据推导出的字段构建标题:- 前缀 =
type(明显时添加作用域,例如feat(<scope>):),若为breaking,则加上!— 因此格式为feat: <body>、fix: <body>、perf: <body>、docs: <body>、refactor: <body>、chore: <body>、feat!: <body>等。 - 触发发布(
releaseTriggering: true)→ 前缀已经是会触发发布的类型(feat/fix/perf,或任何带!的类型)。添加作用域即可。 - 不触发发布(
releaseTriggering: false)→ 前缀是不触发发布的类型(docs/refactor/chore/ci/build/test/style)。 - 标题长度。 整个标题 —
type、可选的(scope)、可选的!、:,然后是主题 — 必须不超过 72 个字符(Git 通常的主题长度上限;与commit技能的限制相同)。如果type+ 可选作用域 +body超出限制,则改写至符合要求:优先删除可选作用域,而不是损失含义。不要在单词中间用省略号截断,也绝不要删除类型前缀。
⚠️ 确保标题与提交内容相符。 错误的前缀会误导审阅者和完整性门禁 — 例如,纯文档分支使用
feat:,或实际修复使用chore:。对于功能 PR,合并后的版本号更新依据已合入的提交主题;对于压缩合并路径,标题_就是_声明。根据更改的语义类别(提交类型)推导标题,以确保两者一致。 - 前缀 =
-
撰写发布说明,供步骤 9 填入 PR 正文的
**Release:**行:当releaseTriggering为false时,使用no release (<type>-only)(以便审阅者确认非发布类型是有意为之);当它为true时,使用<type> → <bump>— 两种情况下都要在后面附上第 2 项中的任何发布表面交叉检查说明。
步骤 7:编写或更新带日期的变更日志条目——委派给 changelog 技能
完全禁用? 如果
config.json将changelog设为false,则完全跳过步骤 7 和 8——不编写任何内容、不运行任何changelog脚本、不创建docs(changelog)提交——并在运行摘要中注明“变更日志步骤已禁用(此仓库中没有变更日志流程)”。此设置适用于没有changelog/目录且未安装changelog技能的仓库;步骤 6 中的类别决定仍用于 PR 标题。如果未设置changelog或设为true,则始终编写条目(changelogScope开关已移除——R-600)。每个 PR 都要有条目。 send-it 会为每个 PR 编写一条带日期的
changelog/条目,无论是否会触发发布——采用“全部记录,之后筛选”的模式。带日期的变更日志是已合并工作的完整记录;发布说明会在发布时筛选带有版本标记的条目(会触发发布的条目),因此非发布条目只是不带version。只有changelog: false会阻止编写条目。
按照 changelog 技能编写或更新条目:
-
根据
branchfrontmatter 字段检测此分支是否已有条目 → 决定更新还是新建。更新时保留文件名和created_at。 -
编写或刷新
changelog/<YYYYMMDD-HHMMSS>-<slug>.md(<slug>来自步骤 6),并根据分支信息填写title/release_note/issues。category和breaking直接采用derive-bump的输出(步骤 6):category是其category字段(feature/fix/perf/docs/refactor/chore——变更日志枚举值),breaking是其breaking标记。对于非发布条目(releaseTriggering: false),若没有面向用户的影响,release_note可以留空。将合并后的字段(
merged_at、commit、pr、stats)以及version留作空白占位符——合并后填充脚本会填写这些字段(非发布条目会将version留空,因为不会为其发布版本)。其中包括pr:此处没有任何步骤会在 PR 创建后将其写回;合并后填充脚本会根据条目中的branch:解析它。 -
运行填充脚本:先运行
node skills/changelog/scripts/set-affected-packages.mjs,再运行node skills/changelog/scripts/add-links.mjs。 -
**验证:**运行
node skills/changelog/scripts/validate-changelog.mjs。必须验证通过后才能提交——如果验证失败,报告错误并中止;不要自动修复。
步骤 8:提交变更日志条目并推送
从此处开始,
--dry-run不会写入任何内容。步骤 8–11 是运行中会修改内容的部分。使用--dry-run时,打印每项操作将执行的内容,但一项也不执行:不运行git commit、git push、gh pr create/gh pr edit,不执行 Linear 状态转换(将--dry-run传递给linear-sync,确保绝不调用save_issue),并对步骤 11 的交接操作使用--dry-run。试运行可以读取——例如运行gh pr view、预览 triage-pr——但绝不会写入。然后以 0 退出。
如果在步骤 7 中写入了 changelog/ 条目(即 changelog 不为 false),则只提交该文件:
git add changelog/<YYYYMMDD-HHMMSS>-<slug>.md
git commit -m "docs(changelog): <one-line summary>"
<one-line summary> 必须确保完整标题(类型、范围、冒号、空格和摘要)不超过 72 个字符——如果超出,请重写摘要。不要将标题换行至第二行。
然后推送分支:
git push -u origin <branch>
第 9 步:创建或更新 PR
<title> 是第 6 步中的 Conventional Commits PR 标题——创建和更新时都要设置该标题(每次运行都重新推导,确保与分支上的提交保持同步)。功能 PR 旨在通过合并提交合并;发布和扇出自动化仍在此技能之外使用 squash。
- 检查是否已有 PR:
gh pr view --json number,url 2>/dev/null。 - 两个值都按字面量处理。将完整正文(如下)写入临时文件
<body-file>,并通过--body-file传入——保留区域可能包含反引号或$(...)。将标题赋值给单引号括起的变量(每个内嵌的'写作'\''),例如title='feat: add $(x) support',并传入"$title",这样提交主题或--title值中的命令替换就不会执行。 - 如果要创建:
gh pr create --base <base> --draft --title "$title" --body-file <body-file>。如果用户传入了--ready,则使用--ready(该标志)代替--draft。 - 如果要更新:
gh pr edit <number> --title "$title" --body-file <body-file>。 - 通过
gh pr view --json url,number返回 PR URL 和编号。
send-it 绝不会启用自动合并。 它会打开并更新 PR;合并仍由人工操作(R-1151)。旧的
--merge-when-ready标志——它会在此处启用gh pr merge --auto --merge——自 0.8.0 起已移除:从第 11 步开始,运行可能停留在 triage-pr 的处置封套上,而已启用的自动合并可能会在计划仍待审批时合并分支。请手动合并,或者在你确认 PR 没问题后自行启用自动合并。
PR 正文——遵循 pr 技能(R-2429)。 该技能规定正文结构:Summary visual、Changes、Evidence、Merge Danger、Related Issues,以及 `` 区域。请在此技能包旁边(../pr/SKILL.md)或使用方的技能镜像中(.claude/skills/pr/SKILL.md、.agents/skills/pr/SKILL.md)查找,并遵循其说明编写正文。提供以下内容:
-
Base——解析出的
<base>(遵循--base),确保正文在堆叠式 PR 中描述正确的范围。 -
Release note——第 6 步的说明(
no release (<type>-only)或<type> → <bump>,以及任何发布面交叉核对结果),用于填写**Release:**行。 -
Related issues——分支名称和提交中的问题标识符,用于
## Related Issues(没有问题时省略)。 -
Evidence——仅包含本次运行实际执行的内容(第 5 步的预检结果,包括任何
lintSurface条目,以及本次会话中运行的任何测试)。CI 尚未运行,因此正文不得声称有 CI 结果。
更新时,保留 keep 区域。 在执行 gh pr edit 之前,读取当前正文(gh pr view <number> --json body -q .body),并将每个 … 区域原样复制到重新生成的正文中,具体按 pr 技能的说明操作。这样,手动添加的截图和备注便能在每次重新运行后保留。备用正文也适用此要求。
备用 PR 正文——仅在未安装 pr 时使用:
## Summary
<what changed and why, one bullet per commit>
**Release:** <Step 6 release note>
## Related Issues
- <ISSUE-ID>
未找到任何问题时,省略 ## Related Issues。
第 10 步:将关联的 Linear 问题转为 In Review——委托给 linear-sync 技能
遵循 linear-sync 技能,将目标状态设为 In Review:读取其 config.json 中的 issueKeys(以及备用的 linearTeamName),获取分支的前导问题 ID 和提交中找到的任何 ID,按团队名称针对每个问题所属的团队解析实时状态 ID(每个团队解析一次并缓存),并以幂等方式应用状态转换(跳过任何已处于 In Review 或更后状态的问题)。
如果 linear-sync 或 Linear MCP 服务不可用,则静默跳过。
第 11 步:推动 PR 达到可合并状态——委托给 triage-pr 技能
完成门槛。 不要把第 9 步的 PR URL 当作最终报告并就此停止。第 9–10 步处于流水线中段。除非适用子步骤 1 中的退出选项,或子步骤 2 或 3 发现缺少
triage-pr或版本过旧(并打印相应的跳过/降级提示及原因),否则请继续执行本步骤。如果报告仅包含草稿状态,且没有ℹ️ triage chain skipped …、⚠️ triage-pr not installed …或⚠️ triage-pr predates 0.20.0 …,则/send-it运行失败(R-1645)。
send-it 会打开 PR;triage-pr 会接手并完成后续流程(R-1151)。本步骤属于运行流程的一部分,不是可选项。 仅交接并不代表运行结束——请继续执行 triage-pr,直至达到终止结果(第 13 步报告、humanEnvelope 为 true 时的人类处理包,或有记录的停止)。本步骤在第 10 步之后运行,以确保关联问题在开始 triage 前已进入 In Review 状态。
-
首先检查退出选项——这是本步骤中的第一件事。 如果传入了
--skip-triage,或config.json中设置了triage: false,则打印ℹ️ triage chain skipped (--skip-triage): <reason>——或(triage: false)——报告 PR URL,然后在此停止运行。不要执行安装检查,也不要启动冷启动轮询:跳过的流程不应产生任何开销。这是 0.8.0 之前的行为。不要为了更快结束而使用此选项。 只有在流程确实无法运行时才应退出,而不是把它当作捷径;它绝不是默认选项:
config.example.json中默认启用triage: true,rheged-skills-setup在协调使用方时也会写入true。跳过后,PR 不会经过 triage——红色 CI 检查无人修复,Bot 发现的问题无人查看——而本步骤的目的正是避免这种状态,因此请像第 5 步处理--skip-preflight一样,在报告中说明原因。合理原因仅限以下几种:PR 修改了流程本身,因此正在运行的说明与磁盘上的说明不一致(例如此捆绑包自身的发布运行);CI 以draft == false为门槛,因此草稿 PR 不会注册检查(优先在该仓库的配置中设置triage: false,而不是为单次运行添加标志);或用户要求在 PR 打开后停止。缺少triage-pr无需设置标志——子步骤 2 会处理。 -
确认已安装
triage-pr——检查此捆绑包旁的../triage-pr/SKILL.md。如果文件不存在,打印⚠️ triage-pr not installed — stopping at the open PR. Install it to chain: npx skills add <repo> --skill triage-pr --agent claude-code --copy然后结束运行。此警告就是完成门槛接受的降级结果提示(与
ℹ️ triage chain skipped …一样)——运行可以停止,但不得报告为默认的成功完成。缺少同级目录只发出警告,不会导致进程退出失败——这比第 10 步静默跳过linear-sync的提示更醒目,因为跳过 triage 流程会导致 PR 工作未完成。 -
等待 CI 注册——冷启动门槛。 第 9 步刚刚创建或更新了 PR,因此 GitHub Actions 可能还没有注册任何检查。如果把空的
statusCheckRollup交给刚启动的triage-pr,它会将其解读为“没有失败项”;而开启promoteOnGreen(默认设置)会在 CI 运行之前就把草稿 PR 改为准备就绪状态。triage-pr 的“尚无失败项不等于通过”规则只保护它自身的等待流程,不适用于冷启动入口——因此 send-it 必须先确认至少有一项检查存在,再进行交接。运行 triage-pr 捆绑的、经过测试的等待脚本,并使用其冷启动模式。使用子步骤 2 在此捆绑包旁找到的triage-pr目录;该脚本包含在 triage-pr 0.20.0 及更高版本中。不要自行编写轮询循环。如果<triage-pr dir>/scripts/wait-checks.mjs不存在,说明安装的 triage-pr 版本过旧。打印⚠️ triage-pr predates 0.20.0 (no scripts/wait-checks.mjs) — re-vendor it to chain并在 PR 打开后停止。与子步骤 2 的警告一样,这也是可接受的降级结果。node <triage-pr dir>/scripts/wait-checks.mjs <number> --registered它每 10 seconds 轮询一次,最多 3 minutes,在 Node 中解析所有内容, 并打印一行判定结果。它会将
gh调用失败与汇总为空区分开来: 如果把调用失败当作“零项检查”,就会把认证或 API 错误变成等待整个时间窗口, 并错误地判定“CI 从未启动”。按主机可承受的方式运行(参见 triage-pr 的 等待长时间操作)。 在 Claude Code 中,将其作为后台命令(
run_in_background)运行,并在运行框架报告命令已退出后继续。在 Cursor 中,将其作为单条 命令运行;三分钟远低于终端超时时间。运行期间保持安静;不要发送“仍在等待”之类的中途提示。根据退出状态采取行动:- 退出
0(ci=registered) → 至少有一项检查已注册;继续执行 子步骤 4。 - 退出
1(gh调用失败) → 停止并报告错误(认证、速率限制、PR 已删除)。不要进入无检查分支:无法验证的状态不等于已验证为空的状态,只有后者才可以安全地交接。 - 退出
5(ci=no-checks),时间窗口内没有任何检查注册 → 此 PR 的 CI 从未启动(仓库没有工作流、此 PR 不符合paths过滤条件或draft == false门控条件,或 Actions 队列停滞)。报告⚠️ 3 minutes 内未注册任何检查 — 将使用 --no-promote 交接,并在下方的交接命令中添加--no-promote,这样空汇总就不会被误认为已确认通过,进而将草稿 PR 切换为就绪状态。链条的其他部分不变。
- 退出
-
交接。 如果运行 send-it 时使用了
--dry-run,此命令也必须带上--dry-run——始终如此。 运行实际的triage-pr会提交、推送,并可能将草稿 PR 切换为就绪状态,因此,省略该参数的 dry run 就不再是 dry run。针对步骤 9 中的 PR,按照triage-pr技能操作,并明确指定 PR 编号,避免重新解析到另一个 PR:triage-pr <number> [--dry-run] [--ci-only] [--no-promote] [--auto-apply]如果传给 send-it 时带有
--dry-run、--ci-only、--no-promote和--auto-apply,就将这些参数原样传递;如果子步骤 3 的冷启动门控添加了--no-promote,也要传递该参数——这是本步骤负责的安全标记,不是用户标记,丢弃它会导致未经验证的汇总将草稿 PR 升级。除此之外不要添加任何参数。triage-pr会读取自己的config.json(promoteOnGreen、humanEnvelope、reviewBots、maxCiRounds,等等)——send-it 不会对其进行任何配置,正如它不会配置commit、preflight、changelog或linear-sync一样。 -
运行完整的 triage-pr 技能——不要在不同阶段之间停止。只在 triage-pr 要求停止的地方暂停(包括
humanEnvelope为true时的人工确认环节——不要代替用户作答)。阶段 A/B 的行为请参见triage-pr。 -
只报告一次。
triage-pr自己的最终报告就是本次运行的报告——将 send-it 的条目(分支、PR URL、变更日志条目、Linear 状态转换)附加到该报告中,而不是再输出一份相互竞争的摘要。遵守 triage-pr 的静默规则(R-1178):交接过程中不要发送中途提示。
--dry-run会将流程延续为triage-pr --dry-run。 如果该分支已有 PR,就交接给triage-pr <number> --dry-run,以便预览同时涵盖失败的检查和未解决的问题。如果没有 PR——dry run 不会创建 PR——则打印no PR to triage yet并以 0 退出。因此,dry run 只会执行只读的gh调用;它仍不会写入、提交或推送任何内容。可安全重复运行。 第二次运行
/send-it时,会针对同一个 PR 重新进入 triage-pr;请参见triage-pr。
标志
--dry-run— 打印将要写入/提交的内容(变更日志预览、分支名称、符合 Conventional Commits 规范的 PR 标题,以及任何版本升级建议),不进行提交,也不推送。如果该分支已有 PR(步骤 11),则会继续调用triage-pr --dry-run,因此会执行只读的gh调用,但仍不会写入任何内容。退出码为 0。--branch=<name>— 在基础分支上有未提交更改时,覆盖自动生成的分支名称。--issue=<ID>— 在自动生成的 slug 前加上 Linear issue ID(例如--issue=R-7→r-7-<slug>,转为小写)。如果指定了--branch,则忽略此选项。--base=<branch>— 覆盖本次运行中config.json的baseBranch。所有使用基础分支的地方都会应用此设置:git fetch、分支差异比较(origin/<base>...HEAD)、PR 的--base,以及传递给derive-bump.mjs/check-skill-bumps.mjs的BASE_REF=origin/<branch>环境变量。可用于堆叠 PR 或目标分支不是main的情况。--title="<conventional subject>"— 直接设置 PR 标题,不再自动生成(当自动生成选错类型时,可用此选项覆盖)。标题仍须符合 Conventional Commits 主题格式(CI 会进行检查)。derive-bump仍会运行(其category决定变更日志条目);如果指定的类型与推导出的type/releaseTriggering矛盾,或指定的标题超过 72 个字符,send-it 会发出警告。--skip-preflight— 完全跳过步骤 5 的 lint 检查关卡,并打印绕过警告。--skip-triage— 在 PR 打开后结束运行:跳过步骤 11 的triage-pr调用(等同于将config.json中的triage设为false)。本次运行会恢复 0.8.0 之前的有限收尾行为。这不是捷径——它会让 PR 保持未经分诊的状态;适用范围有限,详见步骤 11,并在报告中说明原因。--ci-only— 原样转发给triage-pr(步骤 11):运行其阶段 A,并在检查全部通过时停止,绝不将草稿 PR 提升为可审查状态。不影响 send-it 自身的步骤。--no-promote— 原样转发给triage-pr:绝不将草稿 PR 改为可审查状态;检查全部通过时停止。冷启动关卡超时时,send-it 也会自行添加此选项。不影响 send-it 自身的步骤。(有意不转发--promote——提升状态本就是triage-pr的默认行为。)--auto-apply— 原样转发给triage-pr:跳过其阶段 B 的人工确认环节,运行其无人值守处置流程。它可以应用符合无人值守即时修复标准的调查结果;配置了 Linear 后续事项记录时,还可以在无人确认、也无需仅由 Linear 进行审批的情况下创建符合条件的后续事项。不影响 send-it 自身的步骤。--ready— 将 PR 设为可供审查,而非草稿(默认值为草稿)。--worktree=<branch-or-path>— 运行前先进入一个工作树(步骤 0)。
--merge-when-ready 已于 0.8.0 移除。send-it 不再启用 gh pr merge --auto --merge;详见步骤 9 的说明。
说明
- 正文遵循宿主仓库的语言规范。 PR 标题、PR 正文和提交消息都应使用所消费仓库文档规定的正文语言。在整个代码库中,该语言为英式英语(
colour、behaviour、-ise/-yse);changelog技能对其编写的条目也遵循同一规则。此规则仅约束正文,不约束标识符、依赖项名称或上游 API 字段名称。 - Conventional Commits 标题不得超过 72 个字符。 send-it 写入的每一行首行——委派的
/commit主题、send-it 自己的git commit -m行(锁文件、捆绑包版本更新、变更日志),以及据此生成的 PR 标题——都按整个标题计算(type、可选作用域、可选!、:、主题)。标题过长时应改写;不要把内容换行放到正文中来规避字符限制。--title会原样保留,但超过 72 个字符时会发出警告。此处不限制正文和页脚的换行。 - 基于主干开发: PR 的目标分支为基础分支(
config.json中的baseBranch,或本次运行指定的--base)。 - send-it 只递增每个捆绑包的版本,绝不递增仓库版本。 可选的第 6 步版本检查会更新已更改技能自身的
metadata.version;仓库级 npm 发布仍由 release-please 负责(功能 PR:已合并的提交主题;压缩合并路径:压缩合并主题 / PR 标题)。 - send-it 现在会驱动整个流水线,而不仅仅是打开 PR(R-1151 / R-1645)。 在 0.7.0 及之前,它是一个有时限的收尾工具:运行数秒,以生成报告并打开 PR 结束。从 0.8.0 开始,默认运行会继续执行
triage-pr(第 11 步),因此一次/send-it可能会无人值守地运行约 30 分钟——包括多轮 CI 修复和等待评审——并在humanEnvelope为true时以 triage-pr 的处置结果摘要结束;若humanEnvelope为false,则以其第 13 步报告结束。这是对该命令职责的有意调整。使用--skip-triage或设置triage: false可恢复旧有行为,但必须说明理由。 - send-it 绝不合并 PR,也绝不启用自动合并。 它的职责止于将 PR 推进到 CI 通过且可供评审的状态;合并由人工操作。不要把“将合并留给人工”误解为“草稿 PR 创建后就停止”。
- 在非草稿 PR 上运行的 CI 会让整个流程变成负担。 send-it 默认会打开草稿 PR,因此,如果仓库的工作流中包含
if: github.event.pull_request.draft == false,在 PR 准备就绪之前就不会注册任何检查项——第 11 步的冷启动检查随后会在每次运行中等待完整的 3 分钟,并通过--no-promote将任务交给无事可做的 triage-pr。在这类仓库中,请使用--ready或设置triage: false。 - 幂等: 重新运行 send-it 会更新现有 PR 标题和变更日志条目;对于已处于 In Review 或更高状态的 issue,Linear 回写会跳过;第 11 步的流程会针对同一 PR 重新进入
triage-pr,其follow-up-pending标记(R-679)会阻止已处置的发现再次进入结果摘要。 - send-it 不会递增版本,也不会写入任何
CHANGELOG.md。 合并后,release-please 会在主干上按 Conventional Commits 排序(功能 PR 使用合并提交历史;发布 / 扇出使用压缩合并主题),在发布 PR 中递增清单版本,随后发布工作流会发布并打标签。send-it 只会写入带日期的changelog/<ts>-<slug>.md条目(第 7 步),并在合并后由仓库内的丰富器完成最终处理。
错误处理
gh auth status失败——先运行gh auth login;完成身份验证前停止。- 变更日志验证失败——显示错误;不要自动修复。由用户解决条目问题后重新运行。
- 没有领先于基准分支的提交——退出并提示“没有领先于基准分支的提交。没有内容需要发布。”
- 推送分支失败——检查是否有推送权限;确保已配置远程仓库。
- 创建/更新 PR 失败——确认 PR 未关闭;确认分支已推送。
- 未安装
triage-pr——发出警告并在打开的 PR 处结束(步骤 11)。缺少同级工具不会导致 send-it 运行失败。 - 冷启动等待窗口内未注册任何检查(
wait-checks.mjs --registered以5退出)——使用--no-promote交接,并说明原因。绝不能将空的statusCheckRollup视为通过。 - 已安装的
triage-pr早于 0.20.0(没有scripts/wait-checks.mjs)——发出警告,在打开的 PR 处停止,并要求重新供应代码(步骤 11.3)。 - triage 链失败、已中止或信封被拒绝——步骤 3–10 中的提交、变更日志条目、PR 和 Linear 状态转换均保持不变。直接重新运行
/triage-pr <number>,不要重新运行整个 send-it。