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
8 changes: 5 additions & 3 deletions scaffold/templates/AGENTS.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Builds and runs the test suite on Node 20 and 22:

### `release.yml` (runs on push to main)

Conventional-commit auto-bump: determines the bump type from commit messages since the last tag, updates `package.json`, creates a git tag and GitHub Release.
Reads the version from `package.json` and, if there is no matching tag yet, pushes the `v<version>` tag (plus the floating `vMAJOR` and `vMAJOR.MINOR` tags), creates a GitHub Release, and dispatches `publish.yml`. It only pushes tags and never writes to `main`; bump the version in your PR.

### `publish.yml` (runs on release published or workflow_dispatch)

Expand All @@ -105,11 +105,13 @@ Keeps repository labels in sync.

{% if type == 'cursor-plugin' %}
- The **source of truth** for the current version is `.cursor-plugin/plugin.json`.
- The release workflow auto-bumps it and the README badge on every qualifying push to main.
- Never manually change the version.
{% else %}
- The **source of truth** for the current version is `package.json`.
- Bump it in your PR with `npm version <patch|minor|major> --no-git-tag-version` (keeps the lockfile in sync) and update the README badge, following conventional-commit intent.
- On merge, `release.yml` tags that version and publishes it. `main` is protected and is never written to by CI.
{% endif %}
- The release workflow auto-bumps it and the README badge on every qualifying push to main.
- Never manually change the version.

## Code conventions

Expand Down
2 changes: 1 addition & 1 deletion scaffold/templates/CLAUDE.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This file provides guidance for Claude Code when working in this repository.
- All rules need frontmatter with description, globs, alwaysApply
{% else %}
- Use conventional commits (`feat:`, `fix:`, `chore:`, `docs:`)
- Never manually edit the version in `package.json` -- CI auto-bumps it
- Bump the version in `package.json` in your PR (`npm version`, keeps the lockfile in sync); `release.yml` tags and publishes that version on merge
- Provider adapters live in `src/providers/` and implement the `Provider` interface, wired into `ProviderManager`; tools live in `src/tools/`
- Keep `mcp-tools.json` in sync with the registered tools
{% endif %}
Expand Down
77 changes: 19 additions & 58 deletions scaffold/templates/release.mcp.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,60 @@ on:

permissions:
contents: write
actions: write

concurrency:
group: release
cancel-in-progress: false

jobs:
release:
name: Version, tag, and release
tag-and-release:
name: Tag and release
runs-on: ubuntu-latest
# Skip the release commit's own push (it carries [skip version]) so the
# job never re-triggers itself into a loop.
if: "!contains(github.event.head_commit.message, '[skip version]')"
steps:
# The default GITHUB_TOKEN (contents: write) pushes tags and creates the
# release. main is protected and is NEVER written to by CI; bump the
# version in your PR. A GITHUB_TOKEN release does not trigger publish.yml,
# so this job dispatches it explicitly afterward.
- uses: actions/checkout@v6
with:
fetch-depth: 0
# A PAT lets the release event trigger publish.yml. GITHUB_TOKEN
# pushes/releases do NOT trigger downstream workflows by design,
# which is why publish never fires without this. Falls back to
# GITHUB_TOKEN (publish.yml can then be run via workflow_dispatch).
token: ${{ secrets.RELEASE_PAT != '' && secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}

- uses: actions/setup-node@v6
with:
node-version: 22

- name: Determine bump from conventional commits
id: bump
run: |
set -euo pipefail
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$last_tag" ]; then
range_log=$(git log --pretty=format:'%s%n%b')
else
range_log=$(git log "${last_tag}..HEAD" --pretty=format:'%s%n%b')
fi
bump=patch
if printf '%s\n' "$range_log" | grep -qE 'BREAKING CHANGE' \
|| printf '%s\n' "$range_log" | grep -qE '^[a-z]+(\([^)]*\))?!:'; then
bump=major
elif printf '%s\n' "$range_log" | grep -qE '^feat(\([^)]*\))?:'; then
bump=minor
fi
echo "bump=$bump" >> "$GITHUB_OUTPUT"
echo "Computed bump: $bump (since ${last_tag:-<initial>})"

- name: Apply version bump
- name: Read version from package.json
id: ver
run: |
set -euo pipefail
new=$(npm version "${{ steps.bump.outputs.bump }}" --no-git-tag-version)
new=${new#v}
echo "version=$new" >> "$GITHUB_OUTPUT"
echo "New version: $new"

- name: Update README version badge
run: |
set -euo pipefail
v="${{ steps.ver.outputs.version }}"
if [ -f README.md ]; then
sed -i -E "s|(badge/version-)[0-9]+\.[0-9]+\.[0-9]+(-blue)|\1${v}\2|g" README.md || true
fi
v=$(node -p "require('./package.json').version")
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "package.json version: $v"

- name: Guard against re-tagging an existing version
- name: Check if tag already exists
id: check
run: |
set -euo pipefail
v="${{ steps.ver.outputs.version }}"
if git rev-parse "v$v" >/dev/null 2>&1; then
if git rev-parse "v${{ steps.ver.outputs.version }}" >/dev/null 2>&1; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Tag v$v already exists; skipping release."
echo "Tag v${{ steps.ver.outputs.version }} exists; nothing to release."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Commit, tag, and create release
- name: Tag, release, and trigger publish
if: steps.check.outputs.skip == 'false'
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT != '' && secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
v="${{ steps.ver.outputs.version }}"
IFS='.' read -r major minor _patch <<< "$v"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add package.json package-lock.json README.md
git commit -m "chore(release): v$v [skip version]"
git tag "v$v"
git tag -f "v$major"
git tag -f "v$major.$minor"
git push origin HEAD:main
git push origin "v$v"
git push origin "v$major" --force
git push origin "v$major.$minor" --force
gh release create "v$v" --title "v$v" --generate-notes
# publish.yml is idempotent (skips an already-published version), so a
# duplicate dispatch is harmless.
gh workflow run publish.yml --ref main
{% endraw %}