Ship
Open a PR and merge it in one step — combines `bin/pr.sh` create with `bin/wait-for-pr-checks.sh` polling and `gh pr merge`. Default mode enables GitHub's native auto-merge so the terminal returns immediately; `--client-side` polls in the foreground and merges when CI goes green. TRIGGER when the user says "ship this PR", "ship it", "open and merge", "PR and merge", "create PR and auto-merge it", "wait for CI then merge", "block until merged", "/nyann:ship". Do NOT trigger on "open a PR" alone (no merge intent) — that's the `pr` skill. Do NOT trigger on "merge this existing PR #N" — ship is PR-creation + merge, not standalone merge of an already-open PR. Do NOT trigger on "release X.Y.Z" — that's the `release` skill, which tags a merged commit rather than creating one.From its SKILL.md
npx -y skills add thettwe/nyann --skill shipAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.2 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
ship
Wraps bin/ship.sh. The script composes bin/pr.sh (PR creation),
bin/wait-for-pr-checks.sh (poll), and gh pr merge (merge) into a
single ShipResult. The output schema is at
schemas/ship-result.schema.json.
0. Drift check (quick, non-blocking)
Run bash bin/session-check.sh --flow=ship. If it produces output,
surface the line to the user verbatim. Do not block the flow.
1. Pick the mode up front
Two modes, decided at invocation. They have very different terminal behavior, so confirm with the user when it isn't obvious which they want.
| Mode | Default | Terminal behavior | Use when |
|---|---|---|---|
auto-merge | yes | returns ~instantly with outcome:"queued" | repo allows auto-merge, user wants to walk away |
client-side (--client-side) | opt-in | blocks until ship-or-fail | repo doesn't allow auto-merge, or user wants the wait surfaced |
If the user says "ship it and let me know when it's in" or "block
until merged", use --client-side. If they say "queue it for merge"
or "ship and I'll come back later", use the default.
When the user's intent doesn't clearly map to either mode, you MUST
call the AskUserQuestion tool (not plain text):
{
"questions": [
{
"question": "How should this PR be merged after CI passes?",
"header": "Ship mode",
"multiSelect": false,
"options": [
{ "label": "Auto-merge (Recommended)", "description": "Returns immediately; GitHub merges when checks pass" },
{ "label": "Client-side", "description": "Blocks here until CI is green, then merges" }
]
}
]
}
2. Pre-flight (same as pr skill)
bin/ship.shruns the same gh guard asbin/pr.sh. Skip records with reasongh-not-installedorgh-not-authenticatedshort-circuit before any PR is created. Relay these as-is.- The current branch must not be
main/master. The underlyingpr.shenforces this; if it dies, route the user to/nyann:branchfirst. - Synthesize the title + body the same way the
prskill does:bin/pr.sh --target <cwd> --context-onlyfirst, then build a Conventional-Commits title and a Summary/Test-plan markdown body.
Pre-action guards
bash bin/pre-action-guard.sh --flow ship --target <cwd> [--profile <resolved-profile.json>]
By default ship runs the PR-flow guards (branch-pushed, wip-commits). A
guards.ship array REPLACES that default set (subset semantics — it does
not append) and may promote severities.
coverage-delta is an opt-in guard — off by default. Since
guards.ship replaces the default set, enable it by re-listing the
built-ins alongside it:
"guards":{"ship":[{"name":"branch-pushed"},{"name":"wip-commits"},{"name":"coverage-delta"}]}.
Naming only coverage-delta would silently drop them. It warns (advisory
by default; promote to confirm via the profile) when the branch lowers
test coverage versus the cached .nyann/coverage-baseline.json, tolerant
up to guards.coverage_delta_threshold percentage points (default 0). It
reuses an existing CI coverage artifact and soft-skips when none is
present (it never runs the suite), so it's best paired with a profile/CI
that already produces coverage. Refresh the baseline on the base branch /
post-merge with bash bin/guards/coverage-delta.sh --update-baseline.
| Exit | Meaning | Action |
|---|---|---|
| 0 | Pass | Continue |
| 3 | Critical guard failed | Refuse to ship. The user has to fix the underlying issue or pass --skip-guards. |
| 4 | Confirm-severity guard failed | AskUserQuestion: "Proceed with ship?" — explicit confirm required. |
3. Confirm before shipping
Show the user the proposed title, body, and mode before invoking.
- Auto-merge: "Ship it via auto-merge? (returns immediately; GitHub merges when checks pass)"
- Client-side: "Block here while CI runs and merge when green?
Default timeout is 30 minutes (polling every 30 seconds).
Override with
--timeout/--intervalif your CI is slower."
Skip the confirmation only when the user said "just ship it" / "don't ask, ship it now".
4. Invoke
bin/ship.sh \
--target <cwd> \
--title "<conventional-title>" \
--body "<markdown-body>" \
[--base <branch>] \
[--draft] \
[--client-side] \
[--merge-strategy squash|rebase|merge] \
[--timeout <sec>] [--interval <sec>] \
[--allow-no-checks]
--draft opens as draft (auto-merge will wait for ready-for-review +
checks; client-side mode merges as draft if you pass --draft, which
is rarely what you want). --merge-strategy defaults to squash;
respect the user's preference if they name a different one. --timeout
and --interval are forwarded to the wait phase in client-side mode
(defaults: 30 min / 30 s).
--allow-no-checks is a safety opt-out for client-side mode. By
default, the script refuses to merge when the waiter reports
no-checks — that outcome on a fresh PR almost always means
"workflows haven't attached yet" rather than "this repo has no PR
CI", so silently merging would defeat the gate. Pass
--allow-no-checks only when you know the repo runs no PR-side
checks (no GitHub Actions, no required reviews, etc.) and the empty
state is intentional. Auto-merge mode doesn't need this flag —
GitHub's server-side auto-merge already handles required-checks
state correctly.
5. Interpret the outcome
Branch on .outcome:
| Outcome | Mode | What happened | What the user should do |
|---|---|---|---|
queued | auto-merge | GitHub's auto-merge enabled; merge happens server-side when checks + reviews pass | trust it, walk away |
shipped | client-side | merge call succeeded after green CI | done; PR is merged + branch deleted |
ci-failed | client-side | one or more checks failed OR the PR had no checks attached without --allow-no-checks; loop bailed | inspect ci_failed_reason in the JSON; for the no-checks case re-run with --allow-no-checks if the empty state is intentional, otherwise wait for workflows to attach and retry |
ci-timeout | client-side | wait deadline hit, some still in_progress | retry with --timeout higher, or investigate runners |
merge-failed | either | auto-merge couldn't be enabled (e.g. branch protection) or gh pr merge failed | inspect merge_failed_reason; common causes: required reviews missing, branch protection forbids the strategy |
pr-failed | either | bin/pr.sh died before producing a URL | rerun /nyann:pr to surface the underlying error |
skipped | either | gh missing/unauthed; nothing was created | tell the user; can't proceed |
The script always exits 0. Branch on the .outcome field in the
JSON to determine what happened.
6. Handling errors
merge-failedwith reason like "auto-merge is not allowed" → suggest retrying with--client-side, which polls then callsgh pr mergedirectly (works even when server-side auto-merge is disabled).merge-failedwith reason like "required reviews" → relay verbatim; the user needs a reviewer to approve before merge can proceed.ci-failed→ don't suggest re-running ship; route the user to fix the failing job first. Mention they can check it viagh pr checks <num>directly.
When to hand off
- "Just open the PR" / "don't merge yet" →
prskill. - "Wait for CI but don't merge" →
wait-for-pr-checksskill. - "Tag a release after merge" →
releaseskill — invoke after ship reportsshippedor after the auto-merge actually lands.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.