send-it

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

send-it

send-it

Bundle uncommitted work into atomic commits (via the
commit
skill), run the change-gated lint
preflight
, author or update the dated
changelog/<ts>-<slug>.md
entry (via the
changelog
skill), compose a Conventional Commits PR title (the squash subject release-please reads to decide the version bump), push the branch, open or update a pull request against the base branch, and transition any linked Linear issues to In Review (via the
linear-sync
skill).
This skill is the single source of truth for the ship flow. It is a thin orchestrator: it owns only the glue no sibling skill does — the branch guard, worktree resolution, the release-type decision (by category), PR-title composition, push, and the PR — and delegates the rest:
  • Commit → the
    commit
    skill (classify in-scope vs out-of-scope, atomic Conventional Commits, out-of-scope guard).
  • Lint gate → the
    preflight
    skill (change-gated; no-ops when nothing lint-relevant changed).
  • Changelog → the
    changelog
    skill (author/update + validate; an entry for every PR, skipped entirely only when
    config.json
    sets
    changelog: false
    ).
  • Linear In Review → the
    linear-sync
    skill (resolve state by team name, idempotent transition).
The delegated skills auto-detect their own scope, so monorepo features (per-workspace ESLint fan-out, changelog
affected_packages
) no-op cleanly in a single-package repo. send-it configures nothing about them.
Install the delegated skills alongside
send-it
.
This bundle invokes and links its siblings by relative path (
../commit/SKILL.md
,
../preflight/SKILL.md
,
../changelog/SKILL.md
,
../linear-sync/SKILL.md
), so a
--skill send-it
-only install leaves the commit, lint, changelog, and Linear steps unavailable and those links dangling. Install them together:
bash
npx skills add https://github.com/acme-skunkworks/agent-skills \
  --skill send-it --skill commit --skill preflight --skill changelog --skill linear-sync \
  --agent claude-code --agent cursor --copy
This flow intentionally does not run typecheck, tests, or format checks — CI handles those. The only gate it runs is the change-gated
preflight
lint.
将未提交的工作打包为原子提交(通过
commit
技能),运行变更触发的lint 预检(
preflight
),编写或更新带日期的
changelog/<ts>-<slug>.md
条目(通过
changelog
技能),生成符合Conventional Commits规范的PR标题(release-please会读取该合并提交主题来决定版本升级幅度),推送分支,针对基准分支创建或更新 PR,并将所有关联的Linear工单切换至In Review状态(通过
linear-sync
技能)。
该技能是代码交付流程的唯一可信来源。它是一个轻量协调器:仅负责其他兄弟技能未覆盖的衔接工作——分支防护、工作区解析、发布类型决策(基于分类)、PR标题生成、代码推送和PR管理——其余工作均委托给其他技能:
  • 提交
    commit
    技能(区分范围内与范围外变更、生成符合Conventional Commits规范的原子提交、范围外变更防护)。
  • Lint检查
    preflight
    技能(变更触发;当无lint相关变更时自动跳过)。
  • 变更日志
    changelog
    技能(编写/更新 + 验证;每个PR都对应一条条目,仅当
    config.json
    中设置
    changelog: false
    时才完全跳过)。
  • Linear工单状态同步至In Review
    linear-sync
    技能(按团队名称解析状态、幂等状态转换)。
被委托的技能会自动检测自身作用范围,因此单体仓库的特性(如每个工作区的ESLint扩展、变更日志的
affected_packages
)在单包仓库中会自动无操作运行。send-it不会对这些技能进行任何配置。
请将send-it与被委托的技能一同安装。 该工具会通过相对路径(
../commit/SKILL.md
../preflight/SKILL.md
../changelog/SKILL.md
../linear-sync/SKILL.md
)调用并关联其兄弟技能,因此仅安装
--skill send-it
会导致提交、lint、变更日志和Linear同步步骤无法使用,且这些关联会失效。请一起安装它们:
bash
npx skills add https://github.com/acme-skunkworks/agent-skills \
  --skill send-it --skill commit --skill preflight --skill changelog --skill linear-sync \
  --agent claude-code --agent cursor --copy
本流程刻意不运行类型检查、测试或格式检查——这些由CI负责。它仅运行变更触发的
preflight
lint检查。

Configuration

配置

A few knobs live in
config.json
beside this file; edit your copied
config.json
to match the consuming repo (a neutral
config.example.json
ships as a template):
KeyMeaningDefault
baseBranch
The trunk the branch diff is taken against (
origin/<baseBranch>
) and the PR base.
"main"
shippablePaths
(advisory)
Path prefixes that make up the published surface — a documentation hint for reviewers, not the release decision (A-598; see Step 6). Release-type is decided by the change's semantic category, so these no longer gate the title. Kept for the optional publish-surface cross-check note.
["skills/"]
shippableManifestKeys
(advisory)
package.json
keys that form the published-
files
surface — same advisory role as
shippablePaths
, no longer a release gate.
["name", "version", "files", "publishConfig"]
changelog
(optional)
Whether to author a dated
changelog/
entry at all (Steps 7–8). Set
false
for repos with no changelog flow — no
changelog/
directory and no
changelog
skill installed (e.g. a
private
repo with no release pipeline). When
false
, send-it skips changelog authoring entirely, and the category decision continues to drive only the PR title. Omit it (or set
true
) whenever the
changelog
skill is installed.
true
bundleVersioning
(optional)
Enables the per-bundle version-bump check (Step 6) for repos that ship many independently-versioned skill bundles. An object
{ root, manifest, skillFile }
naming the bundle parent dir and the manifest / skill-manifest filenames each bundle carries. Omit it entirely in single-package repos — the check then no-ops.
unset (disabled)
The team name, issue-ID prefixes, and workspace slug are not configured here — they live in the
linear-sync
and
changelog
skills' own
config.json
files, read by the delegated steps.
Changelog scope (was
changelogScope
).
send-it authors a dated entry for every PR — the "record everything, filter later" model. Release notes come from filtering the changelog to the version-stamped (release-triggering) entries at release time, not from gating authoring at write time. The
changelogScope
knob (added in 0.4.0) is gone (A-600); only the
changelog: true|false
master switch remains.
本文件旁的
config.json
中有一些可配置项;请编辑你复制的
config.json
以匹配目标仓库(附带一个中性的
config.example.json
作为模板):
键名含义默认值
baseBranch
分支差异对比的基准主干(
origin/<baseBranch>
),同时也是PR的目标分支。
"main"
shippablePaths
(参考性)
构成发布内容的路径前缀——供评审者参考的文档提示,并非发布决策依据(A-598;见步骤6)。发布类型由变更的语义分类决定,因此这些路径不再作为标题的判断依据。保留该配置是为了可选的发布内容交叉检查备注。
["skills/"]
shippableManifestKeys
(参考性)
package.json
中构成发布
files
内容的键——作用与
shippablePaths
相同,不再作为发布判断依据。
["name", "version", "files", "publishConfig"]
changelog
(可选)
是否编写带日期的
changelog/
条目(步骤7-8)。对于无变更日志流程的仓库(无
changelog/
目录且未安装
changelog
技能,例如无发布流水线的私有仓库),请设置为
false
。当设置为
false
时,send-it会完全跳过变更日志编写步骤,分类决策仍仅用于生成PR标题。只要安装了
changelog
技能,请省略该配置(或设置为
true
)。
true
bundleVersioning
(可选)
为发布多个独立版本技能包的仓库启用每个包的版本升级检查(步骤6)。该配置为一个对象
{ root, manifest, skillFile }
,指定包的父目录以及每个包携带的清单/技能清单文件名。在单包仓库中请完全省略该配置——此时检查会自动无操作运行。
未设置(禁用)
团队名称、工单ID前缀和工作区别名不在这里配置——它们位于
linear-sync
changelog
技能各自的
config.json
文件中,由被委托的步骤读取。
变更日志范围(原
changelogScope
)。
send-it会为每个PR编写一条带日期的条目——采用“记录所有内容,后续过滤”的模式。发布说明来自于在发布时将变更日志过滤为带有版本标记(触发发布)的条目,而非在编写时限制内容。
changelogScope
配置项(0.4.0版本新增)已移除(A-600);仅保留
changelog: true|false
这个总开关。

Prerequisites

前置条件

  • gh
    CLI installed and authenticated (
    gh auth status
    ).
  • The sibling skills (
    commit
    ,
    preflight
    ,
    changelog
    ,
    linear-sync
    ) installed.
  • 已安装并认证
    gh
    CLI(运行
    gh auth status
    验证)。
  • 已安装兄弟技能(
    commit
    preflight
    changelog
    linear-sync
    )。

Process

流程

Step 0: Worktree resolution (only if
--worktree=
is set)

步骤0:工作区解析(仅当设置
--worktree=
时执行)

If
--worktree=<branch-or-path>
was passed, resolve and
cd
into that worktree before any other step runs. Skip this step otherwise.
  1. Run
    git worktree list --porcelain
    to list worktrees with their paths and branches.
  2. Resolve the argument:
    • Absolute path (starts with
      /
      ): match against the
      worktree <path>
      field.
    • Otherwise: treat as a branch name and match against the
      branch refs/heads/<name>
      field.
  3. No match — exit immediately with:
    No worktree found for <arg>. Available: <comma-separated paths>
    .
  4. Match
    cd
    into the resolved worktree path. The
    cwd
    persists for the rest of the workflow, so all subsequent
    git
    and
    gh
    calls operate on the worktree.
  5. Ensure dependencies are present. A freshly-created worktree has no
    node_modules
    . If it is absent, run
    pnpm install --frozen-lockfile
    now — before any step that invokes a bundled script or a validator — so
    --worktree
    is self-sufficient:
    bash
    [ -d node_modules ] || pnpm install --frozen-lockfile
  6. Continue to Step 1.
This step does nothing when
--worktree
is omitted — no-arg send-it keeps working unchanged from whatever directory the session is in.
如果传入了
--worktree=<branch-or-path>
,则在执行其他步骤前先解析并切换到该工作区。否则跳过此步骤。
  1. 运行
    git worktree list --porcelain
    列出所有工作区的路径和分支。
  2. 解析参数:
    • 绝对路径(以
      /
      开头):匹配
      worktree <path>
      字段。
    • 其他情况:视为分支名称,匹配
      branch refs/heads/<name>
      字段。
  3. 无匹配项——立即退出并提示:
    No worktree found for <arg>. Available: <逗号分隔的路径>
  4. 匹配成功——切换到解析后的工作区路径。当前工作目录会在后续流程中保持不变,因此所有后续的
    git
    gh
    命令都会在该工作区执行。
  5. 确保依赖已安装。新创建的工作区没有
    node_modules
    。如果不存在该目录,则立即运行
    pnpm install --frozen-lockfile
    ——在执行任何调用打包脚本或验证器的步骤之前,确保
    --worktree
    模式可以独立运行:
    bash
    [ -d node_modules ] || pnpm install --frozen-lockfile
  6. 继续执行步骤1。
当省略
--worktree
时,此步骤不执行——无参数的send-it会在当前会话所在的目录中正常运行。

Step 1: Branch guard

步骤1:分支防护

  1. Get the current branch:
    git branch --show-current
    .
  2. If on the base branch (
    baseBranch
    from
    config.json
    ; default
    main
    ):
    • Run
      git status --porcelain
      . If clean, exit with: "Nothing to ship from the base branch. Create a feature branch first."
    • If there are uncommitted changes:
      • Inspect the diff (
        git diff
        and
        git diff --cached
        ) and the changed file paths.
      • Derive a short kebab-case slug summarising the change (~3 words, lowercase, max ~40 chars). Examples:
        add-readme-section
        ,
        fix-config-typo
        .
      • Branch name resolution (in order):
        1. --branch=<name>
          — use as-is.
        2. --issue=<ID>
          — use
          <ID>-<slug>
          lower-cased (e.g.
          a-7-as-acquired
          ), matching Linear's
          gitBranchName
          .
        3. Otherwise — just
          <slug>
          (no
          wip/
          prefix).
      • If the chosen branch already exists locally or on
        origin
        , append
        -2
        ,
        -3
        , … until unused.
      • Run
        git checkout -b <branch>
        to move the working tree onto it.
      • Inform the user: "Was on the base branch with uncommitted changes; created
        <branch>
        and continuing."
    • Continue with the rest of the workflow on the new branch.
  3. If on a feature branch: continue.
  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)别名,总结变更内容(约3个单词,小写,最多约40个字符)。示例:
        add-readme-section
        fix-config-typo
      • 分支名称解析顺序
        1. --branch=<name>
          ——直接使用该名称。
        2. --issue=<ID>
          ——使用
          <ID>-<slug>
          (全小写,例如
          a-7-as-acquired
          ),与Linear的
          gitBranchName
          格式匹配。
        3. 其他情况——仅使用
          <slug>
          (无
          wip/
          前缀)。
      • 如果选定的分支已在本地或
        origin
        上存在,则追加
        -2
        -3
        ……直到找到未使用的名称。
      • 运行
        git checkout -b <branch>
        将工作区切换到新分支。
      • 通知用户:"Was on the base branch with uncommitted changes; created
        <branch>
        and continuing."
    • 在新分支上继续执行后续流程。
  3. 如果当前在功能分支:继续执行。

Step 2: Refresh lockfile if
package.json
drifted

步骤2:如果
package.json
发生变更则更新锁文件

Skip this step if no
package.json
was touched on the branch.
  1. git diff --name-only origin/<base>...HEAD | grep -E '(^|/)package\.json$'
    . If empty, skip.
  2. Run
    pnpm install --frozen-lockfile
    . If it succeeds, the lockfile is already in sync — continue.
  3. If it fails, run
    pnpm install
    to update the lockfile.
  4. If the lockfile changed, stage and commit it before any other commits go in:
    bash
    git add pnpm-lock.yaml
    git commit -m "chore: update lockfile"
This keeps CI's
--frozen-lockfile
install green. (Skip silently in repos that don't use pnpm.)
如果分支上未修改
package.json
,则跳过此步骤。
  1. 运行
    git diff --name-only origin/<base>...HEAD | grep -E '(^|/)package\.json$'
    。如果结果为空,则跳过。
  2. 运行
    pnpm install --frozen-lockfile
    。如果执行成功,说明锁文件已同步——继续执行。
  3. 如果执行失败,运行
    pnpm install
    更新锁文件。
  4. 如果锁文件发生变更,则在提交其他内容之前暂存并提交锁文件:
    bash
    git add pnpm-lock.yaml
    git commit -m "chore: update lockfile"
这可以确保CI的
--frozen-lockfile
安装步骤正常通过。(在不使用pnpm的仓库中会自动静默跳过。)

Step 3: Commit uncommitted changes — delegate to the
commit
skill

步骤3:提交未提交的变更——委托给
commit
技能

send-it is the all-in-one finisher: whatever's uncommitted should be committed before the changelog/PR work begins — but only what belongs to this branch.
Follow the
commit
skill to do this: classify uncommitted files in-scope vs out-of-scope against the merge base (
git merge-base HEAD origin/<base>
), show a staging plan flagging any out-of-scope files (never
git add -A
; stray files from another branch/worktree are never staged silently), and create logical atomic Conventional Commits (type + optional scope + British-English body;
!
/
BREAKING CHANGE:
for breaking changes). If clean, skip this step. Direct the
commit
skill to classify against this send-it run's resolved base —
<base>
is
baseBranch
(from
config.json
), or
--base
when passed — not the
commit
skill's own
config.json
baseBranch
, which differs on a
--base
run (the stacked-PR case). The scope classification and the out-of-scope guard must be computed against the same base send-it ships against, or a stacked PR would mis-classify files.
The Conventional-Commit types this step writes are the input to Step 6's release decision (
derive-bump.mjs
reads them back out of the commits), so the honest types and
!
/
BREAKING CHANGE:
markers matter.
This delegation covers only the initial commit of uncommitted work. send-it's own later, targeted commits stay here: the lockfile refresh (Step 2), the optional bundle-version bump (Step 6), and the changelog entry (Step 8).
send-it是一站式收尾工具:在开始变更日志/PR工作之前,所有未提交的内容都应该被提交——但仅提交属于当前分支的内容。
按照
commit
技能的流程执行:根据合并基准(
git merge-base HEAD origin/<base>
)区分未提交文件的范围内与范围外,展示暂存计划并标记任何范围外的文件(绝不会自动执行
git add -A
;绝不会静默暂存来自其他分支/工作区的无关文件),并创建符合逻辑的原子Conventional Commits(类型 + 可选范围 + 英式英文描述;使用
!
/
BREAKING CHANGE:
标记破坏性变更)。如果工作区干净,则跳过此步骤。指定
commit
技能根据本次send-it运行解析出的基准进行分类——
<base>
config.json
中的
baseBranch
,或者传入的
--base
参数——而非
commit
技能自身
config.json
中的
baseBranch
,因为在使用
--base
的场景中(堆叠PR),后者可能不同。范围分类和范围外防护必须与send-it交付时使用的基准保持一致,否则堆叠PR会错误分类文件。
此步骤生成的Conventional Commit类型是步骤6中发布决策的输入(
derive-bump.mjs
会从提交记录中读取这些类型),因此准确的类型和
!
/
BREAKING CHANGE:
标记非常重要。
此委托仅覆盖未提交工作的初始提交。send-it自身后续的针对性提交仍在此处处理:锁文件更新(步骤2)、可选的包版本升级(步骤6)和变更日志条目(步骤8)。

Step 4: Fetch the base branch and confirm there's something to ship

步骤4:拉取基准分支并确认有内容可交付

bash
git fetch origin <base>
If
git log origin/<base>..HEAD
is empty, exit with: "No commits ahead of the base branch. Nothing to ship."
bash
git fetch origin <base>
如果
git log origin/<base>..HEAD
为空,则退出并提示:"No commits ahead of the base branch. Nothing to ship."

Step 5: Lint gate — delegate to the
preflight
skill

步骤5:Lint检查——委托给
preflight
技能

--skip-preflight
bypasses this whole step. Print a clear
⚠️ lint gate bypassed (--skip-preflight)
warning and jump to Step 6. Use it only when the gate misfires; CI still runs the repo's real linting.
Run the change-gated lint preflight, following the
preflight
skill:
bash
node skills/preflight/scripts/preflight.mjs
Act on its exit-code contract, reading
.preflight-summary.json
to interpret a non-zero exit:
  • Exit 0 — pass. No introduced violations; continue.
  • Exit 1 with
    violations.introducedCount > 0
    — introduced violations (blocking).
    Run
    node skills/preflight/scripts/lint-fix.mjs
    , re-run preflight, and repeat until introduced violations clear. Commit the fixes (a
    style:
    /
    fix:
    commit, or fold into the relevant Step 3 commit if not yet pushed) before continuing.
  • Exit 1 with
    introducedCount == 0
    and
    results.failedLinters
    non-empty — a linter could not run (its binary is absent), not a real violation.
    This is expected in a repo that doesn't use that toolchain (e.g. a docs/skills repo with no ESLint or markdownlint installed). Treat it as a skip, not a block: warn that
    <linter>
    was unavailable and continue. The repo's own CI owns whatever linting it actually runs.
  • Exit 2 — pre-existing violations only. Not introduced by this branch — do not block shipping. Surface them and continue (optionally offer a debt issue per the preflight skill).
Preflight is change-gated: it lints only the categories the branch touched, so it no-ops when nothing lint-relevant changed. Skip this step entirely only if
preflight
isn't installed.
**
--skip-preflight
**会跳过整个步骤。打印清晰的
⚠️ lint gate bypassed (--skip-preflight)
警告并跳至步骤6。仅当检查出现异常时使用;CI仍会运行仓库的完整lint检查。
按照
preflight
技能的流程运行变更触发的lint预检:
bash
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技能的建议选择创建技术债务工单)。
预检是变更触发的:仅检查分支涉及的分类,因此当无lint相关变更时会自动无操作运行。仅当未安装
preflight
时才完全跳过此步骤。

Step 6: Decide release-type by category and compose the Conventional Commits PR title

步骤6:根据分类决定发布类型并生成符合Conventional Commits规范的PR标题

Versioning is driven by release-please reading Conventional Commits. The repo squash-merges, so the squash subject is the PR title — and that single conventional title is what release-please parses to decide the bump. send-it composes a correct conventional title and writes the dated changelog entry (for every PR — see Step 7). It does not bump versions, write any
CHANGELOG.md
, or tag.
Release-type is decided by the change's semantic category — the Conventional-Commit type of the work send-it itself committed — not by which paths the diff touches (A-598). A docs-only edit is
docs:
(no release) even when it lives under a published path like
skills/
; a
feat:
is a release wherever its files sit. (Earlier versions keyed this off
shippablePaths
, which mis-titled a docs edit inside a published path as
feat:
/
fix:
and cut a spurious release.)
  1. Derive the slug, body, type, and category from the branch commits via the bundled helper (zero-dep — no tsx):
    bash
    node skills/send-it/scripts/derive-bump.mjs
    It prints JSON:
    { "slug", "bump", "body", "type", "breaking", "category", "releaseTriggering" }
    :
    • type
      — the Conventional-Commit type of the lead commit (
      feat
      /
      fix
      /
      perf
      /
      docs
      /
      refactor
      /
      chore
      /
      ci
      /…); this is the PR-title prefix.
    • breaking
      true
      if any commit carries a
      !
      or a
      BREAKING CHANGE:
      trailer.
    • category
      — the dated changelog
      category
      enum value (
      feat
      feature
      ,
      fix
      fix
      ,
      perf
      perf
      ,
      docs
      docs
      ,
      refactor
      refactor
      , everything else →
      chore
      ).
    • releaseTriggering
      true
      iff
      breaking
      or
      type ∈ {feat, fix, perf}
      . This is the release decision:
      true
      cuts a release,
      false
      does not.
    • bump
      major
      /
      minor
      /
      patch
      , the release magnitude when
      releaseTriggering
      (a
      BREAKING CHANGE:
      /
      !
      → major; lead
      feat:
      → minor; else patch). Ignored when
      releaseTriggering
      is
      false
      .
  2. (Advisory) publish-surface cross-check.
    shippablePaths
    /
    shippableManifestKeys
    in
    config.json
    are a documentation hint of the published surface — they do not decide release-type any more. Optionally sanity-check the category against them: if
    releaseTriggering
    is
    true
    but the diff (
    git diff --name-only origin/<base>...HEAD
    ) touches no
    shippablePaths
    prefix (nor a
    shippableManifestKeys
    key in
    package.json
    ), note it in the PR body so a reviewer can confirm the release was intended — and likewise if a change touching a published path is
    releaseTriggering: false
    . This is a soft note only; never let it override the category decision or block.
  3. Check per-bundle version bumps — only when
    config.json
    sets
    bundleVersioning
    (multi-artefact repos; skip this step entirely when it's unset). Each skill bundle carries its own version in its
    package.json
    +
    SKILL.md metadata.version
    , bumped by hand and decoupled from the repo release. CI enforces that the two agree, but nothing enforces they were bumped when the bundle's content changed — so an edited bundle can ship with a stale version label. Close that gap:
    bash
    node skills/send-it/scripts/check-skill-bumps.mjs
    It prints
    { "configured", "unbumped": [{ name, currentVersion, suggestedBump, suggestedVersion, manifestPath, skillPath }], "bumped" }
    . For each
    unbumped
    entry, surface the proposal and apply it on confirmation:
    skills/<name>
    changed but its version is still
    <currentVersion>
    . Suggested bump:
    <suggestedBump>
    <suggestedVersion>
    (matches the PR-title bump). Apply? (yes / no / patch / minor / major)
    On
    yes
    (or an explicit level), edit both
    manifestPath
    (
    version
    ) and
    skillPath
    (
    metadata.version
    ) to the chosen version — in lockstep, so the parity invariant CI checks still holds — then stage and commit just those two files:
    git commit -m "chore(<name>): release <name>@<version>"
    . On
    no
    , leave it and continue. Under
    --dry-run
    , print the proposal and edit nothing.
  4. Compose the PR title as a single Conventional Commits subject — this is the release-please bump signal and is enforced by CI's PR-title lint. If
    --title
    was passed, use it verbatim (still run
    derive-bump
    above for the changelog
    category
    , and warn — don't block — if the supplied type contradicts the derived
    type
    /
    releaseTriggering
    ). Otherwise build it straight from the derived fields:
    • Prefix =
      type
      (add a scope when one is obvious, e.g.
      feat(<scope>):
      ), plus
      !
      when
      breaking
      — so
      feat: <body>
      ,
      fix: <body>
      ,
      perf: <body>
      ,
      docs: <body>
      ,
      refactor: <body>
      ,
      chore: <body>
      ,
      feat!: <body>
      , etc.
    • Release-triggering (
      releaseTriggering: true
      ) → the prefix is already a release type (
      feat
      /
      fix
      /
      perf
      , or any
      !
      ); release-please cuts the bump from it. Add the scope; that's it.
    • Non-release (
      releaseTriggering: false
      ) → the prefix is a non-release type (
      docs
      /
      refactor
      /
      chore
      /
      ci
      /
      build
      /
      test
      /
      style
      ); release-please cuts nothing.
    ⚠️ The PR title is the version. A mistyped prefix silently ships the wrong semver — a
    feat:
    on a docs PR cuts a needless release; a
    chore:
    on a real fix ships nothing. There is no changeset file to cross-check against: the title is the declaration. It comes straight from the change's semantic category (the commit types) — keep the commit types honest and the title follows.
    When
    releaseTriggering
    is
    false
    , note
    no release (<type>-only)
    in the PR body so reviewers can confirm the non-release type was intentional.
版本控制由release-please通过读取Conventional Commits来驱动。仓库采用合并提交(squash merge)方式,因此合并提交主题即为PR标题——这个符合规范的标题是release-please用来决定版本升级幅度的依据。send-it会生成正确的规范标题并编写带日期的变更日志条目(每个PR都有——见步骤7)。它不会升级版本、编写
CHANGELOG.md
或打标签。
发布类型由变更的语义分类——即send-it自身提交的工作的Conventional Commit类型——而非差异涉及的路径决定(A-598)。仅修改文档的提交属于
docs:
类型(不触发发布),即使它位于
skills/
这样的发布路径下;
feat:
类型的提交无论文件位置如何都会触发发布。(早期版本基于
shippablePaths
判断,这会导致发布路径内的文档编辑被错误标记为
feat:
/
fix:
类型并触发不必要的发布。)
  1. 通过内置工具从分支提交中提取别名、描述、类型和分类(零依赖——无需tsx):
    bash
    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
      /…);这是PR标题的前缀。
    • breaking
      ——如果任何提交带有
      !
      BREAKING CHANGE:
      标记,则为
      true
    • category
      ——带日期的变更日志的
      category
      枚举值(
      feat
      feature
      fix
      fix
      perf
      perf
      docs
      docs
      refactor
      refactor
      ,其他类型→
      chore
      )。
    • releaseTriggering
      ——当
      breaking
      true
      type ∈ {feat, fix, perf}
      时为
      true
      。这是发布决策:
      true
      会触发发布,
      false
      则不会。
    • bump
      ——
      major
      /
      minor
      /
      patch
      ,当
      releaseTriggering
      true
      时的发布幅度
      BREAKING CHANGE:
      /
      !
      →major;首个提交为
      feat:
      →minor;否则为patch)。当
      releaseTriggering
      false
      时忽略该值。
  2. (参考性)发布内容交叉检查
    config.json
    中的
    shippablePaths
    /
    shippableManifestKeys
    是发布内容的文档提示——它们不再决定发布类型。可选择将分类与这些配置进行 sanity 检查:如果
    releaseTriggering
    true
    但分支差异(
    git diff --name-only origin/<base>...HEAD
    )未涉及任何
    shippablePaths
    前缀(也未修改
    package.json
    中的
    shippableManifestKeys
    键),则在PR正文中添加备注,供评审者确认是否确实需要发布——反之,如果涉及发布路径的变更被标记为
    releaseTriggering: false
    ,也添加备注。这仅为软提示;绝不会覆盖分类决策或阻塞流程。
  3. 检查每个包的版本升级——仅当
    config.json
    中设置了
    bundleVersioning
    时执行(多产物仓库;未设置时完全跳过此步骤)。每个技能包在其
    package.json
    +
    SKILL.md metadata.version
    中都有自己的版本,需手动升级且与仓库发布版本解耦。CI会强制这两个版本保持一致,但不会强制在包内容变更时升级版本——因此编辑后的包可能会带着过时的版本标签交付。此步骤填补了这个空白:
    bash
    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
    模式下,仅打印建议而不进行任何编辑。
  4. 生成PR标题为符合Conventional Commits规范的单行主题——这是release-please的版本升级信号,且会被CI的PR标题lint检查强制要求。如果传入了
    --title
    参数,则直接使用该标题(仍需运行上述
    derive-bump
    来获取变更日志的
    category
    ,如果传入的类型与推导的
    type
    /
    releaseTriggering
    不一致,会警告但不阻塞)。否则直接根据推导的字段生成标题:
    • 前缀 =
      type
      (当范围明确时添加范围,例如
      feat(<scope>):
      ),如果
      breaking
      true
      则追加
      !
      ——例如
      feat: <body>
      fix: <body>
      perf: <body>
      docs: <body>
      refactor: <body>
      chore: <body>
      feat!: <body>
      等。
    • 触发发布
      releaseTriggering: true
      )→ 前缀已经是发布类型(
      feat
      /
      fix
      /
      perf
      ,或带有
      !
      的任何类型);release-please会根据该前缀决定升级幅度。添加范围即可。
    • 不触发发布
      releaseTriggering: false
      )→ 前缀为非发布类型(
      docs
      /
      refactor
      /
      chore
      /
      ci
      /
      build
      /
      test
      /
      style
      );release-please不会触发任何发布。
    ⚠️ PR标题即版本。 错误的前缀会静默导致错误的语义化版本——将文档PR标记为
    feat:
    会触发不必要的发布;将实际修复标记为
    chore:
    则不会触发任何发布。没有变更集文件可以交叉验证:标题就是声明。它直接来自变更的语义分类(提交类型)——确保提交类型准确,标题自然会正确。
    releaseTriggering
    false
    时,在PR正文中添加
    no release (<type>-only)
    备注,供评审者确认非发布类型是否符合预期。

Step 7: Author or update the dated changelog entry — delegate to the
changelog
skill

步骤7:编写或更新带日期的变更日志条目——委托给
changelog
技能

Disabled entirely? If
config.json
sets
changelog: false
, skip Steps 7 and 8 completely — author nothing, run no
changelog
scripts, make no
docs(changelog)
commit — and note "changelog step disabled (no changelog flow in this repo)" in the run summary. This is for repos with no
changelog/
directory and no
changelog
skill installed; the category decision from Step 6 still drives the PR title. When
changelog
is unset or
true
, always author an entry (the
changelogScope
knob was removed — A-600).
An entry for every PR. send-it authors a dated
changelog/
entry for every PR, release-triggering or not — the "record everything, filter later" model. The dated changelog is the full record of merged work; release notes filter it to the version-stamped (release-triggering) entries at release time, so a non-release entry simply carries no
version
.
changelog: false
is the only thing that suppresses authoring.
Follow the
changelog
skill to author or update the entry:
  1. Detect an existing entry for this branch (by the
    branch
    frontmatter field) → update vs create. On update, preserve the filename and
    created_at
    .
  2. Write/refresh
    changelog/<YYYYMMDD-HHMMSS>-<slug>.md
    (the
    <slug>
    from Step 6), deriving
    title
    /
    release_note
    /
    issues
    from the branch. Set
    category
    and
    breaking
    straight from
    derive-bump
    's output (Step 6):
    category
    is its
    category
    field (
    feature
    /
    fix
    /
    perf
    /
    docs
    /
    refactor
    /
    chore
    — the changelog enum), and
    breaking
    is its
    breaking
    flag. For a non-release entry (
    releaseTriggering: false
    ),
    release_note
    may be blank when there's no user-facing impact.
    Leave the post-merge fields (
    merged_at
    ,
    commit
    ,
    pr
    ,
    merge_strategy
    ,
    stats
    ) and
    version
    as blank placeholders — the release step finalises them (a non-release entry keeps
    version
    blank, as no release is cut for it). This includes
    pr
    : no step here writes it back after the PR opens; the release/enrich step resolves it post-merge from the entry's
    branch:
    .
  3. Run the enrichment scripts:
    node skills/changelog/scripts/set-affected-packages.mjs
    then
    node skills/changelog/scripts/add-links.mjs
    .
  4. Validate:
    node skills/changelog/scripts/validate-changelog.mjs
    . It must pass before committing — if it fails, surface the error and abort; don't auto-fix.
完全禁用? 如果
config.json
中设置
changelog: false
,则完全跳过步骤7和8——不编写任何内容,不运行任何
changelog
脚本,不创建
docs(changelog)
提交——并在运行摘要中添加备注"changelog step disabled (no changelog flow in this repo)"。这适用于无
changelog/
目录且未安装
changelog
技能的仓库;步骤6的分类决策仍会驱动PR标题。当
changelog
未设置或为
true
时,始终编写条目
changelogScope
配置项已移除——A-600)。
每个PR对应一条条目。 send-it会为每个PR编写一条带日期的
changelog/
条目,无论是否触发发布——采用“记录所有内容,后续过滤”的模式。带日期的变更日志是合并工作的完整记录;发布说明来自于在发布时将变更日志过滤为带有版本标记(触发发布)的条目,因此不触发发布的条目仅保留
version
为空。只有
changelog: false
会抑制条目编写。
按照
changelog
技能的流程编写或更新条目:
  1. 检测当前分支是否已有对应的条目(通过
    branch
    前置字段)→ 决定是更新还是创建。如果是更新,保留文件名和
    created_at
    字段。
  2. 编写/刷新
    changelog/<YYYYMMDD-HHMMSS>-<slug>.md
    <slug>
    来自步骤6),从分支中提取
    title
    /
    release_note
    /
    issues
    。直接从
    derive-bump
    的输出(步骤6)中设置
    category
    breaking
    category
    为其
    category
    字段(
    feature
    /
    fix
    /
    perf
    /
    docs
    /
    refactor
    /
    chore
    ——变更日志枚举值),
    breaking
    为其
    breaking
    标记。对于不触发发布的条目(
    releaseTriggering: false
    ),如果没有用户可见的影响,
    release_note
    可以为空。
    保留合并后的字段(
    merged_at
    commit
    pr
    merge_strategy
    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
    。必须验证通过才能提交——如果失败,提示错误并终止流程;不自动修复。

Step 8: Commit the changelog entry and push

步骤8:提交变更日志条目并推送

If a
changelog/
entry was written in Step 7 (i.e.
changelog
is not
false
), commit only that file:
bash
git add changelog/<YYYYMMDD-HHMMSS>-<slug>.md
git commit -m "docs(changelog): <one-line summary>"
Then push the branch:
bash
git push -u origin <branch>
如果步骤7中编写了
changelog/
条目(即
changelog
不为
false
),则仅提交该文件:
bash
git add changelog/<YYYYMMDD-HHMMSS>-<slug>.md
git commit -m "docs(changelog): <单行摘要>"
然后推送分支:
bash
git push -u origin <branch>

Step 9: Create or update the PR

步骤9:创建或更新PR

<title>
is the Conventional Commits PR title from Step 6 — release-please reads it as the squash subject, so set it on both create and update (re-derive it every run so it stays in sync with the branch's commits).
  1. Check for an existing PR:
    gh pr view --json number,url 2>/dev/null
    .
  2. If creating:
    gh pr create --base <base> --draft --title "<title>" --body "<body>"
    . Use
    --ready
    (the flag) instead of
    --draft
    if the user passed
    --ready
    .
  3. If updating:
    gh pr edit <number> --title "<title>" --body "<body>"
    .
  4. If
    --merge-when-ready
    was passed:
    after create/update, run
    gh pr merge --auto --squash <number>
    to enable auto-merge once requirements are met.
  5. Return the PR URL via
    gh pr view --json url -q '.url'
    .
PR body template:
markdown
undefined
<title>
是步骤6中生成的符合Conventional Commits规范的PR标题——release-please会将其作为合并提交主题读取,因此在创建和更新时都要设置该标题(每次运行都会重新推导,以保持与分支提交同步)。
  1. 检查是否已有PR:
    gh pr view --json number,url 2>/dev/null
  2. 如果创建PR:运行
    gh pr create --base <base> --draft --title "<title>" --body "<body>"
    。如果用户传入了
    --ready
    参数,则使用
    --ready
    标志而非
    --draft
  3. 如果更新PR:运行
    gh pr edit <number> --title "<title>" --body "<body>"
  4. 如果传入了
    --merge-when-ready
    参数
    :创建/更新PR后,运行
    gh pr merge --auto --squash <number>
    以启用满足条件后的自动合并。
  5. 通过
    gh pr view --json url -q '.url'
    返回PR链接。
PR正文模板:
markdown
undefined

Summary

摘要

  • Comprehensive summary of all changes on this branch
  • What changed and why
  • 分支上所有变更的全面总结
  • 变更内容及原因

Related Issues

关联工单

<!-- Linear identifiers extracted from the branch and commits -->
  • <ISSUE-ID>
<!-- 从分支和提交中提取的Linear标识符 -->
  • <工单ID>

Test Plan

测试计划

  • <test>

Drop the `## Related Issues` section if no issues were found.
  • <测试内容>

如果未找到关联工单,则移除`## 关联工单`部分。

Step 10: Transition linked Linear issues to In Review — delegate to the
linear-sync
skill

步骤10:将关联的Linear工单切换至In Review状态——委托给
linear-sync
技能

Follow the
linear-sync
skill with target state In Review: read its
config.json
for
linearTeamName
and
issueKeys
, extract issue IDs from the branch and commits, resolve the live state ID by team name (once), and apply the transition idempotently (skip any issue already at or past In Review). Skip silently if
linear-sync
or the Linear MCP server is unavailable.
按照
linear-sync
技能的流程,将目标状态设置为In Review:从其
config.json
中读取
linearTeamName
issueKeys
,从分支和提交中提取工单ID,按团队名称解析实时状态ID(仅解析一次),并幂等应用状态转换(跳过已处于或超过In Review状态的工单)。如果
linear-sync
或Linear MCP服务器不可用,则静默跳过。

Flags

标志

  • --dry-run
    — print what would be written/submitted (changelog preview, branch, conventional PR title, any version-bump proposals), make no commits, no push, no
    gh
    calls. Exit 0.
  • --branch=<name>
    — override the auto-derived branch name when running on the base branch with uncommitted changes.
  • --issue=<ID>
    — prefix the auto-derived slug with a Linear issue ID (e.g.
    --issue=A-7
    a-7-<slug>
    , lower-cased). Ignored if
    --branch
    is given.
  • --base=<branch>
    — override
    config.json
    's
    baseBranch
    for this run. Applies everywhere the base is used: the
    git fetch
    , the branch diff (
    origin/<base>...HEAD
    ), the PR
    --base
    , and the
    BASE_REF=origin/<branch>
    env passed to
    derive-bump.mjs
    /
    check-skill-bumps.mjs
    . Use it for stacked PRs or a non-
    main
    target.
  • --title="<conventional subject>"
    — set the PR title verbatim instead of deriving it (escape hatch for when derivation picks the wrong type). It must still be a valid Conventional Commits subject (CI lints it).
    derive-bump
    still runs (its
    category
    drives the changelog entry); send-it warns if the supplied type contradicts the derived
    type
    /
    releaseTriggering
    .
  • --skip-preflight
    — skip the Step 5 lint gate entirely, printing a bypass warning.
  • --ready
    — open the PR ready-for-review instead of draft (default is draft).
  • --merge-when-ready
    — after create/update, enable
    gh pr merge --auto --squash
    .
  • --worktree=<branch-or-path>
    cd
    into a worktree before running (Step 0).
  • --dry-run
    ——打印将要编写/提交的内容(变更日志预览、分支名称、符合规范的PR标题、任何版本升级建议),不创建提交、不推送、不调用
    gh
    命令。退出码为0。
  • --branch=<name>
    ——当在基准分支上存在未提交变更时,覆盖自动推导的分支名称。
  • --issue=<ID>
    ——在自动推导的别名前添加Linear工单ID(例如
    --issue=A-7
    a-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="<符合规范的主题>"
    ——直接设置PR标题,而非自动推导(当自动推导选择了错误类型时的应急方案)。该标题仍需符合Conventional Commits规范(CI会进行lint检查)。
    derive-bump
    仍会运行(其
    category
    会驱动变更日志条目);如果传入的类型与推导的
    type
    /
    releaseTriggering
    不一致,send-it会警告
  • --skip-preflight
    ——完全跳过步骤5的lint检查,打印绕过警告。
  • --ready
    ——将PR标记为就绪状态而非草稿(默认是草稿)。
  • --merge-when-ready
    ——创建/更新PR后,启用
    gh pr merge --auto --squash
  • --worktree=<branch-or-path>
    ——在运行前切换到指定工作区(步骤0)。

Notes

注意事项

  • Prose follows the host repo's language convention. Author the PR title, PR body, and commit messages in the consuming repo's documented prose language. Across this estate that is British English (
    colour
    ,
    behaviour
    ,
    -ise
    /
    -yse
    ); the
    changelog
    skill applies the same rule to the entry it writes. This governs prose only — never identifiers, dependency names, or upstream API field names.
  • Trunk-based: PRs target the base branch (
    config.json
    baseBranch
    , or
    --base
    for this run).
  • send-it bumps only per-bundle versions, never the repo version. The optional Step 6 bundle-version check moves a changed skill's own
    metadata.version
    ; the repo-level npm release stays owned by release-please via the PR title.
  • Idempotent: re-running send-it updates the existing PR title and changelog entry; the Linear writeback skips issues already In Review or beyond.
  • send-it does not bump versions or write any
    CHANGELOG.md
    .
    release-please reads the merged Conventional-Commit PR title, bumps the manifest in the release PR, and the release workflow publishes + tags. send-it only writes the dated
    changelog/<ts>-<slug>.md
    entry (Step 7), finalised at release.
  • 文案遵循宿主仓库的语言约定。 PR标题、PR正文和提交消息的文案遵循目标仓库的文档语言约定。在本环境中为英式英文
    colour
    behaviour
    -ise
    /
    -yse
    );
    changelog
    技能编写条目时也遵循相同规则。仅适用于文案——标识符、依赖名称或上游API字段名称不受此约束。
  • 基于主干开发: PR的目标分支为基准分支(
    config.json
    中的
    baseBranch
    ,或本次运行的
    --base
    参数)。
  • send-it仅升级每个包的版本,绝不升级仓库版本。 步骤6中的可选包版本检查会更新已变更技能的
    metadata.version
    ;仓库级别的npm发布仍由release-please通过PR标题管理。
  • 幂等性: 重新运行send-it会更新现有的PR标题和变更日志条目;Linear状态同步会跳过已处于In Review或更高状态的工单。
  • send-it不升级版本或编写
    CHANGELOG.md
    release-please会读取合并后的符合Conventional Commits规范的PR标题,在发布PR中升级清单版本,发布工作流会完成发布和打标签。send-it仅编写带日期的
    changelog/<ts>-<slug>.md
    条目(步骤7),该条目会在发布时完成最终填充。

Error Handling

错误处理

  • gh auth status
    fails
    — run
    gh auth login
    first; abort until authenticated.
  • changelog validation fails — surface the error; don't auto-fix. The user resolves the entry and re-runs.
  • No commits ahead of the base — exit "No commits ahead of the base branch. Nothing to ship."
  • Branch push fails — verify push access; ensure the remote is configured.
  • PR create/update fails — verify the PR isn't closed; verify the branch is pushed.
  • gh auth status
    失败
    ——先运行
    gh auth login
    ;未认证前终止流程。
  • 变更日志验证失败——提示错误;不自动修复。用户需修复条目后重新运行。
  • 无提交领先于基准分支——退出并提示"No commits ahead of the base branch. Nothing to ship."
  • 分支推送失败——验证推送权限;确保远程仓库已配置。
  • PR创建/更新失败——验证PR未关闭;验证分支已推送。