home / skills / dropseed / plain / release

release skill

/.claude/skills/release

This skill releases Python packages to PyPI with intelligent version suggestions, changelog generation, and git tagging to streamline releases.

npx playbooks add skill dropseed/plain --skill release

Review the files below or copy the command above to add this skill to your agents.

Files (5)
SKILL.md
5.0 KB
---
name: release
description: Releases Plain packages with intelligent version suggestions and parallel release notes generation. Use when releasing packages to PyPI.
---

# Release Packages

Release Plain packages with version bumping, changelog generation, and git tagging.

## Arguments

```
/release [packages...] [--major|--minor|--patch] [--force]
```

- No args: discover all packages with changes, prompt for each
- Package names: only release specified packages
- `--major`: auto-select major release for all packages with changes
- `--minor`: auto-select minor release for all packages with changes
- `--patch`: auto-select patch release for all packages with changes
- `--force`: ignore dirty git status

## Scripts

All mechanical operations are handled by scripts in this skill directory:

| Script             | Purpose                                                         |
| ------------------ | --------------------------------------------------------------- |
| `discover-changes` | Find packages with unreleased commits (outputs JSON)            |
| `bump-versions`    | Bump package versions (`<package>:<type> ...`)                  |
| `commit-and-push`  | Format, sync, commit, tag, and push (`<package>:<version> ...`) |
| `add-hunks`        | Stage specific uv.lock hunks by grep pattern (used internally)  |

## Workflow

### Phase 1: Check Preconditions

1. Check git status is clean (unless `--force`):

    ```
    git status --porcelain
    ```

    If not clean, stop and ask user to commit or stash changes.

### Phase 2: Discover Packages with Changes

```
./.claude/skills/release/discover-changes
```

This outputs JSON with each package's name, current version, and commits since last release.
If specific packages were requested, filter the results to only those packages.

### Phase 2b: First Release Detection

For any package with `current_version` of `0.0.0`:

1. Inform the user: "Package X has never been released (version 0.0.0)."
2. Ask what version to release:
    - **0.1.0** - First development release (recommended)
    - **1.0.0** - First stable release
3. Use `uv version <version>` in the package directory to set the version directly (instead of bump)

### Phase 3: Collect Release Decisions

For each package with changes:

1. Display the commits since last version change
2. **Analyze commits and suggest release type**:
    - **Minor**: new features, breaking changes, significant additions, new APIs
    - **Patch**: small bugfixes, minor tweaks, documentation updates, refactors
3. Ask user to confirm or adjust (minor/patch/skip)
    - If `--minor` or `--patch` was passed, auto-select that type
    - Default to skip if user just presses Enter

### Phase 4: Bump Versions

```
./.claude/skills/release/bump-versions <package>:<type> [<package>:<type> ...]
```

Example: `./.claude/skills/release/bump-versions plain-admin:patch plain-dev:minor`

### Phase 5: Generate Release Notes

For each package to release, sequentially:

1. Get the file changes since the last release:

    ```
    git diff <last_tag>..HEAD -- <name> ":(exclude)<name>/tests"
    ```

2. Read the existing `<changelog_path>` file.

3. Prepend a new release entry to the changelog with this format:

```
## [<new_version>](https://github.com/dropseed/plain/releases/<name>@<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](https://github.com/dropseed/plain/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

```
./.claude/skills/release/commit-and-push <package>:<version> [<package>:<version> ...]
```

This script handles everything: `uv sync`, `./scripts/fix`, staging files, committing each package separately, tagging, and pushing. Sub-packages are committed first, core `plain` last.

Example: `./.claude/skills/release/commit-and-push plain-admin:0.65.1 plain:0.103.0`

## Release Type Guidelines

Consider the current version when suggesting release types:

### Pre-1.0 packages (0.x.y)

Most Plain packages are pre-1.0. For these:

- **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 explicitly requested for stability milestone

### Post-1.0 packages (x.y.z where x >= 1)

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"

Overview

This skill automates releasing Plain Python packages to PyPI with intelligent version suggestions and parallel release-notes generation. It discovers changed packages, recommends semantic bumps based on commit analysis, updates versions, prepends changelog entries, and commits/tags/pushes releases. Use it to standardize releases and reduce manual overhead when publishing package updates.

How this skill works

The tool inspects the git history to find packages with unreleased commits and parses commit messages and diffs to suggest patch/minor/major bumps. It supports first-release handling for 0.0.0 packages, runs scripts to bump versions, generates release-note entries from actual diffs (skipping tests/internal-only changes), and then runs a commit-and-push workflow that tags and pushes each package. Flags allow forcing a release despite a dirty git state and overriding automatic bump choices.

When to use it

  • Preparing one or more Plain packages for publication to PyPI after development changes.
  • When you want automated, consistent changelogs and semantic version suggestions.
  • When multiple subpackages changed and you need coordinated commits/tags.
  • When releasing a package for the first time (0.0.0) and deciding initial version.

Best practices

  • Keep Git working directory clean before running (or use --force deliberately).
  • Review suggested bump types and generated changelog text before committing.
  • Prefer 0.1.0 for initial development releases and 1.0.0 only for stable milestones.
  • Exclude test-only and internal refactor changes from user-facing release notes.
  • Release sub-packages first and the core package last to preserve dependency order.

Example use cases

  • Discover changed packages and interactively release them: /release
  • Release specific packages with an enforced patch bump: /release plain-admin --patch
  • First-time release for a package at 0.0.0: choose 0.1.0 or 1.0.0 and set version via uv.
  • Generate changelogs that summarize diffs and include commit hash links automatically.

FAQ

What if my git status is dirty?

The tool stops and asks you to commit or stash changes unless you pass --force to override the check.

How are release types suggested?

It analyzes commits and diffs: 'fix'/'docs' hints at patch, 'add'/'feature' hints at minor, and explicit breaking indicators suggest major. You can accept or override each suggestion.