ship:finish
Completes verified features by automating the PR and merge lifecycle.
Install
mkdir -p .claude/skills/ship-finish && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16230" && unzip -o skill.zip -d .claude/skills/ship-finish && rm skill.zipInstalls to .claude/skills/ship-finish
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when a feature has been verified and needs to be completed — creates PR, merges locally, or keeps branchKey capabilities
- →Parse arguments for feature name and inconclusive acceptance
- →Identify the active feature from injected state or file system
- →Check for inconclusive verdicts in VERIFY.md
- →Run project test suite as a prerequisite
- →Create a Pull Request for the finished feature
- →Merge the feature to the base branch locally
How it works
The skill parses arguments, identifies the active feature, checks for inconclusive verdicts, runs tests, and then presents options to the user for creating a PR, merging locally, or keeping the branch.
Inputs & outputs
When to use ship:finish
- →Merging finished feature branches
- →Creating pull requests
- →Closing out feature development
About this skill
Finish the active feature after verification passes.
Parse Arguments
Parse $ARGUMENTS (a single string) into two components:
--accept-inconclusive "reason"— if this flag appears anywhere in the string, setACCEPT_INCONCLUSIVE = trueand extract the quoted reason text (everything between the matching"s after the flag).- Remaining tokens (after removing the flag + reason) — treat as feature name.
If --accept-inconclusive appears WITHOUT a quoted reason, abort and tell the user: --accept-inconclusive requires a non-empty reason in quotes. Example: /ship:finish my-feature --accept-inconclusive "manually verified end-to-end on staging".
If ACCEPT_INCONCLUSIVE = false, behave as before.
Find Active Feature
Feature state is injected by hooks at session start and after compaction — check conversation context for "SHIP ACTIVE FEATURES" or "SHIP FEATURE STATE" blocks first.
- If
$ARGUMENTSis provided, use it as the feature name - Otherwise, use injected feature state to identify the feature with status
done - If no injected state is available, fall back to scanning
.planning/features/*/CONTEXT.md - If no candidates, report that no finished features were found
Check INCONCLUSIVE Verdicts
Read .planning/features/{name}/VERIFY.md. Search for any of:
status: INCONCLUSIVEin the frontmatter, OR- Any row in the Stage 1 table with verdict
INCONCLUSIVE.
If found:
- If
ACCEPT_INCONCLUSIVE = false: Display:
Stop. Do not proceed to Prerequisites.Cannot finish — VERIFY.md contains INCONCLUSIVE verdicts: {list each INCONCLUSIVE criterion} Options: 1. Add runnable <verify> commands to PLAN.md for the inconclusive criteria, then re-run /ship:verify. 2. Override with: /ship:finish {name} --accept-inconclusive "reason for manual acceptance" - If
ACCEPT_INCONCLUSIVE = true: Append the override record to VERIFY.md's## Inconclusive Overridesection:- Set
Override applied: yes - Set
Reason: {the reason text} - Set
Operator: $(git config user.email || echo unknown) - Set
Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)Then continue to Prerequisites.
- Set
If VERIFY.md has no INCONCLUSIVE markers, proceed directly to Prerequisites.
Prerequisites
Run the project's test suite to confirm everything passes:
# Auto-detect test command from package.json, Cargo.toml, etc.
# If unclear, ask the user for the test command
If tests fail, stop and report failures. Do not proceed.
Present Options
Feature '{name}' is verified and complete.
1. Create a Pull Request (push branch + gh pr create)
2. Merge to {base-branch} locally
3. Keep as-is (I'll handle it later)
Which option?
Use AskUserQuestion to get the user's choice.
Execute Choice
Option 1: Create PR
First, check that gh is available: gh auth status. If it fails, tell the user to install/authenticate gh and abort.
# Detect base branch
git rev-parse --verify main &>/dev/null && echo main || echo master
# Determine PR title type from branch commits
# Look at commit prefixes (feat, fix, refactor, etc.) — use the most common one
# If mixed or unclear, default to "feat"
# Get feature summary from CONTEXT.md for PR body
# Push and create PR
git push -u origin HEAD
gh pr create --title "{type}: {feature-name}" --body "$(cat <<'EOF'
## Summary
{2-3 bullets from CONTEXT.md acceptance criteria}
## Test plan
{key verify commands from PLAN.md}
Built with [Ship](https://github.com/dilhanz/ship)
EOF
)"
Report the PR URL to the user.
Option 2: Merge Locally
# Detect base branch
BASE=$(git rev-parse --verify main &>/dev/null && echo main || echo master)
git checkout $BASE
git merge {feature-branch}
Run tests again on the merged result. If tests pass, report success.
Option 3: Keep As-Is
Report: "Feature '{name}' kept on current branch. Run /ship:finish again when ready."
Do NOT archive the feature directory for this option.
Archive Feature
After Option 1 or Option 2 completes successfully, move the feature directory to the main worktree's archive. Resolve the main worktree root first:
MAIN_ROOT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
mkdir -p "$MAIN_ROOT/.planning/archive"
mv .planning/features/{feature-name} "$MAIN_ROOT/.planning/archive/{feature-name}"
In the main worktree, MAIN_ROOT resolves to the current root — behavior unchanged. From a linked worktree, this moves the record to the main worktree so the audit trail (CONTEXT.md, PLAN.md, VERIFY.md) survives git worktree remove. If git rev-parse fails (not a git repo), fall back to the local .planning/archive/ exactly as before.
Then run pm-update from the main root so its archive check sees the moved directory (mappedStatus runs against its cwd):
cd "$MAIN_ROOT" && node "${CLAUDE_PLUGIN_ROOT}/ship/pm-update.cjs" {feature-name}
This syncs PM state (silent no-op when .project-manager/ is absent — pm-update finds the main root's .project-manager/ itself via the resolver) — archive presence at the main root is what maps the roadmap row to done. It is mechanical only: status cells and the dashboard. Authored .project-manager/ edits are never applied here.
Carry the PM Handoff
Check for PM-HANDOFF.md in the feature directory — at its archived location after Option 1 or 2, or in place under .planning/features/{name}/ after Option 3.
The archive mv above moves the whole feature directory, so on Options 1 and 2 the handoff reaches the main worktree root with the rest of the record and needs no separate step. Do not attempt to apply it: the edits belong to the PM layer, and this skill has no Write or Edit tool by design.
If the handoff exists and its frontmatter reads applied: no, surface it in the report below. On Option 3 (keep as-is), say explicitly that the handoff is still sitting in this worktree — if the lane is later removed without finishing, an unapplied handoff goes with it.
Report
## FEATURE FINISHED
Feature: {name}
Action: {PR created / Merged to main / Kept as-is}
{If PR:} PR: {url}
{If merged:} Branch merged and tests passing
Archived: .planning/archive/{name}
{If an unapplied PM-HANDOFF.md exists:} PM handoff pending: {N} shared .project-manager/ edit(s) at {path} — run /ship:pm apply
$ARGUMENTS
When not to use it
- →When `--accept-inconclusive` is used without a quoted reason
- →When project tests fail before proceeding to options
- →When the feature is not yet verified
Limitations
- →Cannot finish if VERIFY.md contains INCONCLUSIVE verdicts and `ACCEPT_INCONCLUSIVE` is false.
- →If tests fail, the process stops and reports failures.
- →Requires `gh` to be installed and authenticated for creating Pull Requests.
How it compares
This skill automates the finalization of a feature by integrating verification checks, test execution, and options for PR creation or local merging, unlike a manual process that would require separate steps.
Compared to similar skills
ship:finish side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| ship:finish (this skill) | 0 | 2mo | Review | Intermediate |
| github-release-management | 4 | 6mo | Review | Advanced |
| agent-release-swarm | 1 | 6mo | Review | Advanced |
| magerun-release | 1 | 5mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by dilhanz
View all by dilhanz →You might also like
github-release-management
ruvnet
Comprehensive GitHub release orchestration with AI swarm coordination for automated versioning, testing, deployment, and rollback management
agent-release-swarm
ruvnet
Agent skill for release-swarm - invoke with $agent-release-swarm
magerun-release
netz98
Technical release process for n98-magerun2
release
ziggy42
Use this skill when the user wants to cut a new release.
release-branch
mono
Create a release branch for SkiaSharp. Use when user says "release X", "start release X", "create release branch for X", "I want to release", or "release now". This is the FIRST step of releasing - creates branch and pushes to trigger CI. Can auto-detect next preview version from main branch.
release-minor
knuckleswtf
Automates the process of tagging a new minor release for Scribe by analyzing commit messages, updating the changelog, and creating a GitHub release.