mcpField
S
send-it

send-it

github.com/rheged-studio开发0136 次安装2 周前更新
代码仓库

关于 send-it

send-it 是用于将未提交改动整理并推进到 Pull Request 的发布流程编排 skill,面向使用 GitHub 和相关协作流程的开发团队。它把提交、lint、changelog、PR 和后续分流交给配套 skills,并负责分支检查、发布类型判断和推送。

主要功能

  1. 01检查分支并解析工作树
  2. 02编排原子化 Conventional Commits
  3. 03运行按改动触发的 lint
  4. 04生成并校验日期化 changelog
  5. 05推送分支并创建或更新 PR
  6. 06将后续 CI 与评审分流交给 skill

适用场景

  1. 01将分支上的未提交改动整理提交
  2. 02为改动运行 lint 并记录 changelog
  3. 03创建或更新 GitHub Pull Request
  4. 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.json
package.json
README.md
scripts/
check-skill-bumps.mjs
derive-bump.mjs
scripts/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 主开关。

前置条件

  • 已安装并完成身份验证的 gh CLI(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 到该工作树。 否则跳过此步骤。

  1. 运行 git worktree list --porcelain,列出工作树及其路径和分支。

  2. 解析参数:

    • 绝对路径(以 / 开头):与 worktree <path> 字段匹配。
    • 否则:将其视为分支名称,与 branch refs/heads/<name> 字段匹配。
  3. 无匹配项 — 立即退出,并显示:No worktree found for <arg>. Available: <comma-separated paths>。

  4. 匹配项 — cd 到解析出的工作树路径。cwd 会在整个工作流中保持不变,因此后续所有 git 和 gh 调用都会在该工作树中运行。

  5. 确保依赖项已安装。 新创建的工作树没有 node_modules。如果该目录不存在,请立即运行 pnpm install --frozen-lockfile,并确保在任何调用打包脚本或验证器的步骤之前完成,以便 --worktree 能够独立运行:

    [ -d node_modules ] || pnpm install --frozen-lockfile
    
  6. 继续步骤 1。

省略 --worktree 时,此步骤不执行 — 不带参数的 send-it 会继续像之前一样,从当前会话所在目录运行。

步骤 1:分支检查
  1. 获取当前分支:git branch --show-current。
  2. 如果当前位于基准分支(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。
      • 按以下顺序确定分支名称:
        1. --branch=<name> — 按原样使用。
        2. --issue=<ID> — 使用 <ID>-<slug> 并转换为小写(例如 r-7-as-acquired),与 Linear 的 gitBranchName 保持一致。
        3. 否则 — 仅使用 <slug>(不加 wip/ 前缀)。
      • 如果所选分支已存在于本地或 origin,则依次追加 -2、-3……,直到名称未被占用。
      • 运行 git checkout -b <branch>,将工作树切换到该分支。
      • 告知用户:"之前位于基准分支且有未提交的更改;已创建 <branch>,将继续执行。"
    • 在新分支上继续工作流的其余步骤。
  3. **如果当前位于功能分支:**继续。
步骤 2:如果 package.json 有变动,则刷新锁文件

如果当前分支上没有修改 package.json,则跳过此步骤。

  1. 运行 git diff --name-only origin/<base>...HEAD | grep -E '(^|/)package\.json$'。如果 输出为空,则跳过。

  2. 运行 pnpm install --frozen-lockfile。如果成功,说明锁文件已同步 — 继续。

  3. 如果失败,则运行 pnpm install 更新锁文件。

  4. 如果锁文件有变动,请先暂存并提交,然后再进行其他提交:

    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:,并触发不必要的发布。)

  1. 从分支提交中推导 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 时忽略。
  2. (建议)发布范围交叉检查。 config.json 中的 shippablePaths / shippableManifestKeys 是对已发布范围的文档提示——它们不再决定发布类型。可以选择 据此检查类别是否合理:如果 releaseTriggering 为 true,但差异( git diff --name-only origin/<base>...HEAD)未涉及任何 shippablePaths 前缀 (也未涉及 package.json 中的 shippableManifestKeys 键),请将此情况加入发布说明 (如下),以便审阅者确认是否确实有意发布;同样,如果变更涉及已发布路径,但 releaseTriggering: false,也请注明。这只是软性提示;绝不要让它 覆盖类别决策或阻止发布。

  3. 检查每个 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 个字符(首行中的类型、范围、冒号、空格及其余内容)。

  4. 撰写 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,合并后的版本号更新依据已合入的提交主题;对于压缩合并路径,标题_就是_声明。根据更改的语义类别(提交类型)推导标题,以确保两者一致。

  5. 撰写发布说明,供步骤 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 技能编写或更新条目:

  1. 根据 branch frontmatter 字段检测此分支是否已有条目 → 决定更新还是新建。更新时保留文件名和 created_at。

  2. 编写或刷新 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: 解析它。

  3. 运行填充脚本:先运行 node skills/changelog/scripts/set-affected-packages.mjs,再运行 node skills/changelog/scripts/add-links.mjs。

  4. **验证:**运行 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。

  1. 检查是否已有 PR:gh pr view --json number,url 2>/dev/null。
  2. 两个值都按字面量处理。将完整正文(如下)写入临时文件 <body-file>,并通过 --body-file 传入——保留区域可能包含反引号或 $(...)。将标题赋值给单引号括起的变量(每个内嵌的 ' 写作 '\''),例如 title='feat: add $(x) support',并传入 "$title",这样提交主题或 --title 值中的命令替换就不会执行。
  3. 如果要创建: gh pr create --base <base> --draft --title "$title" --body-file <body-file>。如果用户传入了 --ready,则使用 --ready(该标志)代替 --draft。
  4. 如果要更新: gh pr edit <number> --title "$title" --body-file <body-file>。
  5. 通过 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 状态。

  1. 首先检查退出选项——这是本步骤中的第一件事。 如果传入了 --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 会处理。

  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 工作未完成。

  3. 等待 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 切换为就绪状态。链条的其他部分不变。
  4. 交接。 如果运行 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 一样。

  5. 运行完整的 triage-pr 技能——不要在不同阶段之间停止。只在 triage-pr 要求停止的地方暂停(包括 humanEnvelope 为 true 时的人工确认环节——不要代替用户作答)。阶段 A/B 的行为请参见 triage-pr。

  6. 只报告一次。 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。

评论

支持 Markdown