diff --git a/.changeset/README.md b/.changeset/README.md index e74974b36..2511ccacb 100644 --- a/.changeset/README.md +++ b/.changeset/README.md @@ -1,6 +1,95 @@ -This directory is managed by Changesets. +# Changesets -- Add a changeset locally with `pnpm changeset`. -- The CI "Release (prepare)" workflow opens/updates a Version Packages PR. -- Publishing happens from a GitHub Release via the "Publish to npm" workflow. +This directory is managed by [Changesets](https://github.com/changesets/changesets). +## Quick Start + +```bash +pnpm changeset +``` + +Follow the prompts to select version bump type and describe your changes. + +## Workflow + +1. **Add a changeset** — Run `pnpm changeset` locally before or after your PR +2. **Version PR** — CI opens/updates a "Version Packages" PR when changesets merge to main +3. **Release** — Merging the Version PR triggers npm publish and GitHub Release + +> **Note:** Contributors only need to run `pnpm changeset`. Versioning (`changeset version`) and publishing happen automatically in CI. + +## Template + +Use this structure for your changeset content: + +```markdown +--- +"@fission-ai/openspec": patch +--- + +### New Features + +- **Feature name** — What users can now do + +### Bug Fixes + +- Fixed issue where X happened when Y + +### Breaking Changes + +- `oldMethod()` has been removed, use `newMethod()` instead + +### Deprecations + +- `legacyOption` is deprecated and will be removed in v2.0 + +### Other + +- Internal refactoring of X for better performance +``` + +Include only the sections relevant to your change. + +## Version Bump Guide + +| Type | When to use | Example | +|------|-------------|---------| +| `patch` | Bug fixes, small improvements | Fixed crash when config missing | +| `minor` | New features, non-breaking additions | Added `--verbose` flag | +| `major` | Breaking changes, removed features | Renamed `init` to `setup` | + +## When to Create a Changeset + +**Create one for:** +- New features or commands +- Bug fixes that affect users +- Breaking changes or deprecations +- Performance improvements users would notice + +**Skip for:** +- Documentation-only changes +- Test additions/fixes +- Internal refactoring with no user impact +- CI/tooling changes + +## Writing Good Descriptions + +**Do:** Write for users, not developers +```markdown +- **Shell completions** — Tab completion now available for Bash, Fish, and PowerShell +``` + +**Don't:** Write implementation details +```markdown +- Added ShellCompletionGenerator class with Bash/Fish/PowerShell subclasses +``` + +**Do:** Explain the impact +```markdown +- Fixed config loading to respect `XDG_CONFIG_HOME` on Linux +``` + +**Don't:** Just reference the fix +```markdown +- Fixed #123 +``` diff --git a/.changeset/config.json b/.changeset/config.json index 399245099..50cbb5132 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,9 @@ { "$schema": "https://unpkg.com/@changesets/config/schema.json", - "changelog": "@changesets/cli/changelog", + "changelog": [ + "@changesets/changelog-github", + { "repo": "Fission-AI/OpenSpec" } + ], "commit": false, "fixed": [], "linked": [], diff --git a/.github/workflows/polish-release-notes.yml b/.github/workflows/polish-release-notes.yml new file mode 100644 index 000000000..7c1898308 --- /dev/null +++ b/.github/workflows/polish-release-notes.yml @@ -0,0 +1,137 @@ +name: Polish Release Notes + +# Triggers when changesets creates a release +on: + release: + types: [published] + +permissions: + contents: write + +jobs: + polish: + # Only run on the main repo, not forks + if: github.repository == 'Fission-AI/OpenSpec' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Get current release body + id: get-release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release view "${{ github.event.release.tag_name }}" --json body -q '.body' > current-notes.md + echo "Fetched release notes for ${{ github.event.release.tag_name }}" + + - name: Transform release notes with Claude + uses: anthropics/claude-code-action@v1 + id: claude + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} + direct_prompt: | + Transform the changelog in `current-notes.md` into release notes for OpenSpec ${{ github.event.release.tag_name }}. + + ## Voice + + OpenSpec is a developer tool. Write like you're talking to a peer: + - Direct and practical, not marketing copy + - Focus on what changed and why it matters + - Skip the hype, keep it real + + ## Output + + Create two files: + + ### 1. `release-title.txt` + + A short title in this format: + ``` + ${{ github.event.release.tag_name }} - [1-4 words describing the release] + ``` + + Examples: + - `v0.18.0 - OPSX Experimental Workflow` + - `v0.16.0 - Antigravity, iFlow Support` + - `v0.15.0 - Gemini CLI, RooCode` + + Rules for title: + - Lead with the most notable addition + - 1-4 words after the dash, no fluff + - If multiple features, comma-separate the top 2 + - For bugfix-only releases, use something like `v0.17.2 - Pre-commit Hook Fix` + + ### 2. `polished-notes.md` + + ```markdown + ## What's New in ${{ github.event.release.tag_name }} + + [One sentence: what's the theme of this release?] + + ### New + + - **Feature name** - What it does and why you'd use it + + ### Improved + + - **Area** - What got better + + ### Fixed + + - What was broken, now works + ``` + + Omit empty sections. + + ## Rules + + 1. Write for developers using OpenSpec with AI coding assistants + 2. Remove commit hashes (like `eb152eb:`), PR numbers, and changesets wrappers (`### Minor Changes`) + 3. Lead with what users can do, not implementation details + 4. One to two sentences per item, max + 5. Use **bold** for feature/area names + 6. Skip internal changes (CI, refactors, tests) unless they affect users + 7. If the input is already well-formatted, just clean up structure and remove noise + + ## Example + + Before: + ``` + ### Minor Changes + - 8dfd824: Add OPSX experimental workflow commands and enhanced artifact system + **New Commands:** + - `/opsx:ff` - Fast-forward through artifact creation + ``` + + After (polished-notes.md): + ``` + ### New + + - **Fast-forward mode** - Generate all planning artifacts at once with `/opsx:ff`. Useful when you already know what you're building. + ``` + + After (release-title.txt): + ``` + v0.18.0 - OPSX Experimental Workflow + ``` + + Write both files. No other output. + + - name: Update release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ github.event.release.tag_name }}" + + if [ -f "polished-notes.md" ] && [ -f "release-title.txt" ]; then + TITLE=$(cat release-title.txt) + gh release edit "$TAG" --title "$TITLE" --notes-file polished-notes.md + echo "Updated: $TITLE" + elif [ -f "polished-notes.md" ]; then + gh release edit "$TAG" --notes-file polished-notes.md + echo "Updated notes (title unchanged)" + else + echo "No changes generated, keeping original" + fi diff --git a/package.json b/package.json index 174aff245..d36a9af71 100644 --- a/package.json +++ b/package.json @@ -54,13 +54,13 @@ "check:pack-version": "node scripts/pack-version-check.mjs", "release": "pnpm run release:ci", "release:ci": "pnpm run check:pack-version && pnpm exec changeset publish", - "release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish", "changeset": "changeset" }, "engines": { "node": ">=20.19.0" }, "devDependencies": { + "@changesets/changelog-github": "^0.5.2", "@changesets/cli": "^2.27.7", "@types/node": "^24.2.0", "@vitest/ui": "^3.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 870e9d767..92da325a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: specifier: ^4.0.17 version: 4.0.17 devDependencies: + '@changesets/changelog-github': + specifier: ^0.5.2 + version: 0.5.2 '@changesets/cli': specifier: ^2.27.7 version: 2.29.6(@types/node@24.2.0) @@ -73,6 +76,9 @@ packages: '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + '@changesets/changelog-github@0.5.2': + resolution: {integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==} + '@changesets/cli@2.29.6': resolution: {integrity: sha512-6qCcVsIG1KQLhpQ5zE8N0PckIx4+9QlHK3z6/lwKnw7Tir71Bjw8BeOZaxA/4Jt00pcgCnCSWZnyuZf5Il05QQ==} hasBin: true @@ -86,6 +92,9 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + '@changesets/get-github-info@0.7.0': + resolution: {integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==} + '@changesets/get-release-plan@4.0.13': resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} @@ -829,6 +838,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -853,6 +865,10 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -1198,6 +1214,15 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -1465,6 +1490,9 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -1574,6 +1602,12 @@ packages: jsdom: optional: true + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1641,6 +1675,14 @@ snapshots: dependencies: '@changesets/types': 6.1.0 + '@changesets/changelog-github@0.5.2': + dependencies: + '@changesets/get-github-info': 0.7.0 + '@changesets/types': 6.1.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding + '@changesets/cli@2.29.6(@types/node@24.2.0)': dependencies: '@changesets/apply-release-plan': 7.0.12 @@ -1695,6 +1737,13 @@ snapshots: picocolors: 1.1.1 semver: 7.7.2 + '@changesets/get-github-info@0.7.0': + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + '@changesets/get-release-plan@4.0.13': dependencies: '@changesets/assemble-release-plan': 6.0.9 @@ -2379,6 +2428,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + dataloader@1.4.0: {} + debug@4.4.1: dependencies: ms: 2.1.3 @@ -2393,6 +2444,8 @@ snapshots: dependencies: path-type: 4.0.0 + dotenv@8.6.0: {} + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {} @@ -2737,6 +2790,10 @@ snapshots: natural-compare@1.4.0: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + onetime@7.0.0: dependencies: mimic-function: 5.0.1 @@ -2985,6 +3042,8 @@ snapshots: totalist@3.0.1: {} + tr46@0.0.3: {} + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -3092,6 +3151,13 @@ snapshots: - tsx - yaml + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which@2.0.2: dependencies: isexe: 2.0.0