# Ship

> Create branch, commit, push, open PR, auto-review, fix issues, and merge — the full ship workflow. Pushes and can merge, so it runs only when invoked explicitly.

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

---


You are automating the complete git workflow to ship code changes. After creating the PR, you will automatically review it, fix any issues, and merge it. The user may provide a branch name and description as arguments: $ARGUMENTS

## Pre-loaded Context

The following git state is injected before Claude processes this prompt. Injection is a
**load-time precondition** — a non-zero exit aborts prompt expansion and the skill never
reaches Claude (ADR-0011 F3), so every command below is guarded to exit 0 everywhere and
reports the sentinel `(not a git repository)` instead of failing.

**Working tree status:**
!`git status -s 2>/dev/null || echo "(not a git repository)"`

**Diff summary (unstaged):**
!`git diff --stat 2>/dev/null || echo "(not a git repository)"`

**Staged diff summary:**
!`git diff --cached --stat 2>/dev/null || echo "(not a git repository)"`

**Current branch:**
!`git branch --show-current 2>/dev/null || echo "(not a git repository)"`

**Remote:**
!`git remote -v 2>/dev/null || echo "(not a git repository)"`

**Diff size (lines changed):**
!`git diff HEAD --shortstat 2>/dev/null | grep -oE '[0-9]+ (insertions?|deletions?)' | awk '{s+=$1} END {print s+0}'`

The diff-size value is always a bare integer. It counts insertions **plus** deletions, and
`git diff HEAD` covers staged **and** unstaged changes, so the number is correct in the normal
pre-ship state where everything is already `git add`-ed. Outside a repository it is `0`.

## Destructive Action Warning

This skill modifies git state (creates branches, commits, pushes, and merges). Before proceeding:
- Confirm the user intends to ship the current changes
- Never force-push or push to main/master directly
- Always create a feature branch for changes
- If the working directory has uncommitted changes that look unrelated, ask before staging everything

## Input Validation

**Optional Arguments:**
- `<branch-name>` - Custom branch name (default: auto-generated from changes)
- `draft` - Create PR as draft
- `--dry-run` - Preview all operations without making any changes
- `--audit` - Log all git and PR operations to `.claude-plugin/audit.log` (see common-patterns.md)

**Dry-Run Mode:**
When `--dry-run` is specified:
- Show what branch name would be created
- Show what files would be staged and committed
- Show the proposed commit message
- Show the PR title and body that would be created
- Prefix all output with `[DRY-RUN]` to clearly indicate preview mode
- Do NOT execute any git commands that modify state (checkout, add, commit, push, pr create)

**Audit Mode:**
When `--audit` is specified:
- Log every git and PR operation to `.claude-plugin/audit.log`
- Each log entry is a JSON line with: timestamp, command, action, details, success
- Create `.claude-plugin/` directory if it doesn't exist
- Append to log file (never overwrite existing entries)

Example log entries:
```json
{"timestamp": "2026-01-14T10:30:00Z", "command": "ship", "action": "git_checkout", "details": {"branch": "feat/new-feature"}, "success": true}
{"timestamp": "2026-01-14T10:30:01Z", "command": "ship", "action": "git_commit", "details": {"sha": "abc1234", "message": "feat: add new feature"}, "success": true}
{"timestamp": "2026-01-14T10:30:02Z", "command": "ship", "action": "git_push", "details": {"remote": "origin", "branch": "feat/new-feature"}, "success": true}
{"timestamp": "2026-01-14T10:30:03Z", "command": "ship", "action": "pr_create", "details": {"number": 42, "url": "https://github.com/..."}, "success": true}
{"timestamp": "2026-01-14T10:30:30Z", "command": "ship", "action": "pr_merge", "details": {"number": 42, "strategy": "squash"}, "success": true}
```

## Pre-flight Checks

Use the pre-loaded context injected above — do NOT re-run these git commands:

1. **Verify git repository** — if any injected value above is the literal sentinel `(not a git repository)`, abort with: `ship must be run inside a git repository.` Do NOT infer this from empty output: empty is a legitimate result for several of these commands inside a valid repo.
2. **Verify a remote is configured** — if the injected **Remote** block is empty (and not the sentinel), the repository is valid but has no remote. Abort with: `No git remote configured. Run: git remote add origin <url>` — there is nothing to push to and Phase 0 cannot detect the platform.
3. **Confirm uncommitted changes** — check the injected `git status -s` and diff summaries; if both are empty, abort with a clear message
4. **Confirm current branch** — check the injected branch name; if not `main`, ask the user if they want to proceed from the current branch or abort
5. **Diff size gate** — the injected "Diff size (lines changed)" value is an integer. If it is greater than 500, note this for Phase 6 (will suggest `/code-review ultra` instead of standard review)

## Phase 0: Platform Detection

Detect the git hosting platform and select the appropriate CLI:

1. Parse the injected `git remote -v` output (pre-loaded above) for the push remote URL — do NOT re-run the command
2. **If URL contains `github.com`** → set `PLATFORM=github`
   - Verify `gh auth status` succeeds
   - If `gh` is not installed or not authenticated, abort with install/auth instructions
3. **Otherwise** → set `PLATFORM=gitea`
   - Verify `tea login list` shows a login matching the remote URL's host
   - If `tea` is not installed, abort with: `Install tea CLI: https://gitea.com/gitea/tea`
   - If no matching login exists, abort with: `Run: tea login add --url <host-url> --name <name> --token <token>`
4. Store the platform choice for all subsequent steps
5. Display: `Platform detected: [GitHub|Gitea] (using [gh|tea] CLI)`

**Draft PR limitation:** The `tea` CLI does not support `--draft` for PR creation. When the `draft` argument is passed on a Gitea repo, warn the user that draft PRs are not supported on Gitea and create a normal PR instead.

## Execution Steps

### Phase 1: Determine Branch Name
- If the user provided a branch name in arguments, use it
- If not, analyze the staged/unstaged changes and generate a descriptive kebab-case branch name (e.g., `fix-login-validation`, `add-user-export-feature`)
- Confirm the branch name with the user before proceeding

### Phase 2: Create and Switch to New Branch
```bash
git checkout -b <branch-name>
```

### Phase 3: Documentation Gate, Stage, and Commit

#### 3.1: Documentation Gate

Before staging changes, verify project documentation is current. **LAB_NOTEBOOK.md is a hard gate** — do not proceed to staging until notebook requirements are met.

**LAB_NOTEBOOK.md (Hard Gate):**

1. Check if `LAB_NOTEBOOK.md` exists in the project root
2. If it does NOT exist — skip this gate entirely
3. If it exists:
   a. Read the project's `CLAUDE.md` AND the system-level `~/.claude/CLAUDE.md` for lab notebook rules — the project's CLAUDE.md contains the "Lab Notebook — MANDATORY Logging Protocol" section with the project's logging rules, entry template, and project-specific tags; the system CLAUDE.md may contain additional constraints
   b. Read the latest entries in `LAB_NOTEBOOK.md` (the Experiment Log section)
   c. Analyze the current changes using the pre-loaded diff summaries injected at the top of this prompt (do NOT re-run `git diff` or `git diff --cached` — use the injected output)
   d. Determine if there's a current entry covering the changes being shipped:
      - An entry dated today whose objective relates to the changes
      - An IN PROGRESS entry that covers the current work
   e. **If no current entry exists** — create one:
      - Add a new entry following the notebook's template (next sequential Entry number)
      - Include: Date, Environment (from recent entries or system context), Objective (derived from the changes), Status: COMPLETE
      - Summarize what was done in Actions & Results
      - For Hypothesis: reconstruct from the changes — what was the expected outcome?
      - For Rollback Plan: reference the git SHA before the changes (`git rev-parse HEAD`)
      - Update the Decision Log and Action Items tables at the top if applicable
   f. **If a current entry exists but is IN PROGRESS** — close it out:
      - Update the entry: add final results, set Status to COMPLETE
      - Fill in Duration if empty
      - Update What Worked / What Failed based on outcomes
   g. Notebook updates will be included in the commit at step 3.2

**Other Documentation (Soft Warnings — non-blocking):**

Scan for documentation that may need attention. Report but do NOT halt:
- `CHANGELOG.md` — warn if it exists and changes include new features or breaking changes
- `README.md` — warn if changes add new user-facing features, commands, or endpoints

Display results before proceeding:
```text
Documentation Gate:
  LAB_NOTEBOOK.md: [Updated entry E{NNN} | Created entry E{NNN} | Not found (skipped) | Current (no update needed)]
  Warnings: [list, or "None"]
```

#### 3.2: Stage and Commit

- Stage all changes (including any notebook updates from 3.1): `git add -A`
- Analyze the diff to generate a clear, conventional commit message
- Format: `<type>: <concise description>` (e.g., `feat: add CSV export for user data`)
- Include a body if changes are complex enough to warrant explanation
- Show the user the proposed commit message and proceed unless they object

### Phase 4: Push to Remote
```bash
git push -u origin <branch-name>
```

### Phase 5: Create Pull Request
- Set the PR title to match or expand on the commit message
- Generate a PR body that includes:
  - Summary of what changed and why
  - Key files modified
  - Any testing notes if apparent from the changes
- Target branch: `main`

**GitHub:**
```bash
gh pr create --title "..." --body "..." [--draft]
```

**Gitea:**
```bash
tea pr create --title "..." --description "..."
# Note: --draft is not supported by tea CLI
# If user requested draft, warn and create normal PR
```

- Open the PR as a draft if the user included "draft" in their arguments (GitHub only)

## Output
After completion, display:
- Branch name created
- Commit SHA
- Direct link to the PR

## Error Handling
- If any git command fails, stop immediately and show the error
- If push fails due to remote rejection, suggest possible causes
- If PR creation fails, provide the manual `gh pr create` or `tea pr create` command the user can run

---

## Phase 6: Auto-Review

After the PR is created, automatically analyze it for issues.

### 6.1 Fetch PR Information

**GitHub:**
```bash
# Get the PR number from the just-created PR
PR_NUMBER=$(gh pr view --json number -q '.number')

# Fetch the diff for analysis
gh pr diff $PR_NUMBER
```

**Gitea:**
```bash
# Get the PR number (parse from tea pr create output, or list open PRs)
PR_NUMBER=$(tea pr list --output json --fields index --state open | jq '.[0].index')

# Fetch the diff for analysis
tea pr view $PR_NUMBER --fields diff --output simple
```

### 6.2 Analyze the PR

Perform a comprehensive review across these dimensions:

**Security Analysis (CRITICAL/WARNING)**
- Hardcoded secrets, API keys, or credentials
- SQL injection vulnerabilities
- XSS vulnerabilities
- Insecure dependencies
- Missing input validation
- Authentication/authorization issues
- Sensitive data exposure in logs

**Performance Analysis (WARNING)**
- N+1 query patterns
- Unbounded loops or recursion
- Large file reads into memory
- Missing pagination
- Inefficient algorithms (O(n²) when O(n) possible)
- Blocking operations in async contexts

**Code Quality Analysis (WARNING)**
- DRY violations (copy-paste code)
- Functions/methods over 50 lines
- High cyclomatic complexity
- Inconsistent naming conventions
- Missing error handling
- Magic numbers without constants
- Dead code or unused imports

**Test Coverage Analysis (WARNING)**
- New code paths without tests
- Modified code with outdated tests
- Missing edge case coverage

**Documentation Analysis (WARNING for public APIs)**
- Public APIs without documentation
- Complex logic without comments
- Missing README updates for new features

### 6.3 Classify Issues

Categorize each issue by severity:
- **CRITICAL**: Must fix - blocks merge (security vulnerabilities, data integrity risks)
- **WARNING**: Should fix - blocks merge (quality issues, missing tests)
- **SUGGESTION**: Nice to have - does NOT block merge

### 6.4 Output

**Large-diff gate:** If the injected "Diff size (lines changed)" value from pre-flight is > 500, display this recommendation before the standard review output and skip the automated fix loop — route to `/code-review ultra` instead:

```text
Phase 6: Large Diff Detected
==============================
This PR changes [N] lines (>500). Standard auto-review may miss subtle issues.

Recommendation: Run `/code-review ultra` for multi-agent deep review on this PR.
  - /code-review ultra provides more thorough analysis for large changes
  - Auto-fix loop skipped for large diffs (too many moving parts for reliable auto-fix)

PR URL: [url] (open — awaiting manual review)
Branch: [branch-name] (preserved)
```

For diffs ≤ 500 lines, display the standard review output:

```text
Phase 6: Auto-Review
====================
PR #[number] analyzed.

Issues Found:
  Critical: [N] (must fix)
  Warnings: [N] (should fix)
  Suggestions: [N] (non-blocking)

[If no blocking issues]
✓ All checks passed! Proceeding to merge.

[If blocking issues exist]
Found [N] blocking issues. Starting fix loop.
```

---

## Phase 7: Fix Loop

If there are CRITICAL or WARNING issues, attempt to fix them automatically.

### 7.1 Loop Parameters

- **Maximum attempts**: 5
- **Blocking issues**: CRITICAL + WARNING only (suggestions don't block)
- **Exit conditions**:
  - All blocking issues resolved → proceed to merge
  - Unfixable issue detected → report and stop
  - Max attempts reached → report exhaustion and stop

### 7.2 Fix Loop Logic

- Attempt a fix for every blocking issue; mark unfixable ones per 7.4
- Any unfixable issue → exit immediately to the Phase 8 failure report (no commit that attempt)
- Otherwise commit the fixes, push, and re-analyze the PR
- All blocking issues resolved → exit to the Phase 8 success path; else continue to the next attempt
- 5 attempts exhausted with blockers still remaining → exit to the Phase 8 exhaustion report

Full pseudocode walkthrough: see the Fix Loop Pseudocode template in `references/ship-output-templates.md`.

### 7.3 Fix Strategies by Issue Type

| Issue Type | Fix Approach |
|------------|--------------|
| Hardcoded secrets | Replace with `process.env.VAR_NAME` reference |
| SQL injection | Convert to parameterized query |
| XSS vulnerability | Add sanitization/escaping |
| N+1 queries | Add eager loading or batching |
| Long functions | Split into smaller focused functions |
| Missing error handling | Add try/catch with appropriate handling |
| Magic numbers | Extract to named constants |
| Missing tests | Generate basic test stubs |
| Missing docs | Generate JSDoc/docstring comments |

### 7.4 Unfixable Issue Detection

Mark an issue as "unfixable" if:
- **Requires external changes**: Database schema, external API, infrastructure
- **Architectural decision needed**: Multiple valid approaches, needs human choice
- **Cyclical issue**: Same fix keeps being undone or introduces new issues
- **Beyond scope**: Would require changes outside the PR's files
- **False positive**: Code is actually correct, analysis was wrong

When unfixable:
```yaml
Unfixable: [ID] [title]
Reason: [why it cannot be auto-fixed]
Suggestion: [what the user should do manually]
```

---

## Phase 8: Completion

Three possible outcomes: success (merge), failure (unfixable), or exhaustion (max attempts).

### 8.1 Success Path (All Blocking Issues Resolved)

Execute merge:
```bash
# --- GitHub ---
gh pr merge $PR_NUMBER --squash --delete-branch

# --- Gitea ---
tea pr merge $PR_NUMBER --style squash
git push origin --delete $BRANCH_NAME   # tea doesn't auto-delete the branch

# --- Both platforms ---
git checkout main
git pull

# Clean up local branch if it exists
git branch -d $BRANCH_NAME 2>/dev/null || true
```

Prune stale branches:
```bash
# Fetch and prune remote tracking branches that no longer exist on remote
git remote prune origin

# Find local branches where upstream is gone and delete them (merged only)
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d 2>/dev/null
```

**Note:** Only fully merged branches are deleted (`-d` not `-D`). Unmerged branches with deleted remotes are preserved and reported as warnings.

Emit the completion summary using the Phase 8.1 Success template in `references/ship-output-templates.md`.

### 8.2 Failure Path (Unfixable Issues Exist)

Do NOT merge. Report to the user using the Phase 8.2 Failure template in `references/ship-output-templates.md`.

### 8.3 Exhaustion Path (Max Attempts Reached)

Do NOT merge. Report diagnostics to the user using the Phase 8.3 Exhaustion template in `references/ship-output-templates.md`.

---

## Performance

| Scenario | Expected Duration |
|----------|-------------------|
| Clean PR, no issues | 30-60 seconds (branch, commit, push, PR create, review, merge) |
| PR with fixable issues | 1-3 minutes (includes fix loop iterations) |
| PR with CI checks | 2-10 minutes (depends on CI pipeline duration) |
| Max fix loop (5 attempts) | 3-5 minutes (excludes CI wait time) |

Duration scales with the number of changed files (affects review time) and CI pipeline configuration. Gitea operations may be slightly slower than GitHub due to CLI differences.

## Summary of Workflow

| Phase | Action | Outcome |
|-------|--------|---------|
| Pre-flight | Verify git, gh/tea, changes | Ready to ship |
| Phase 0 | Detect platform (GitHub/Gitea) | CLI selected |
| Phase 1 | Determine branch name | Branch name confirmed |
| Phase 2 | Create branch | On new branch |
| Phase 3.1 | Documentation gate | LAB_NOTEBOOK.md updated (if present), doc warnings reported |
| Phase 3.2 | Stage and commit | Changes committed (including doc updates) |
| Phase 4 | Push to remote | Branch pushed |
| Phase 5 | Create PR | PR opened |
| Phase 6 | Auto-review PR | Issues identified |
| Phase 7 | Fix loop (up to 5x) | Issues fixed or marked unfixable |
| Phase 8 | Complete | Merged, branches pruned, or failure report |

