release

release is a cursor rule for Cursor from louisbrulenaudet/monorepo-template. It costs 0 tokens per session (3,654 once invoked), scanned A, original, Apache-2.0.

A guide to the repository's Changesets release process, which creates version changes and Git tags before deploying private Cloudflare Workers. It explicitly says the workspaces are not published to npm, the package registry.

In plain words
What is it for?
Use it when changing release workflows, version pull requests, Git tags, or the deployment step that promotes Workers.
Why use it?
It prevents releases from skipping the CI gate, using the wrong mode, or assuming that a Git tag also publishes packages.

Cursor rule for Cursor

Written for Cursor: installed under .cursor/. Also seen: mentions CLAUDE.md; mentions AGENTS.md.

Good fit Use it when changing release workflows, version pull requests, Git tags, or the deployment step that promotes Workers.

Compare 6 cursor rules from other repositories ↓
Install with agentmods
npx agentmods add rules/louisbrulenaudet/monorepo-template/release
Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

Clone the repo
git clone --depth 1 https://github.com/louisbrulenaudet/monorepo-template

Made for: Cursor.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for release

README.md
[![agentmods](https://agentmods.dev/badge/rules/louisbrulenaudet/monorepo-template/release/github.svg)](https://agentmods.dev/rules/louisbrulenaudet/monorepo-template/release)
Your own site
<a href="https://agentmods.dev/rules/louisbrulenaudet/monorepo-template/release"><img src="https://agentmods.dev/badge/rules/louisbrulenaudet/monorepo-template/release/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for release

Your own site · 80×15
<a href="https://agentmods.dev/rules/louisbrulenaudet/monorepo-template/release"><img src="https://agentmods.dev/badge/rules/louisbrulenaudet/monorepo-template/release.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 0 Nothing until a file matches its globs; then the whole rule loads.
When invoked 3,654 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00000 $0.03654
Opus 5.5 $0.00000 $0.01462
Sonnet 5.5 $0.00000 $0.00731
Haiku 4.5 $0.00000 $0.00365

Measured today against content hash c253356fb35f, method: parsed. Prices are Anthropic first-party input rates as of 2026-10-07, from the pricing page.

Security

Grade A, and why

release scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured today.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

.cursor/rules/ops/release.mdc · 70 lines

How it starts

The opening of the file, as written. The whole thing — 70 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Releases

Changesets computes versions; the pipeline is .github/workflows/release.yml - read it for the job list. Human-facing "how do I": .changeset/README.md; writing a changeset: changesets.mdc. CI internals: ci.mdc; the deploy: cd.mdc.

Nothing is published to npm: every workspace is private: true, with no publishConfig and no .npmrc. A release is a git tag plus a Cloudflare Workers promote.

State machine

push to main ──► Release  (concurrency release-main, queue: max, never canceled)
  ├─ gate         ALWAYS. uses ci.yml (full graph, not --affected)
  ├─ select-mode  'version' | 'none'   ('publish' is unreachable here)
  ├─ mode == 'version' → version → open/update the "chore: release" PR
  │                                → dispatch ci.yml on changeset-release/main   [END]
  └─ mode != 'version' → tag (needs gate) → created? → deploy → uses cd.yml

Invariants

  • gate runs on every push to main, in both modes. While a release PR is open the mode is always version, so a gate only on the release path would leave main unvalidated for that PR's whole life. Never move it under the mode != 'version' branch.
  • The tag is the idempotency key. .github/actions/release/create-release-tag.sh skips when vX.Y.Z exists and reports created=false; deploy is gated on created == 'true', so re-running Release never re-deploys. The existence probe is three-way: 2xx skips, 404 creates, any other failure fails the job rather than guessing. Redeploy on purpose with cd.yml's workflow_dispatch and the tag.
  • mode != 'version', not mode == 'publish': select-mode returns publish only when publishable packages exist, and all of ours are private, so the equality test would never fire. Load-bearing.
  • select-mode installs first: with no pending changeset, as on every push after a release PR merges, it runs changeset publish-plan through require.resolve("@changesets/cli/bin.js") from the checkout, so without pnpm install the job fails and no tag is cut. Only the version branch works without the CLI, which is why a missing install stays hidden while a release PR is open. Every package is private and privatePackages.tag is false, so the plan is empty and the mode is none.
  • privatePackages.tag: false is deliberate: changeset git-tag (v3's name for changeset tag) would emit one <app>@X.Y.Z per app, while the deploy keys on one shared vX.Y.Z, which create-release-tag.sh cuts instead.
  • fixed: [["*"]] is the app set, resting on apps being unscoped: Changesets matches each fixed entry with picomatch against package names, and * does not cross /, so the group is exactly the unscoped workspaces (the apps), never an @repo/* package; a new app joins the shared version by existing. Nothing machine-checks the naming underneath (.syncpackrc.json's @repo/** group governs dependency specifiers, not workspace names; pnpm boundaries never reads a name), so naming.mdc keeps it by hand: an unscoped package under packages/ would silently join the group and bump to the app version.
  • changelog names no repo: @changesets/changelog-github falls back to GITHUB_REPOSITORY, which Actions sets in the version job, so a repository created from the template links its own PRs and commits. Hard-coding the slug would point every derived changelog at this repository. changeset version runs only in CI, so nothing local needs it.
  • privatePackages.version: true is load-bearing: v3 still defaults to {version: false, tag: false}, which would version nothing here. baseBranch: "main" restates the v3 default and stays because the bundled changesets/action sub-actions read the config with their own copy of @changesets/config.
  • changedFilePatterns is the "what ships" list, matched with picomatch against paths relative to each package directory, so !*.md drops only top-level docs (README.md, AGENTS.md, CLAUDE.md), never a Markdown file under src/. It has no dot option: dotfiles never count, so a shipped dotfile (public/.well-known/*) needs its own pattern. Keep it aligned with the excludes of turbo's transit task.
  • ignore: ["@repo/vitest-config"]: that package is test tooling only, so a change to it never needs a changeset. The config rule that dependents of an ignored package must be ignored too skips devDependency edges, so the apps stay versionable. Never ignore a package an app bundles: its changes would stop requiring a changeset.
  • format stays at its auto default: Changesets runs oxfmt on every changeset and changelog it writes, so they pass format:check (the CLI writes ---\n\n--- for an empty changeset, which oxfmt rewrites). $schema pins the installed @changesets/config version; bump it with @changesets/cli.
  • release:check (changesets/check.mjs, its logic checkChangesets in changesets/lib.mjs) wraps changeset status --since=origin/main --output, which exits 1 only when packages changed and no changeset changed since the merge base, and counts only tracked files (git diff), so a new changeset must be staged. Plain changeset status (release:status) counts every pending changeset, so it cannot tell whether the branch added one. The JSON plan feeds the lint (app-only names, title length, headings, major behind CHANGESET_ALLOW_MAJOR). Editing any pending changeset satisfies the presence half: coarse by design, the lint and review cover the rest.
  • The changeset PR comment renders the gate's own result (changesets/comment.mjs): changeset-pr-status.yml runs checkChangesets, so the comment and the CI Changesets step never disagree, and changesets/action/pr-comment only posts it under the default marker. Never go back to changesets/action/pr-status: it takes no inputs, its add-a-changeset link prefills every changed workspace package (@repo/* and ignored ones included, which release:check rejects or Changesets refuses as a mixed changeset), an empty changeset reads as "included in the next version bump", and its no-changeset text assumes a non-blocking setup. The job installs because changeset status needs the CLI; it restores the pnpm store read-only (ci.mdc). changeset status exits 1 both for a missing changeset and for a crash, so a non-zero exit with no .changeset/*.md changed since the merge base reads as missing, anything else as unavailable: the renderer exits 1 after writing that body, and pr-comment runs on !cancelled(), so a failed run replaces the comment instead of leaving the previous commit's.
  • Catalog bumps are checked separately (changesets/lib.mjs): pnpm-workspace.yaml belongs to no package, so Changesets never sees a catalog change. runtimeCatalogChanges diffs the catalog: / catalogs: specifiers against the merge base and keeps those an app lists under dependencies. A bump with no changeset in the branch fails the gate. In Actions the merge-base pnpm-workspace.yaml is read through the contents API with the job's read-only GITHUB_TOKEN, never git show: the blob:none checkout would fetch that blob lazily without credentials, which fails on a private repository such as one created from this template. Locally it is git show. For the same reason changesets/lib.mjs runs git and changeset status with diff.renames=false (through GIT_CONFIG_*): rename detection reads the blobs of every added/deleted pair, so a PR that renames files failed both release:check and the Release plan on could not fetch … from promisor remote. Lockfile-only refreshes of a direct dependency are not detected; add lockfile importer diffing if they start to matter.
  • Dependabot changesets (dependabot-changesets.yml) commit one patch changeset per affected app as .changeset/dependabot-<PR>-<app>.md, reconciled to the desired state on every run, so a run that finds them current is a no-op. That no-op path needs no token, which matters because the run our own push triggers gets Actions secrets rather than Dependabot secrets.
    • The commit goes through GraphQL createCommitOnBranch with expectedHeadOid: GitHub signs it, and it fails rather than overwrite a newer push.
    • It uses the CHANGESET_BOT_TOKEN Dependabot secret (a fine-grained PAT, Contents: write), because Dependabot runs get a read-only GITHUB_TOKEN and a GITHUB_TOKEN push would start no CI run.
    • [dependabot skip] in the message keeps Dependabot rebasing the PR; a rebase drops the commit and the next run writes it again.
    • pull_request, never pull_request_target, and never pnpm install in that job: only git and Node run while the token is in the environment. With the secret unset it prints a notice and exits 0, and ci.yml sets CHANGESET_SKIP_RUNTIME_DEPENDENCIES=1 for that Dependabot PR, so the gate warns instead of failing: a project created from the template works with no setup, and its Dependabot bumps ship without a release note until someone adds one. The flag reads the Dependabot secret store, which is what a Dependabot-triggered run sees.
  • The version job dispatches CI on the release branch (actions: write, beside the contents / pull-requests: write that changesets/action/version needs): the release PR is opened with GITHUB_TOKEN, so its pull_request runs wait for approval and the required check comes only from that dispatch (ci.mdc). While a release PR is open, every push to main therefore runs the full graph twice, as gate and as the dispatch.
  • One contract comment on the release PR (release/comment-release-pr-contract.sh): changesets/action/version has no PR-body input, so its "published to npm automatically" description stays and the comment corrects it. gh pr comment --edit-last --create-if-none keeps one comment across every version re-run; changesets/action/pr-comment cannot be used here because it only targets the PR that triggered the run.
  • The version PR branch is force-pushed, not accumulated: every push to main resets changeset-release/main from the tip, re-runs changeset version, and force-pushes one commit, so it is never behind or in conflict with main, and manual edits to it are destroyed.
  • release.yml calls cd.yml directly because tags created with GITHUB_TOKEN start no workflow (cd.mdc); never "restore" a tag trigger.
  • queue: max, not the default: with queue: single, a third push replaces the pending run and that release is silently dropped.

Read the full file on GitHub · 70 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. today Changed · +1 lines c253356fb35f
  2. 3d ago Changed 44b4a96e1326
  3. 15d ago Changed 0a579a1892bb
  4. 20d ago Changed · +3 lines 4ea6b304030c
  5. 22d ago Changed · +15 lines b6c708de3ea0
  6. 23d ago Changed · -1 lines f2c76d24c9b4
  7. 24d ago Changed dca2d0e911be
  8. 25d ago Changed bcda0245d112
  9. 27d ago Changed · +1 lines 0544d1a6efb2
  10. 1mo ago First seen · 51 lines · 0 tokens per session scan A 02d8cc5e7d6a

Subscribe to this mod's changes

release is a cursor rule published in the GitHub repository louisbrulenaudet/monorepo-template (19 stars, last pushed today), licensed Apache-2.0. It costs nothing until one of its globs matches a file; then it loads 3,654 tokens. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-09-03.

Related

Other cursor rules, from other repositories

mcp-releasing

Steps and procedures for releasing MCP (Model Context Protocol) servers as NPM packages.

steipete/agent-rules · 0 tokens

add-to-changelog

Update the project's CHANGELOG.md file with a new entry.

steipete/agent-rules · 260 tokens

changelog-generator-manual

This rule generates a comprehensive changelog.md file by analyzing all git tags and commits, creating a chronological record of all project changes with proper semantic versioning structure. The changelog.md file is stored at the root of the project.

usrrname/cursorrules · 0 tokens

release-commit-analysis-auto

This rule analyzes commits to determine if they should trigger a version bump. It categorizes conventional and non-conventional commits into Features, Bug Fixes, Breaking Changes, Documentation, and Other. It uses intelligent keyword matching for non-conventional commits and analyzes file changes to help categorize…

usrrname/cursorrules · 0 tokens

release-git-tags-auto

This rule checks for existing tags before creating new ones. It also offers to push the tag to the remote repository. It runs when users request release creation, version bumping, or package publishing.

usrrname/cursorrules · 0 tokens

release-validation-auto

This rule validates release prerequisites including branch naming conventions and package.json structure. It runs when users request release creation, version bumping, or package publishing. The rule ensures releases only occur from appropriate branches (release/, hotfix/, fix/) and validates package.json exists with…

usrrname/cursorrules · 0 tokens