Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changesets

This directory contains [**Changesets**](https://github.com/changesets/changesets) which are markdown files that describe package changes for the next release.

For guidance on when and how to add changesets, checkout the [Maintainer's Guide](../.github/maintainers_guide.md#-updating-changesets).
15 changes: 15 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦠 note: I'm finding the npx version of "changesets" returns a different version

$ npx --yes @changesets/cli --version
2.31.0

🔬 ramble: I'm leaving a comment with package.json towards this so am holding off on a suggestion!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point I added this to the package.json

"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"privatePackages": {
"version": true,
"tag": true
}
}
1 change: 1 addition & 0 deletions .github/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ For your contribution to be accepted:
- [x] The test suite must be complete and pass.
- [x] The changes must be approved by code review.
- [x] Commits should be atomic and messages must be descriptive. Related issues should be mentioned by Issue number.
- [x] User-facing changes include a changeset (run `npx changeset add`). See [Updating Changesets](./maintainers_guide.md#-updating-changesets) for the format; releases are automated from these.

If the contribution doesn't meet the above criteria, you may fail our automated checks or a maintainer will discuss it with you. You can continue to improve a Pull Request by adding commits to the branch from which the PR was created.

Expand Down
13 changes: 9 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ updates:
interval: "monthly"
open-pull-requests-limit: 5
labels:
- "build"
- "semver:patch"
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
labels:
- "build"
- "semver:patch"
- "dependencies"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
versioning-strategy: increase
labels:
- "dependencies"
50 changes: 43 additions & 7 deletions .github/maintainers_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ brew update
brew install pyenv
```

Install necessary Python runtimes for development/testing. You can rely on GitHub Actions workflows for testing with various major versions. <https://github.com/slackapi/bolt-python/tree/main/.github/workflows>
Install necessary Python runtime for development/testing.

```sh
$ pyenv install 3.14 # select the latest patch version
Expand All @@ -46,14 +46,50 @@ source .venv/bin/activate

## Versioning

Follow the [conventional commit specification][conv-commits]. PR titles and
commit messages use prefixes like `feat:`, `fix:`, `chore:`, `docs:`, etc.
First letter after the prefix is lowercase unless it's a proper noun.
Follow the [conventional commit specification][conv-commits]. PR titles and commit messages use prefixes like `feat:`, `fix:`, `chore:`, `docs:`, etc. First letter after the prefix is lowercase unless it's a proper noun.

### Plugin version bumps
### 🎁 Updating Changesets

Every release must bump the `version` field in
`.claude-plugin/plugin.json` and `.cursor-plugin/plugin.json` following [semver][semver].
This project uses [Changesets](https://github.com/changesets/changesets) to track changes and automate releases.

Each changeset describes a change to the package and its [semver][semver] impact, and a new changeset should be added when updating the package with some change that affects consumers:

```sh
npx changeset add
```

Alternatively, hand-write a file named `.changeset/<anything>.md`, with this format:

```md
---
"slack": minor
---

Add the channel-digest command
```

The frontmatter key is always `"slack"`; the value is the [semver][semver] bump level, like `patch`, `minor`, or `major`. The body becomes the changelog entry, so write it for a reader of the release notes.

Updates to documentation, tests, or CI might not require new entries.

When a PR containing changesets is merged to `main`, a different PR is opened or updated using [changesets/action](https://github.com/changesets/action) which consumes the pending changesets, bumps the package version, and updates the `CHANGELOG` in preparation to release.

### 🚀 Releases

Releasing can feel intimidating at first, but don't fret! Venture on!

New official package versions are published when the release PR created from changesets is merged. Follow these steps to build confidence:

1. **Run the tests locally**: Before merging the release PR please run all the tests especially the eval ones. If they no longer pass we may need fix it before releasing the changes.

2. **Check GitHub**: Please check if issues or pull requests are still open either decide to postpone the release or save those changes for a future update.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔮 question: Are milestones something we want to introduce here, either now or later?

@WilliamBergamin WilliamBergamin Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd want to refrain from using milestones 🤔 I find they tend to clutter our release process and Its not clear to me what our users gain from them

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to agree with @WilliamBergamin - Milestones feel like a forgotten feature of GitHub and GitHub Releases serve as a way of documenting what landed in a release. Milestones have benefit when changes land across multiple versions ("Next Release" vs "Next Major Release"). But this project doesn't need that complexity.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WilliamBergamin @mwbrooks I agree in the overhead without obvious benefit. No problem omitting these for the project!

Some PRs might find a moment of confusion without a milestone to add but this isn't meaningful enough to change for 📠


3. **Review the release PR**: Verify that the version bump matches expectations, `CHANGELOG` entries are clear, and CI checks pass.

4. **Merge and approve**: Merge the release PR. It may take up to 24 hours before you see you release in the [Claude Plugins](https://claude.com/plugins/slack) directory.

5. **Communicate the release**:
- **External**: Post in relevant channels (e.g. #lang-javascript, #tools-bolt) on [Slack Community](https://community.slack.com/). Include a link to the release notes.

## Everything Else

Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Release

on:
push:
branches:
- main
workflow_dispatch:

# Don't let two release runs race on the same branch.
concurrency: ${{ github.workflow }}-${{ github.ref }}

env:
PY_VERSION: "3.14"
NODE_VERSION: "26"

jobs:
release:
name: Release
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
Comment thread
WilliamBergamin marked this conversation as resolved.
persist-credentials: false
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Set up Python ${{ env.PY_VERSION }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.PY_VERSION }}
- name: Install Node dependencies
run: npm install
- name: Changesets release
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
version: bash scripts/changeset_version.sh
publish: npx changeset publish
commit: "chore: release"
title: "chore: release"
prDraft: create
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,11 @@ GitHub Actions (`.github/workflows/ci-build.yml`) gates every PR with:
- **Test** — `make test-unit` (pytest)

LLM-judged tests are not run in CI (Ollama + model download would exceed time budget).

## Releasing

Releases are automated and run in CI — **you never run a release yourself.** Your only release-related task is adding a changeset when a PR makes a user-facing change.

See the [maintainers guide](.github/maintainers_guide.md#-updating-changesets) for the format.

Everything after that is handled by [changesets](https://github.com/changesets/changesets) and `scripts/changeset_version.sh`: merging to `main` opens a "chore: release" PR, and merging that PR publishes the release.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ install-tools: $(VENV) ## Install linting/formatting tools (ruff)

clean: ## Remove virtual environment, Ollama, and local Cursor install
-$(PYTHON) scripts/cursor.py uninstall
rm -rf $(VENV) $(OLLAMA_DIR)
rm -rf $(VENV) $(OLLAMA_DIR) node_modules

cursor-install: $(VENV) ## Install this plugin into a local Cursor for development
$(PYTHON) scripts/cursor.py install
Expand Down
11 changes: 11 additions & 0 deletions package.json

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎁 suggestion: I think we should include the @changeset/cli package here alongside a lockfile to avoid surprise in updates. IIRC "v3" is in development at the moment!

🍰 ramble: I'm not wanting to add separate documentation for this either! I'm curious if the scripts/changeset_version.sh file and make file might just... re-install the packages? This might be upsetting.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "slack",
"version": "1.1.0",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Let's add the required Node.js verson to the package.json.

While testing on my local machine, my nvm defaulted to Node 20.x and I ran into runtime errors with npx changeset add. It was fixed by upgrading to Node 26.x however package.json would have warned me if it knew the required version.

Suggested change
"version": "1.1.0",
"version": "1.1.0",
"engines": {
"node": ">=26.0.0"
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch 💯

"private": true,
"engines": {
"node": ">=26"
},
"devDependencies": {
"@changesets/cli": "^2.31.0"
}
}
5 changes: 5 additions & 0 deletions scripts/changeset_version.sh

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌟 praise: Great to see these scripts extended so well!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bash, Node, and Python all for a project that only needs markdown 😆

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail

npx changeset version
python scripts/sync_versions.py
37 changes: 37 additions & 0 deletions scripts/sync_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import json
import logging
from pathlib import Path

logger = logging.getLogger(Path(__file__).stem)

REPO_ROOT = Path(__file__).resolve().parent.parent

# package.json (committed at the repo root) is the version source of truth; changesets
# bumps it, then this script synchronizes the version out to everywhere else its defined.
PACKAGE_JSON_PATH = REPO_ROOT / "package.json"
CLAUDE_PLUGIN_PATH = REPO_ROOT / ".claude-plugin" / "plugin.json"
CURSOR_PLUGIN_PATH = REPO_ROOT / ".cursor-plugin" / "plugin.json"


def read_version(package_path: Path) -> str:
"""Return the ``version`` field from ``package.json``."""
return json.loads(package_path.read_text())["version"]


def write_version(plugin_path: Path, version: str) -> None:
"""Set the ``version`` field of a JSON plugin manifest."""
manifest = json.loads(plugin_path.read_text())
manifest["version"] = version
Comment on lines +22 to +24

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👁️‍🗨️ thought: I'm curious if separate functions for different targets might improve maintenance but not a blocker whatsoever-

plugin_path.write_text(json.dumps(manifest, indent=2) + "\n")
logger.info(f"Set {plugin_path} version to {version}")


def main() -> None:
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
version = read_version(PACKAGE_JSON_PATH)
write_version(CLAUDE_PLUGIN_PATH, version)
write_version(CURSOR_PLUGIN_PATH, version)


if __name__ == "__main__":
main()