# Ship It

> Automates the "ship it" workflow - creates a feature branch if on main with uncommitted changes, creates a pull request, watches for CI checks to pass, auto-merges when green, monitors post-merge Actions on main, then cleans up and switches back to main. Use when the user says "ship it".

- Skill: `sanjaysingh/ship-it` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sanjaysingh/ship-it`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sanjaysingh/ship-it/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: sanjaysingh (https://skillmd.com/u/sanjaysingh)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/sanjaysingh/ship-it

---


# Ship It - Automated PR and Merge Workflow

Automates shipping code changes with intelligent branch handling and post-merge monitoring.

## Workflow

```
Task Progress:
- [ ] Step 1: Verify branch state and gather context
- [ ] Step 2: Create feature branch if on main with uncommitted changes
- [ ] Step 3: Push branch to origin
- [ ] Step 4: Create pull request
- [ ] Step 4b: Clean up PR description (remove Cursor attribution)
- [ ] Step 5: Monitor PR CI checks until all pass
- [ ] Step 6: Merge PR when green
- [ ] Step 7: Monitor post-merge Actions on main
- [ ] Step 8: Delete feature branch and sync main
```

## Step 1: Verify Branch State

**Notify user:** "Checking current branch and gathering context..."

Run these commands to understand the current state:

```bash
BRANCH=$(git branch --show-current)
UNCOMMITTED=$(git status --short)
COMMITS_AHEAD=$(git log origin/main..HEAD --oneline | wc -l)
```

**Determine workflow path:**
- **On main with uncommitted changes** → Go to Step 2 (create feature branch, commit changes there)
- **On main, clean working tree, 0 commits ahead** → Nothing to ship. Stop and notify user.
- **On feature branch with uncommitted changes** → Ask user: "Commit changes first or stash them?"
- **On feature branch, clean working tree** → Proceed to Step 3

**Notify user:** "On branch `$BRANCH` with `$COMMITS_AHEAD` commits ahead and uncommitted changes on $UNCOMMITTED files."

## Step 2: Create Feature Branch if on Main with Changes

**CRITICAL: If on main AND there are uncommitted changes, create a feature branch first.**

**Notify user:** "On main with uncommitted changes. Creating feature branch and committing changes there..."

```bash
# Generate branch name from first changed file
FIRST_FILE=$(git status --short | head -1 | awk '{print $2}')
BRANCH_NAME=$(echo "$FIRST_FILE" | sed 's/.*\///' | sed 's/[^a-zA-Z0-9]/-/g' | cut -d'-' -f1-3 | tr '[:upper:]' '[:lower:]' | sed 's/^-*//;s/-*$//')
BRANCH_NAME="${BRANCH_NAME:-feature}-$(date +%s)"

# Create branch and switch
git checkout -b "$BRANCH_NAME"

# Commit all uncommitted changes
git add .
git commit -m "Add $BRANCH_NAME changes"
```

**Notify user:** "Created branch `$BRANCH_NAME` and committed changes. Proceeding with ship workflow..."

## Step 3: Push Branch to Origin

**Notify user:** "Pushing branch to origin..."

```bash
git push -u origin $(git branch --show-current)
```

**Notify user:** "Branch pushed."

## Step 4: Create Pull Request

**Notify user:** "Creating pull request..."

```bash
# Check if PR already exists
PR_EXISTS=$(gh pr view --json number 2>/dev/null && echo "yes" || echo "no")

# If no PR exists, create one
if [ "$PR_EXISTS" = "no" ]; then
  gh pr create --title "$(git log -1 --pretty=%s)" --body "$(cat <<'EOF'
## Summary
Changes from feature branch.

## Test Plan
- [ ] Tests pass
EOF
)"
fi

# Get PR URL
PR_URL=$(gh pr view --json url --jq '.url')
```

**Notify user:** "PR created: $PR_URL"

## Step 4b: Clean Up PR Description

**Notify user:** "Cleaning up PR description..."

Remove any Cursor attribution from the PR body:

```bash
# Get current PR body and remove Cursor attribution lines
CURRENT_BODY=$(gh pr view --json body --jq '.body')
CLEANED_BODY=$(echo "$CURRENT_BODY" | sed '/Made with \[Cursor\]/d' | sed '/^$/N;/^\n$/d')

# Only update if there was a change
if [ "$CURRENT_BODY" != "$CLEANED_BODY" ]; then
  gh pr edit --body "$CLEANED_BODY"
fi
```

**Notify user:** "PR description cleaned."

## Step 5: Monitor PR CI Checks

**Notify user:** "Monitoring CI checks on PR (this may take a few minutes)..."

```bash
PR_NUM=$(gh pr view --json number --jq '.number')
```

Poll until all checks pass:
```bash
while true; do
  sleep 15
  CHECKS=$(gh pr checks $PR_NUM --json name,state,workflow -q '.[] | "\(.name): \(.state)"' 2>/dev/null)
  
  # Check if any failed
  if echo "$CHECKS" | grep -q "FAILURE"; then
    echo "Checks failed: $CHECKS"
    exit 1
  fi
  
  # Check if all passed
  if ! echo "$CHECKS" | grep -q "IN_PROGRESS\|PENDING"; then
    echo "All checks passed"
    break
  fi
  
  echo "Checks running: $(echo "$CHECKS" | grep -c 'IN_PROGRESS') pending..."
done
```

**Stop and report error if any check fails.**

## Step 6: Merge PR When Green

**Notify user:** "All checks passed. Merging PR now..."

Save branch name before merge:
```bash
FEATURE_BRANCH=$(git branch --show-current)
```

Merge:
```bash
gh pr merge --squash --delete-branch
```

**Notify user:** "PR merged. Remote branch deleted."

## Step 7: Monitor Post-Merge Actions on Main

**CRITICAL: After merge, wait for post-merge Actions on main to complete before cleanup.**

**Notify user:** "Monitoring post-merge Actions on main branch..."

Switch to main first to monitor workflows there:
```bash
git checkout main
git fetch origin
```

Poll for post-merge Actions:
```bash
while true; do
  sleep 10
  
  # Get running workflows on main
  RUNNING=$(gh run list --branch main --json name,status --jq '.[] | select(.status == "in_progress" or .status == "queued") | .name')
  
  if [ -z "$RUNNING" ]; then
    echo "No post-merge Actions running"
    break
  fi
  
  echo "Post-merge Actions running: $RUNNING"
done

# Get final status of recent workflows
gh run list --branch main --limit 5 --json name,status,conclusion
```

**Report each workflow's final status:**
- "`<workflow>`: `<conclusion>`"

**Notify user:** "All post-merge Actions completed. Proceeding with cleanup..."

## Step 8: Delete Feature Branch and Sync Main

**Notify user:** "Deleting feature branch and syncing main..."

```bash
# Pull latest main
git pull origin main

# Delete local feature branch
git branch -D "$FEATURE_BRANCH" 2>/dev/null || echo "Branch already deleted"

# Verify clean state
git status
```

**Notify user:** "Done! Deleted branch $FEATURE_BRANCH. Ready for the next change. Current branch: main (up to date)"

## Error Handling

| Scenario | Action |
|----------|--------|
| On main, clean, nothing to ship | Stop: "Nothing to ship. Make some changes first." |
| On main with uncommitted changes | Create feature branch, commit there, proceed |
| Push fails | Report auth error, ask user to check token |
| PR checks fail | Stop, show which check failed |
| Merge blocked by branch protection | Report requirements, do not bypass |
| Post-merge Actions fail | Report failure but proceed with cleanup (code is merged) |
| Timeout waiting | Ask user to continue or abort |

## Success Output

Report when complete:
- PR URL
- PR checks summary
- Post-merge Actions summary (with status for each)
- Merge method used
- Branches deleted (remote via --delete-branch, local via git branch -D)
- Final state: "On main, up to date, ready for next change"

**Final notification:** "Ship complete. You're ready to work on the next change!"

