---
description: Version sync, clean dist, build for PyPI deployment
---
# /ship — Prepare and ship a release

> **Synonymes** : ship, release, publish, deploy, version bump

Pre-flight checks + version sync + clean build for PyPI deployment.

## Input

The user may provide:
- A **bump type**: `patch`, `minor`, or `major`
- An **explicit version string**: e.g. `2.4.0`
- **Nothing**: default to `patch` bump

## Steps

### 1. Version audit + auto-bump

Read ALL version sources:
- `pyproject.toml` → `version`
- `package/godot-plugin/plugin.cfg` → `version`
- `README.md` → `**vX.Y.Z**`

Flag any file that is **already different from the others** (desync warning).

**Compute the target version:**
- Take the current version from `pyproject.toml` as reference (format `MAJOR.MINOR.PATCH`)
- If user gave an explicit version → use it
- If user gave a bump type or nothing:
  - `patch` (default): `X.Y.Z` → `X.Y.(Z+1)`
  - `minor`: `X.Y.Z` → `X.(Y+1).0`
  - `major`: `X.Y.Z` → `(X+1).0.0`

Display the comparison table:

| File | Current | Target |
|------|---------|--------|
| `pyproject.toml` | $CURRENT | $NEW_VERSION |
| `plugin.cfg` | ? | $NEW_VERSION |
| `README.md` | ? | $NEW_VERSION |

### 2. Version bump

For each file where the version differs from the target:

- **`pyproject.toml`** line `version = "X.Y.Z"` → replace with `version = "$NEW_VERSION"`
- **`package/godot-plugin/plugin.cfg`** line `version="X.Y.Z"` → replace with `version="$NEW_VERSION"`
- **`README.md`** line `**vX.Y.Z**` → replace with `**v$NEW_VERSION**`

After editing, re-read all three files to **confirm** the version is correct. Display the confirmation table.

### 3. PyPI collision check

Before building, verify the target version doesn't already exist on PyPI:

```bash
pip index versions golem-mcp 2>/dev/null | head -1
```

If `$NEW_VERSION` appears in the list of published versions → the version is **already published**. Bump again (patch) until you find an unpublished version. Display a warning:

```
⚠ v$NEW_VERSION already exists on PyPI — bumping to v$NEXT_VERSION
```

Re-apply the version bump (step 2) with the new target. This prevents the 400 "File already exists" error on `uv publish`.

### 4. Clean dist/

```bash
rm -rf dist/*
```

Confirm the `dist/` directory is empty (but keep the directory and its `.gitignore`).

### 5. Build

```bash
uv build
```

Verify the output filenames contain the correct version:
- `dist/golem_mcp-$NEW_VERSION.tar.gz`
- `dist/golem_mcp-$NEW_VERSION-py3-none-any.whl`

If the filenames don't match the target version, STOP and flag the issue.

### 6. Wheel content verification

Inspect the wheel to confirm `package/` content was correctly bundled:

```bash
python3 -m zipfile -l dist/golem_mcp-$NEW_VERSION-py3-none-any.whl | grep golem_mcp_data
```

For each subdirectory of `package/`, count the source files and verify the wheel contains exactly the same number:

```bash
for d in commands skills rules hooks tools recipes references godot-plugin; do
  src=$(find package/$d -type f | wc -l)
  whl=$(python3 -m zipfile -l dist/golem_mcp-*.whl | grep "golem_mcp_data/$d/" | wc -l)
  echo "$d: source=$src wheel=$whl $([ "$src" = "$whl" ] && echo OK || echo MISMATCH)"
done
```

If any count mismatches → STOP. Likely cause: `pyproject.toml` `[tool.hatch.build.targets.wheel.force-include]` paths are stale.

Also verify no symlinks leaked into the wheel (`.claude/rules/` symlinks should NOT be included — only `package/rules/` real files).

### 7. Summary

Display:

```
## Ship $NEW_VERSION — Ready

Version synced ($NEW_VERSION):
  ✓ pyproject.toml
  ✓ package/godot-plugin/plugin.cfg
  ✓ README.md

Build artifacts:
  ✓ dist/golem_mcp-$NEW_VERSION.tar.gz
  ✓ dist/golem_mcp-$NEW_VERSION-py3-none-any.whl

Next steps:
  1. git add -A && git commit -m "Bump version to $NEW_VERSION"
  2. uv publish (or twine upload dist/*)
  3. git tag v$NEW_VERSION && git push --tags
```

Do NOT auto-commit or auto-publish. Just present the next steps.

## Rules

- **Never skip the audit** — always show the comparison table first, even if all versions already match
- **Never leave dist/ dirty** — old artifacts cause PyPI upload conflicts
- **Never auto-publish** — the user decides when to push
- If version argument looks wrong (not semver-like), ask for confirmation

## See also

- `/checkpoint` — snapshot avant le ship
- `/learn` — capturer les leçons de la release
