--- name: ship description: "Create a release PR to main with version bump and draft a GitHub release" argument-hint: "[optional version number or extra notes]" --- Create a pull request from the current working branch to `main`, ensure the version is bumped, and draft a GitHub release. The user will review, merge, and publish manually. ## Usage - `/ship` — auto-detect versioning scheme and walk through the process - `/ship 2.5.0` — use a specific version - `/ship patch` — shorthand for semver bump type ## Process ### Step 1: Preflight checks Run these commands in parallel to understand the current state: ```bash git status git branch --show-current git log main..HEAD --oneline gh auth status ``` Verify: - There are no uncommitted changes. If there are, warn the user and stop. - The current branch is NOT `main`. If it is, warn the user and stop. - There is at least one commit ahead of `main`. If not, inform the user there is nothing to ship. - `gh` CLI is authenticated. If not, inform the user and stop. ### Step 2: Collect all changes since main git log main..HEAD --pretty=format:"%h %s (by @%an)" --no-merges Also gather PR numbers if commits came from merged PRs: git log main..HEAD --pretty=format:"%h %s" --no-merges For each commit, check if it is associated with a PR: gh api repos/{owner}/{repo}/commits/{sha}/pulls --jq '.[0].number' 2>/dev/null Build a list of all changes with their authors and references (commit hash or PR number). ### Step 3: Check version and detect versioning scheme Read the current version from `package.json`: node -p "require('./package.json').version" Detect the versioning scheme: | Scheme | Pattern | Examples | |--------|---------|---------| | **Semver** | `MAJOR.MINOR.PATCH` | `2.5.0`, `1.0.3`, `0.9.1` | | **Calendar versioning** | Contains year/month/date-like segments | `2026.3.18`, `25.03.1` | **If `$ARGUMENTS` provides a version or bump type**, use it: - Exact version (e.g., `2.5.0`) — use as-is - Bump type (`major`, `minor`, `patch`) — calculate the next version from current **If `$ARGUMENTS` does not specify a version**, check if the version was already bumped compared to `main`: git show main:package.json | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version" - If the version is **the same** as `main`, a bump is required — ask the user (see below). - If the version is **already different** from `main`: - **Semver**: the version is already bumped. Use the current version and skip to Step 4. - **Calendar versioning**: the version may be stale (bumped days or weeks ago). Always ask the user to confirm — see the calver prompt below. **For semver** (when bump is needed), present the options: > Current version: `2.4.1` > > What version bump do you need? > - **patch** -> `2.4.2` (bug fixes, small changes) > - **minor** -> `2.5.0` (new features, backward compatible) > - **major** -> `3.0.0` (breaking changes) **For calendar versioning** (always ask, even if already different from main): > Current version: `2025.12.3` > Today's date version: `2026.3.18` (Recommended) > Use today's date as the version, keep current (`2025.12.3`), or enter a custom one? If the user picks today's date, update the version. If they choose to keep the current version, skip the bump. If they enter a custom version, use that. ### Step 4: Apply version bump If a version bump is needed (version same as main, or calver and user chose a new version): Update `package.json` with the new version. Use a precise edit — only change the `"version"` field. Then re-install dependencies to update the lock file: ni Commit the version bump: git add package.json # Also stage the lock file (package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb — whichever exists) git add package-lock.json pnpm-lock.yaml yarn.lock bun.lockb 2>/dev/null git commit -m "Bump version to " Push the branch: git push -u origin $(git branch --show-current) ### Step 5: Create the PR to main Build the PR title using the version and a summary of changes: **Title format**: `v - ` The short summary should capture the main themes from the changes (features, fixes, optimizations). Keep it under 72 characters total. Examples: - `v2.5.0 - Add dark mode, fix cart calculation, optimize image loading` - `v2026.3.18 - Redesign settings page, update dependencies` Build the PR body by grouping changes into categories: ```markdown ## What's in this release ### New features - Add dark mode toggle (#45) - Add user onboarding flow (#52) ### Bug fixes - Fix cart total when discount applied (#48) - Fix null pointer in auth middleware (a1b2c3d) ### Improvements - Optimize image loading for product pages (#50) - Refactor payment utils for reusability (#51) ### Other - Update dependencies (d4e5f6g) ``` **Grouping rules:** - Scan each change's commit message or PR title to determine the category - Features: new functionality, "add", "implement", "introduce" - Bug fixes: "fix", "resolve", "correct", "patch" - Improvements: "optimize", "refactor", "improve", "update", "enhance", "clean up" - Other: anything that does not fit the above (config, docs, chore, deps) - Skip empty categories - Each item references either `#` or `` depending on what is available Create the PR: gh pr create \ --base main \ --head "$(git branch --show-current)" \ --title "v - " \ --body "$(cat <<'EOF' EOF )" ### Step 6: Draft a GitHub release Create a draft release targeting `main` with tag `v`. Build the release body. List all changes, each in this format: by @ in # or if no PR exists for that change: by @ at **Rules for the release body:** - Use simple English, no emojis - Remove duplicate entries (same change appearing in multiple commits) - Skip trivial changes (typo fixes, formatting-only, merge commits) - Group by category (same groups as the PR body) if there are more than 10 items - If 10 or fewer items, a flat list is fine - Each line starts with a capital letter - Add a "New Contributors" section at the end if there are first-time contributors to the repo (check with `gh api`) gh release create "v" \ --target main \ --title "v" \ --draft \ --notes "$(cat <<'EOF' ## What's Changed - Add dark mode toggle by @hta218 in #45 - Fix cart total calculation when discount is applied by @dev2 in #48 - Optimize image loading for product pages by @hta218 in #50 - Update dependencies by @hta218 at a1b2c3d **Full Changelog**: https://github.com///compare/v...v EOF )" Always append a "Full Changelog" link at the bottom comparing the previous version tag to the new version. ### Step 7: Summary Output a final summary: Ship complete! Version: v PR: Release: (draft) Next steps: 1. Review the PR: 2. Review the release: 3. Merge the PR 4. Publish the release ## Error Handling - If `gh` CLI is not installed or not authenticated, inform the user and stop - If there are uncommitted changes, warn and stop — do not auto-commit - If already on `main`, warn the user and stop - If no commits ahead of `main`, inform the user there is nothing to ship - If `package.json` does not exist, skip version bump logic and ask the user for a version string to use in the PR title and release tag - If `ni` fails, show the error and ask the user if they want to continue without updating the lock file - If PR creation fails (e.g., PR already exists), show the error and provide the URL to the existing PR - If release creation fails, show the error but still report the PR as successful - If push fails, show the error and let the user resolve it ## Important Notes - This command does NOT merge the PR or publish the release — the user does that manually - The release is always created as a **draft** - The PR targets `main` unless the user specifies otherwise via arguments - Use `ni` (not `npm install` or `pnpm install`) to re-install — it auto-detects the package manager - Do NOT create a git tag locally — the tag is created by GitHub when the release is published - Keep the PR title concise but descriptive — it becomes the merge commit message ## User Arguments $ARGUMENTS