Release Package
Release a plainx package with version bumping, changelog generation, and git tagging.
Arguments
/plainx-release [--major|--minor|--patch]
- No args: analyze commits and prompt for version type
--major: auto-select major release
--minor: auto-select minor release
--patch: auto-select patch release
Scripts
| Script |
Purpose |
get-package-info |
Get package metadata and commits since last release |
Workflow
Phase 1: Check Preconditions
Check git status is clean:
git status --porcelain
If not clean, stop and ask user to commit or stash changes.
Phase 2: Get Package Info
uv run ./.claude/skills/plainx-release/get-package-info
This outputs JSON with:
name: Package name from pyproject.toml
current_version: Current version
changelog_path: Path to CHANGELOG.md
last_tag: Most recent git tag for this package
repo_url: GitHub URL (extracted from pyproject.toml)
commits: List of commits since last tag (excluding tests)
If no commits since last release, inform the user and stop.
Phase 2b: First Release Detection
If current_version is 0.0.0, this is the first release:
- Inform the user: "This package has never been released (version 0.0.0)."
- Ask what version to release:
- 0.1.0 - First development release (recommended for most packages)
- 1.0.0 - First stable release (if the package is already production-ready)
- Skip to Phase 4 with the chosen version (use
uv version <version> to set it directly)
Phase 3: Collect Release Decision
- Display the commits since last release
- Analyze commits and suggest release type:
- Major: breaking changes, major API redesigns, significant removals
- Minor: new features, significant additions, new APIs
- Patch: small bugfixes, minor tweaks, documentation updates, refactors
- Ask user to confirm or adjust (major/minor/patch/skip)
- If
--major, --minor, or --patch was passed, auto-select that type
- If user chooses to skip, stop
Phase 4: Set Version
For first releases (from 0.0.0):
uv version <version>
Where <version> is the chosen version like 0.1.0 or 1.0.0.
For subsequent releases:
uv version --bump <type>
Where <type> is major, minor, or patch.
Phase 5: Generate Release Notes
Get the new version:
uv version --short
Get the file changes since the last release:
git diff <last_tag>..HEAD -- . ":(exclude)tests"
If no last_tag, use the initial commit or recent history.
Read the existing <changelog_path> file.
Prepend a new release entry to the changelog.
For first releases:
## [<new_version>](<repo_url>/releases/v<new_version>) (<today's date>)
Initial release.
- Brief summary of what the package provides
- Key features or capabilities
For subsequent releases:
## [<new_version>](<repo_url>/releases/v<new_version>) (<today's date>)
### What's changed
- Summarize user-facing changes based on the actual diff (not just commit messages)
- Include commit hash links: ([abc1234](<repo_url>/commit/abc1234))
- Skip test changes, internal refactors that don't affect public API
### Upgrade instructions
- Specific steps if any API changed
- If no changes required: "- No changes required."
Phase 6: Commit, Tag, and Push
Guide the user through these steps explicitly:
Stage files:
git add pyproject.toml <changelog_path> uv.lock
Show what will be committed:
git diff --cached --stat
Create commit:
git commit -m "Release v<new_version>"
Create tag:
git tag -a v<new_version> -m "Release v<new_version>"
GitHub Workflow Setup (first release only):
Check if .github/workflows/release.yml exists. If not, ask:
"No release workflow found. Would you like to set up GitHub Actions to publish to PyPI when you push a tag?"
If yes, copy .claude/skills/plainx-release/release-workflow.yml to .github/workflows/release.yml, then amend the commit:
git add .github/workflows/release.yml
git commit --amend --no-edit
git tag -fa v<new_version> -m "Release v<new_version>"
Remind the user to configure PyPI trusted publishing after pushing:
Push commit and tag:
git push && git push --tags
Ask user to confirm before each destructive step (commit, push).
Phase 7: GitHub Release (Optional)
After pushing, wait for the GitHub workflow to publish to PyPI. Once published, ask the user if they want to create a GitHub release:
gh release create v<new_version> --notes "<changelog entry summary>"
Or to use the full changelog entry from the file, extract and pass it manually.
Release Type Guidelines
Consider the current version when suggesting release types:
Pre-1.0 packages (0.x.y)
Most packages stay pre-1.0 for a long time. For these packages:
- Minor (0.x.0): New features, breaking changes, new APIs, significant additions
- Patch (0.0.x): Bugfixes, minor tweaks, documentation, refactors
- Major (1.0.0): Only suggest if the user explicitly wants to mark the package as stable/production-ready
Post-1.0 packages (x.y.z where x >= 1)
Once a package has reached 1.0, follow semver strictly:
- Major (x.0.0): Breaking changes, API removals, incompatible changes
- Minor (x.y.0): New features, new APIs, backwards-compatible additions
- Patch (x.y.z): Bugfixes, minor tweaks, documentation, refactors
Commit message indicators
- Breaking/major indicators: "breaking", "remove", "rename API", "redesign", "incompatible"
- Feature/minor indicators: "add", "new", "feature", "implement"
- Fix/patch indicators: "fix", "bugfix", "typo", "docs", "refactor", "update"
1---2name: plainx-release3description: Releases plainx packages with version suggestions, changelog generation, and git tagging. Use when releasing a package to PyPI.4---56# Release Package78Release a plainx package with version bumping, changelog generation, and git tagging.910## Arguments1112```13/plainx-release [--major|--minor|--patch]14```1516- No args: analyze commits and prompt for version type17- `--major`: auto-select major release18- `--minor`: auto-select minor release19- `--patch`: auto-select patch release2021## Scripts2223| Script | Purpose |24| ------------------ | --------------------------------------------------- |25| `get-package-info` | Get package metadata and commits since last release |2627## Workflow2829### Phase 1: Check Preconditions30311. Check git status is clean:3233 ```34 git status --porcelain35 ```3637 If not clean, stop and ask user to commit or stash changes.3839### Phase 2: Get Package Info4041```42uv run ./.claude/skills/plainx-release/get-package-info43```4445This outputs JSON with:4647- `name`: Package name from pyproject.toml48- `current_version`: Current version49- `changelog_path`: Path to CHANGELOG.md50- `last_tag`: Most recent git tag for this package51- `repo_url`: GitHub URL (extracted from pyproject.toml)52- `commits`: List of commits since last tag (excluding tests)5354If no commits since last release, inform the user and stop.5556### Phase 2b: First Release Detection5758If `current_version` is `0.0.0`, this is the **first release**:59601. Inform the user: "This package has never been released (version 0.0.0)."612. Ask what version to release:62 - **0.1.0** - First development release (recommended for most packages)63 - **1.0.0** - First stable release (if the package is already production-ready)643. Skip to Phase 4 with the chosen version (use `uv version <version>` to set it directly)6566### Phase 3: Collect Release Decision67681. Display the commits since last release692. **Analyze commits and suggest release type**:70 - **Major**: breaking changes, major API redesigns, significant removals71 - **Minor**: new features, significant additions, new APIs72 - **Patch**: small bugfixes, minor tweaks, documentation updates, refactors733. Ask user to confirm or adjust (major/minor/patch/skip)74 - If `--major`, `--minor`, or `--patch` was passed, auto-select that type75 - If user chooses to skip, stop7677### Phase 4: Set Version7879For first releases (from 0.0.0):8081```82uv version <version>83```8485Where `<version>` is the chosen version like `0.1.0` or `1.0.0`.8687For subsequent releases:8889```90uv version --bump <type>91```9293Where `<type>` is `major`, `minor`, or `patch`.9495### Phase 5: Generate Release Notes96971. Get the new version:9899 ```100 uv version --short101 ```1021032. Get the file changes since the last release:104105 ```106 git diff <last_tag>..HEAD -- . ":(exclude)tests"107 ```108109 If no `last_tag`, use the initial commit or recent history.1101113. Read the existing `<changelog_path>` file.1121134. Prepend a new release entry to the changelog.114115**For first releases:**116117```118## [<new_version>](<repo_url>/releases/v<new_version>) (<today's date>)119120Initial release.121122- Brief summary of what the package provides123- Key features or capabilities124```125126**For subsequent releases:**127128```129## [<new_version>](<repo_url>/releases/v<new_version>) (<today's date>)130131### What's changed132133- Summarize user-facing changes based on the actual diff (not just commit messages)134- Include commit hash links: ([abc1234](<repo_url>/commit/abc1234))135- Skip test changes, internal refactors that don't affect public API136137### Upgrade instructions138139- Specific steps if any API changed140- If no changes required: "- No changes required."141```142143### Phase 6: Commit, Tag, and Push144145Guide the user through these steps explicitly:1461471. **Stage files**:148149 ```150 git add pyproject.toml <changelog_path> uv.lock151 ```1521532. **Show what will be committed**:154155 ```156 git diff --cached --stat157 ```1581593. **Create commit**:160161 ```162 git commit -m "Release v<new_version>"163 ```1641654. **Create tag**:166167 ```168 git tag -a v<new_version> -m "Release v<new_version>"169 ```1701715. **GitHub Workflow Setup (first release only)**:172173 Check if `.github/workflows/release.yml` exists. If not, ask:174175 > "No release workflow found. Would you like to set up GitHub Actions to publish to PyPI when you push a tag?"176177 If yes, copy `.claude/skills/plainx-release/release-workflow.yml` to `.github/workflows/release.yml`, then amend the commit:178179 ```180 git add .github/workflows/release.yml181 git commit --amend --no-edit182 git tag -fa v<new_version> -m "Release v<new_version>"183 ```184185 Remind the user to configure PyPI trusted publishing after pushing:186 - Go to https://pypi.org/manage/account/publishing/187 - Add trusted publisher: GitHub owner, repo name, workflow "release.yml" (no environment needed)1881896. **Push commit and tag**:190 ```191 git push && git push --tags192 ```193194Ask user to confirm before each destructive step (commit, push).195196### Phase 7: GitHub Release (Optional)197198After pushing, wait for the GitHub workflow to publish to PyPI. Once published, ask the user if they want to create a GitHub release:199200```201gh release create v<new_version> --notes "<changelog entry summary>"202```203204Or to use the full changelog entry from the file, extract and pass it manually.205206## Release Type Guidelines207208Consider the current version when suggesting release types:209210### Pre-1.0 packages (0.x.y)211212Most packages stay pre-1.0 for a long time. For these packages:213214- **Minor (0.x.0)**: New features, breaking changes, new APIs, significant additions215- **Patch (0.0.x)**: Bugfixes, minor tweaks, documentation, refactors216- **Major (1.0.0)**: Only suggest if the user explicitly wants to mark the package as stable/production-ready217218### Post-1.0 packages (x.y.z where x >= 1)219220Once a package has reached 1.0, follow semver strictly:221222- **Major (x.0.0)**: Breaking changes, API removals, incompatible changes223- **Minor (x.y.0)**: New features, new APIs, backwards-compatible additions224- **Patch (x.y.z)**: Bugfixes, minor tweaks, documentation, refactors225226### Commit message indicators227228- Breaking/major indicators: "breaking", "remove", "rename API", "redesign", "incompatible"229- Feature/minor indicators: "add", "new", "feature", "implement"230- Fix/patch indicators: "fix", "bugfix", "typo", "docs", "refactor", "update"