npx skills add ...
npx skills add rheged-studio/agent-skills --skill send-it
The all-in-one ship pipeline — commit uncommitted work, run the change-gated lint preflight, author the dated changelog entry, compose a Conventional Commits PR title, push, open or update a PR, move linked Linear issues to In Review, then chain into `triage-pr` (Step 11) to drive the PR to merge-ready. Incomplete until triage-pr reaches a terminal outcome (its Step 13 report, its human envelope, or a documented stop), or `--skip-triage` / `triage: false` was used with a stated reason. Use when asked to ship, send it, finish a branch, open or update a PR, or wrap up and push. Thin orchestrator over `commit`, `preflight`, `changelog`, `linear-sync`, `pr`, and `triage-pr`; owns branch guard, release-type decision, PR title, push, and PR. Serves monorepos and single-package repos alike.
npx skills add rheged-studio/agent-skills --skill 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 (CI + humans; under the dual
merge policy, feature PRs land as merge commits and release-please ranks the
landed commit subjects for the bump — A-1176 / A-824), 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 skill (classify in-scope vs out-of-scope, atomic
Conventional Commits, out-of-scope guard).preflight skill (change-gated; no-ops when nothing
lint-relevant changed).changelog skill (author/update + validate; an entry for
every PR, skipped entirely only when config.json sets changelog: false).pr skill (Summary visual, Changes, Evidence, Merge Danger
with the release note, Related Issues, and the hand-edited keep region).linear-sync skill (resolve state by team name,
idempotent transition).triage-pr skill (Phase A CI fix loop and the
promote-on-proven-green flip, then Phase B review dispositions — ending on its
human envelope when humanEnvelope is true, or on its Step 13 report when
false — Step 11, A-1151).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,../pr/SKILL.md,../linear-sync/SKILL.md,../triage-pr/SKILL.md), so a--skill send-it-only install leaves the commit, lint, changelog, PR-body, Linear, and triage steps unavailable and those links dangling. Install them together:
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.
Done criteria. Opening or updating the PR (Step 9) and moving Linear issues to In Review (Step 10) are mid-pipeline — not the end of
/send-it. The run is incomplete until Step 11'striage-prreaches a terminal outcome — its Step 13 report, its human envelope (humanEnvelope: true), or a documented stop (Phase-A early stop, hard blocker, budget exhaustion) — or you printed an explicit skip/degraded line with a stated reason (ℹ️ triage chain skipped …for--skip-triage/triage: false, or⚠️ triage-pr not installed …when the sibling is absent). Reporting a draft PR URL as the final outcome without one of those lines is a failed run.
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):
| Key | Meaning | Default |
|---|---|---|
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) |
triage (omit or true by default) | Whether the run chains into the triage-pr skill once the PR is open (Step 11) — the CI fix loop, the promote-on-proven-green flip, then Phase B through triage-pr's human envelope (humanEnvelope: true) or its Step 13 report (false). The key may be omitted (defaults to true); the step is not optional on a default run. Set false only in repos that deliberately stop at the open PR, or where triage-pr isn't installed. --skip-triage does the same for a single run (A-1151) — always state why. | true |
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. ThechangelogScopeknob (added in 0.4.0) is gone (A-600); only thechangelog: true|falsemaster switch remains.
gh CLI installed and authenticated (gh auth status).commit, preflight, changelog, pr) installed.
Without pr, Step 9 writes the minimal fallback body instead.linear-sync — optional; without it (or the Linear MCP server) the In Review
writeback is skipped silently (Step 10).triage-pr — required for the default pipeline. Without it the Step 11 chain
warns and the run finishes at the open PR — that soft-skip is a degraded
outcome, not a successful finish. The two siblings behave differently on purpose: a
skipped Linear writeback changes nothing about the PR, whereas a skipped triage
chain leaves work undone.--worktree= is set)If --worktree=<branch-or-path> was passed, resolve and cd into that worktree
before any other step runs. Skip this step otherwise.
Run git worktree list --porcelain to list worktrees with their paths and
branches.
Resolve the argument:
/): match against the worktree <path>
field.branch refs/heads/<name> field.No match — exit immediately with: No worktree found for <arg>. Available: <comma-separated paths>.
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.
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:
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.
git branch --show-current.baseBranch from config.json; default main):
git status --porcelain. If clean, exit with: "Nothing to ship from the
base branch. Create a feature branch first."git diff and git diff --cached) and the changed file
paths.add-readme-section, fix-config-typo.--branch=<name> — use as-is.--issue=<ID> — use <ID>-<slug> lower-cased (e.g.
a-7-as-acquired), matching Linear's gitBranchName.<slug> (no wip/ prefix).origin, append -2,
-3, … until unused.git checkout -b <branch> to move the working tree onto it.<branch> and continuing."package.json driftedSkip this step if no package.json was touched on the branch.
git diff --name-only origin/<base>...HEAD | grep -E '(^|/)package\.json$'. If
empty, skip.
Run pnpm install --frozen-lockfile. If it succeeds, the lockfile is already in
sync — continue.
If it fails, run pnpm install to update the lockfile.
If the lockfile changed, stage and commit it before any other commits go in:
This keeps CI's --frozen-lockfile install green. (Skip silently in repos that
don't use pnpm.)
commit skillsend-it is the all-in-one ship pipeline: 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; headers
follow that skill's ≤72-character rule, including send-it's own later
git commit -m lines). 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).
If git log origin/<base>..HEAD is empty, exit with: "No commits ahead of the base
branch. Nothing to ship."
preflight skill
--skip-preflightbypasses 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:
Act on its exit-code contract, reading .preflight-summary.json to interpret a
non-zero exit:
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.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.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.
Versioning is driven by release-please reading Conventional Commits. The estate uses a dual merge policy (A-1176 / ADR-0005):
allow_merge_commit and allow_squash_merge stay enabled (A-1177) — squash
is not disabled.send-it still composes a correct Conventional Commits PR title (CI's PR-title
lint + humans; the changelog-completeness gate still keys off a release-triggering
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.)
Derive the slug, body, type, and category from the branch commits via the bundled helper (zero-dep — no tsx):
It prints JSON:
{ "slug", "bump", "body", "type", "breaking", "category", "releaseTriggering" }:
type — the dominant Conventional-Commit type across all branch commits
(feat/fix/perf/docs/refactor/chore/ci/… — A-387); this is the
PR-title prefix. Merge commits are excluded from the scan (git log --no-merges).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; dominant feat: → minor; else patch). Ignored
when releaseTriggering is false.(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), add it to the release note
(below) 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.
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:
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. That
subject must stay ≤72 characters (the type, scope, colon, space, and rest of
the first line).
Compose the PR title as a single Conventional Commits subject — CI's
PR-title lint and the changelog-completeness gate still require it. For feature
PRs (merge commits), the post-merge bump comes from the landed commit
subjects (A-824); the title remains the human/CI declaration and should match
the dominant type. For squash paths (release + fan-out), the squash subject
is still the bump declaration. 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, or
if the supplied title exceeds 72 characters).
Otherwise build it straight from the derived fields:
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.releaseTriggering: true) → the prefix is already a
release type (feat/fix/perf, or any !). Add the scope; that's it.releaseTriggering: false) → the prefix is a non-release type
(docs/refactor/chore/ci/build/test/style).type, optional (scope), optional !,
:, then the subject — must be ≤ 72 characters (Git's usual subject
ceiling; the same cap as the commit skill). If type + optional scope +
body would overflow, rewrite until it fits: prefer dropping an optional
scope before losing meaning. Do not ellipsis-truncate mid-word, and never
drop the type prefix.⚠️ Keep the title honest with the commits. A mistyped prefix misleads reviewers and the completeness gate — a
feat:on a docs-only branch, or achore:on a real fix. For feature PRs the post-merge bump follows the landed commit subjects; for squash paths the title is the declaration. Derive the title from the change's semantic category (the commit types) so they stay aligned.
Compose the release note that Step 9 hands to the PR body's **Release:**
line: no release (<type>-only) when releaseTriggering is false (so reviewers
can confirm the non-release type was intentional), or <type> → <bump> when it is
true — followed in either case by any publish-surface cross-check note from
item 2.
changelog skillDisabled entirely? If
config.jsonsetschangelog: false, skip Steps 7 and 8 completely — author nothing, run nochangelogscripts, make nodocs(changelog)commit — and note "changelog step disabled (no changelog flow in this repo)" in the run summary. This is for repos with nochangelog/directory and nochangelogskill installed; the category decision from Step 6 still drives the PR title. Whenchangelogis unset ortrue, always author an entry (thechangelogScopeknob 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 noversion.changelog: falseis the only thing that suppresses authoring.
Follow the changelog skill to author or update the entry:
Detect an existing entry for this branch (by the branch frontmatter field) →
update vs create. On update, preserve the filename and created_at.
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, stats)
and version as blank placeholders — the post-merge enricher fills 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 post-merge enricher resolves it
from the entry's branch:.
Run the enrichment scripts: node skills/changelog/scripts/set-affected-packages.mjs
then node skills/changelog/scripts/add-links.mjs.
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.
--dry-runwrites nothing from here on. Steps 8–11 are the mutating half of the run. Under--dry-run, print what each would do and perform none of it: nogit commit, nogit push, nogh pr create/gh pr edit, no Linear transition (pass--dry-rundown tolinear-syncsosave_issueis never called), and--dry-runon the Step 11 hand-off. A dry run may read —gh pr view, the triage-pr preview — but it never writes. Then exit 0.
If a changelog/ entry was written in Step 7 (i.e. changelog is not false), commit
only that file:
<one-line summary> must keep the entire header (type, scope, colon, space,
and summary) at 72 characters or under — rewrite the summary if it would
overflow. Do not wrap the header onto a second line.
Then push the branch:
<title> is the Conventional Commits PR title from Step 6 — set it on both
create and update (re-derive it every run so it stays in sync with the branch's
commits). Feature PRs are intended to merge via merge commit; release and
fan-out automation keep using squash outside this skill.
gh pr view --json number,url 2>/dev/null.<body-file>, and pass it with --body-file — a carried keep region can hold
backticks or $(...). Assign the title in single quotes (each embedded '
written as '\''), e.g. title='feat: add $(x) support', and pass "$title",
so command substitution never runs on a commit subject or --title value.gh pr create --base <base> --draft --title "$title" --body-file <body-file>. Use --ready (the flag) instead of --draft if the
user passed --ready.gh pr edit <number> --title "$title" --body-file <body-file>.gh pr view --json url,number.send-it never arms auto-merge. It opens and updates the PR; landing it stays a human action (A-1151). The old
--merge-when-readyflag — which armedgh pr merge --auto --mergehere — is gone as of 0.8.0: from Step 11 onward, a run can be sitting at triage-pr's disposition envelope, and an armed auto-merge could land the branch while that plan is still awaiting approval. Merge by hand, or arm auto-merge yourself once you're happy with the PR.
PR body — follow the pr skill (A-2429). It owns the body's
shape: Summary visual, Changes, Evidence, Merge Danger, Related Issues, and the
<!-- pr:keep --> region. Look for it beside this bundle (../pr/SKILL.md) or in
the consumer's skill mirrors (.claude/skills/pr/SKILL.md,
.agents/skills/pr/SKILL.md), and write the body by following it. Hand it:
Base — the resolved <base> (honouring --base), so the body describes
the right range on a stacked PR.
Release note — the Step 6 note (no release (<type>-only) or
<type> → <bump>, plus any publish-surface cross-check), for its **Release:**
line.
Related issues — the issue identifiers from the branch name and commits, for
## Related Issues (dropped when there are none).
Evidence — only what this run actually executed (the Step 5 preflight result, any tests run in the session). CI has not run yet, so the body claims no CI result.
On update, carry the keep region across. Before gh pr edit, read the
current body (gh pr view <number> --json body -q .body) and copy every
<!-- pr:keep --> … <!-- /pr:keep --> region verbatim into the regenerated
body, exactly as the pr skill describes. Hand-added screenshots and notes
survive every re-run this way. This applies to the fallback body too.
Fallback PR body — only when pr is not installed:
Drop ## Related Issues when no issues were found.
linear-sync skillFollow 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.
triage-pr skillCompletion gate. Do not treat the Step 9 PR URL as the final report and stop. Steps 9–10 are mid-pipeline. Continue into this step unless the opt-out in sub-step 1 applies, or sub-step 2 finds
triage-prmissing (and you print the corresponding skip/degraded line with a reason). A draft-only report withoutℹ️ triage chain skipped …or⚠️ triage-pr not installed …is a failed/send-itrun (A-1645).
send-it opens the PR; triage-pr takes it the rest of the
way (A-1151). This step is part of the run — not an optional extra. Hand-off alone
does not finish the run — follow triage-pr through to a terminal outcome (Step 13
report, human envelope when humanEnvelope is true, or a documented stop). This step
runs after Step 10 so linked issues are already In Review before triage begins.
Check the opt-out first — before anything else in this step. If --skip-triage
was passed, or config.json sets triage: false, print
ℹ️ triage chain skipped (--skip-triage): <reason> — or (triage: false) — report
the PR URL, and stop the run here. Do not run the install check, and do not
start the cold-start poll: a skipped chain must cost nothing. That is the pre-0.8.0
behaviour.
Don't reach for it to finish sooner. The opt-out exists for the cases where the chain genuinely cannot work, not as a shortcut, and it is never the default:
triage: trueships inconfig.example.json, andrheged-skills-setupwritestruewhen reconciling a consumer. Skipping leaves the PR un-triaged — red CI unfixed, bot findings unread — which is the state this step exists to prevent, so treat it the way Step 5 treats--skip-preflight: say why in the report. The legitimate reasons are narrow: the PR changes the chain itself, so the running prose and the prose on disk disagree (this bundle's own ship runs); CI is gated ondraft == false, so a draft never registers a check (prefertriage: falsein that repo's config over a per-run flag); or the user asked to stop at the open PR. A missingtriage-prneeds no flag — sub-step 2 handles it.
Confirm triage-pr is installed — look for ../triage-pr/SKILL.md beside this
bundle. If it is absent, print
and finish the run. That warning is the degraded-outcome line the completion
gate accepts (alongside ℹ️ triage chain skipped …) — the run may stop, but it
must not report as a successful default finish. A missing sibling warns, never
fails the process exit — louder than Step 10's silent linear-sync skip,
because a skipped triage chain leaves PR work undone.
Wait for CI to register — the cold-start gate. Step 9 created or updated the PR
moments ago, so GitHub Actions may not have registered a single check yet. An empty
statusCheckRollup handed to a cold triage-pr reads as "nothing failing", and with
promoteOnGreen on (its default) that would flip the draft to ready before CI ever
ran. triage-pr's "no failures yet is not green" rule guards its own watch loop, not
a cold entry — so send-it proves at least one check exists before handing off. Poll
every 10 seconds for up to 3 minutes, in a single shell loop (not 18
separate calls — a foreground sleep between tool calls is slow and some harnesses
refuse it). Stay quiet while polling; no interim "still waiting" pings:
Capture gh's exit status separately from the count — a failed call returns an
empty string, and treating that as "zero checks" would silently convert an auth or
API error into a full-window wait and a bogus "CI never started" verdict:
Run the loop the way the host can sustain it (the same pattern as triage-pr's
Waiting on long operations).
On Claude Code, a bare foreground sleep is blocked, so run the whole loop as
a background command (run_in_background) and continue when the harness
reports it has exited — or use the Monitor tool with an until-loop on the same
check-count condition. On Cursor, run it as the single bounded command shown
(three minutes is well inside the terminal timeout). Read the loop's exit status
and output the same way in every host.
gh itself fails → stop and surface the error (authentication, rate limit, a
deleted PR). Do not fall through to the no-checks branch: an unverifiable
state is not the same as a verified-empty one, and only the latter is safe to
hand off.0 when the window expires → CI never started for this PR (a repo with
no workflows, a paths-filtered or draft == false-gated workflow this PR doesn't
match, or a stalled Actions queue). Report
⚠️ no checks registered within 3 minutes — handing off with --no-promote and add
--no-promote to the hand-off below, so an empty rollup can never be read as a
proven green and flip the draft to ready. Nothing else about the chain changes.Hand off. If send-it was run with --dry-run, --dry-run goes on this
command too — always. A live triage-pr commits, pushes, and can flip the draft
to ready, so a dry run that omits it stops being a dry run. Follow the
triage-pr skill against the PR from Step 9, naming its
number explicitly so it never re-resolves to a different PR:
Forward --dry-run, --ci-only, --no-promote, and --auto-apply verbatim
when they were passed to send-it, plus --no-promote when sub-step 3's cold-start
gate added it — that one is a safety flag this step owns, not a user flag, and
dropping it would let an unverified rollup promote a draft. Add nothing beyond
those. triage-pr reads its own
config.json (promoteOnGreen, humanEnvelope, reviewBots, maxCiRounds, …) —
send-it configures nothing about it, exactly as it configures nothing about
commit, preflight, changelog, or linear-sync.
Run the full triage-pr skill — do not stop between its phases. Halt only where
triage-pr halts (including its human envelope when humanEnvelope is true — do
not answer on the user's behalf). See triage-pr for
Phase A/B behaviour.
Report once. triage-pr's own final report is the run's report — prepend
send-it's line items (branch, PR URL, changelog entry, Linear transitions) to it
rather than emitting a second, competing summary. Respect triage-pr's quiet rule
(A-1178): no interim pings around the hand-off.
--dry-runchains intotriage-pr --dry-run. When a PR already exists for the branch, hand off totriage-pr <number> --dry-runso the preview covers the failing checks and unresolved findings too. When no PR exists — a dry run creates none — printno PR to triage yetand exit 0. A dry run therefore makes read-onlyghcalls; it still writes nothing, commits nothing, and pushes nothing.Re-runs are safe. A second
/send-itre-enters triage-pr against the same PR; seetriage-pr.
--dry-run — print what would be written/submitted (changelog preview, branch,
conventional PR title, any version-bump proposals), make no commits and no push. It
chains into triage-pr --dry-run when a PR already exists for the branch (Step 11),
so it makes read-only gh calls but still writes nothing. 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, or if the supplied title exceeds 72 characters.--skip-preflight — skip the Step 5 lint gate entirely, printing a bypass warning.--skip-triage — end the run at the open PR: skip the Step 11 triage-pr chain
(identical to config.json triage: false). Restores the pre-0.8.0
bounded-finisher behaviour for one run. Not a shortcut — it leaves the PR
un-triaged; see Step 11 for the narrow cases where it applies, and state the reason
in the report.--ci-only — forwarded verbatim to triage-pr (Step 11): run its Phase A and stop
at green, never promoting the draft. No effect on send-it's own steps.--no-promote — forwarded verbatim to triage-pr: never flip the draft to ready;
stop at green. send-it also adds this itself when the cold-start gate times out.
No effect on send-it's own steps. (--promote is deliberately not forwarded —
promotion is already triage-pr's default.)--auto-apply — forwarded verbatim to triage-pr: skip its Phase B human envelope
and run its unattended disposition path. It can apply findings that meet the
unattended fix-now rubric and, when Linear follow-up capture is configured,
create eligible follow-ups without a human or Linear-only approval gate.
No effect on send-it's own steps.--ready — open the PR ready-for-review instead of draft (default is draft).--worktree=<branch-or-path> — cd into a worktree before running (Step 0).--merge-when-ready was removed in 0.8.0. send-it no longer arms
gh pr merge --auto --merge; see the Step 9 callout.
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./commit subjects, send-it's own git commit -m lines (lockfile,
bundle bump, changelog), and the derived PR title — counts the entire header
(type, optional scope, optional !, :, subject). Rewrite a too-long header;
do not wrap it into the body to dodge the cap. --title stays verbatim but
warns when it exceeds 72. Body and footer wrapping are out of scope here.config.json baseBranch, or
--base for this run).metadata.version; the
repo-level npm release stays owned by release-please (feature PRs: landed commit
subjects; squash paths: squash subject / PR title).triage-pr (Step 11), so a single
/send-it can stay unattended for roughly 30 minutes — CI fix rounds plus the
review wait — and ends on triage-pr's disposition envelope when humanEnvelope is
true, or on its Step 13 report when humanEnvelope is false. That is a
deliberate shift in what the command is. --skip-triage, or
triage: false, restores the old shape — only with a stated reason.if: github.event.pull_request.draft == false registers zero checks until the PR is ready — the Step 11 cold-start gate
then waits its full 3 minutes every run and hands off with --no-promote to a
triage-pr with nothing to do. Use --ready, or set triage: false, in those repos.triage-pr against the same PR, whose follow-up-pending markers
(A-679) keep already-dispositioned findings out of the envelope.CHANGELOG.md. release-please
ranks Conventional Commits on trunk after merge (merge-commit history for feature
PRs; squash subject for release/fan-out), 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 post-merge by the in-repo enricher.gh auth status fails — run gh auth login first; abort until authenticated.triage-pr isn't installed — warn and finish at the open PR (Step 11). A
missing sibling never fails a send-it run.--no-promote
and say why. An empty statusCheckRollup must never be read as green./triage-pr <number> directly rather than re-running the whole of send-it.