/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
Claude Code
$ npx -y skills add thettwe/nyann --skill ship --agent claude-code
Codex
$ npx skills add thettwe/nyann --skill ship -a codex -y
Cursor
$ npx skills add thettwe/nyann --skill ship -a cursor -y
Gemini CLI
$ npx skills add thettwe/nyann --skill ship -a gemini-cli -y
GitHub Copilot
$ npx skills add thettwe/nyann --skill ship -a github-copilot -y
OpenCode
$ npx skills add thettwe/nyann --skill ship -a opencode -y
Windsurf
$ npx skills add thettwe/nyann --skill ship -a windsurf -y
SKILL.md
ship.SKILL.mdname: ship
description: >
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.
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.sh` runs the same gh guard as `bin/pr.sh`. Skip records
with reason `gh-not-installed` or `gh-not-authenticated` short-circuit before any PR is created. Relay these as-is.
- The current branch must not be `main`/`master`. The underlying
`pr.sh` enforces this; if it dies, route the user to `/nyann:branch` first.
- Synthesize the title + body the same way the `pr` skill does:
`bin/pr.sh --target <cwd> --context-only` first, 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` / `--interval` if 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 che
Read more
name: ship description: > 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.
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.sh` runs the same gh guard as `bin/pr.sh`. Skip records
with reason `gh-not-installed` or `gh-not-authenticated` short-circuit before any PR is created. Relay these as-is.
- The current branch must not be `main`/`master`. The underlying
`pr.sh` enforces this; if it dies, route the user to `/nyann:branch` first.
- Synthesize the title + body the same way the `pr` skill does:
`bin/pr.sh --target <cwd> --context-only` first, 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` / `--interval` if 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 che
ငြမ်း is Burmese for scaffolding. Nyann is the Claude Code plugin that picks expert git defaults for your stack — branching, working hooks (Husky / pre-commit.com / lefthook), commits, releases, CI, docs — then keeps the repo on those rails through every PR
Other skills on nyann.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about ship.
- Which coding agents can use ship?
- ship installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

