diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index fe7f29ba506..b9571f8d97f 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,2 +1,3 @@ +See @../AGENTS.md for repository-specific agent instructions. See @../.cursor/rules/base.mdc for information on your desired behavior. See @../.cursor/rules/docs.mdc for details on Shopify CLI architecture and conventions. diff --git a/.cursor/rules/base.mdc b/.cursor/rules/base.mdc index e3dbda1a85a..b71a2c4c08a 100644 --- a/.cursor/rules/base.mdc +++ b/.cursor/rules/base.mdc @@ -33,3 +33,10 @@ Adhere to the following guidelines in your code: - If you think there might not be a correct answer, say so. If you do not know the answer, say so instead of guessing. - In tests, always avoid mocking the filesystem. Use real files and directories, in temporary directories if needed. - In tests, prefer to have as little shared state between tests as possible. Avoid beforeAll and afterAll. + +Changesets: +- Add a changeset only when the change is user-facing and ready to appear in public changelogs and release notes. +- Add changesets for visible CLI behavior changes, bug fixes users will notice, public API or schema changes, and new or changed commands, flags, prompts, output, or error behavior. +- Keep changeset summaries short: one line maximum. +- Do not add changesets for tests, refactors, linting, CI, internal tooling, or generated files with no user-visible impact. +- If the change is not ready to be public, do not add a changeset. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..39dc28408f7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +# Agent Instructions + +## Changesets + +Add a changeset only when the change is user-facing and ready to appear in public changelogs and release notes. + +Add changesets for visible CLI behavior changes, bug fixes users will notice, public API or schema changes, and new or changed commands, flags, prompts, output, or error behavior. + +Keep changeset summaries short: one line maximum. + +Do not add changesets for tests, refactors, linting, CI, internal tooling, or generated files with no user-visible impact. + +If the change is not ready to be public, do not add a changeset. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6422291582a..2ec05fa3502 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,8 @@ This project uses [Changesets](https://github.com/changesets/changesets) to manage versions and changelogs. Every user-facing change requires a changeset file. +Changesets are published in public changelogs and release notes. Only add one when the change is user-facing and ready to announce publicly. Do not add changesets for tests, refactors, linting, CI, internal tooling, or other changes with no user-visible impact. + ``` pnpm changeset add ``` diff --git a/bin/changeset.js b/bin/changeset.js new file mode 100644 index 00000000000..df04f838d6a --- /dev/null +++ b/bin/changeset.js @@ -0,0 +1,71 @@ +import {spawn} from 'node:child_process' +import {fileURLToPath} from 'node:url' +import readline from 'node:readline' + +const args = process.argv.slice(2) +const changesetBinPath = fileURLToPath(new URL('../node_modules/@changesets/cli/bin.js', import.meta.url)) + +if (shouldConfirmPublicChangeset(args)) { + const confirmed = await confirmPublicChangeset() + + if (!confirmed) { + console.log('No changeset created.') + process.exit(1) + } +} + +const child = spawn(process.execPath, [changesetBinPath, ...args], {stdio: 'inherit'}) + +child.on('error', (error) => { + console.error(error) + process.exit(1) +}) + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal) + } + + process.exit(code ?? 1) +}) + +function shouldConfirmPublicChangeset(changesetArgs) { + if (changesetArgs.includes('--empty')) { + return false + } + + if (changesetArgs.length === 0 || changesetArgs[0] === 'add') { + return true + } + + return ( + changesetArgs[0].startsWith('-') && + changesetArgs.some((arg) => ['--message', '--open', '--since'].includes(arg)) + ) +} + +async function confirmPublicChangeset() { + if (!process.stdin.isTTY) { + console.error( + 'Refusing to create a changeset without interactive confirmation. Changesets are public changelog entries; only run `pnpm changeset add` for user-facing changes that are ready to publish.', + ) + return false + } + + const prompt = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + + try { + const answer = await new Promise((resolve) => + prompt.question( + 'Changesets are public changelog entries. Is this change user-facing and ready to publish? (y/N) ', + resolve, + ), + ) + return ['y', 'yes'].includes(answer.trim().toLowerCase()) + } finally { + prompt.close() + } +} diff --git a/package.json b/package.json index ee6cf53c786..945f25ca022 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build:affected": "nx affected --target=build", "build": "nx run-many --target=build --all --skip-nx-cache", "bundle-for-release": "nx run-many --target=bundle --all --skip-nx-cache", + "changeset": "node bin/changeset.js", "changeset-manifests": "changeset version && pnpm install --no-frozen-lockfile && pnpm refresh-manifests && pnpm refresh-readme && pnpm refresh-code-documentation && bin/update-cli-kit-version.js", "clean": "nx run-many --target=clean --all --skip-nx-cache && nx reset", "create-app": "nx build create-app && node packages/create-app/bin/dev.js --package-manager pnpm",