SH

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.zip

Installs 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 branch
108 chars✓ has a “when” trigger
Intermediate

Key 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

You give it
Feature name and optional `--accept-inconclusive` flag
You get back
Pull Request URL, merged branch, or confirmation of feature kept as-is

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, set ACCEPT_INCONCLUSIVE = true and 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.

  1. If $ARGUMENTS is provided, use it as the feature name
  2. Otherwise, use injected feature state to identify the feature with status done
  3. If no injected state is available, fall back to scanning .planning/features/*/CONTEXT.md
  4. If no candidates, report that no finished features were found

Check INCONCLUSIVE Verdicts

Read .planning/features/{name}/VERIFY.md. Search for any of:

  • status: INCONCLUSIVE in the frontmatter, OR
  • Any row in the Stage 1 table with verdict INCONCLUSIVE.

If found:

  • If ACCEPT_INCONCLUSIVE = false: Display:
    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"
    
    Stop. Do not proceed to Prerequisites.
  • If ACCEPT_INCONCLUSIVE = true: Append the override record to VERIFY.md's ## Inconclusive Override section:
    • 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.

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.

SkillInstallsUpdatedSafetyDifficulty
ship:finish (this skill)02moReviewIntermediate
github-release-management46moReviewAdvanced
agent-release-swarm16moReviewAdvanced
magerun-release15moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry