diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 6128da27..558eefe3 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -223,118 +223,23 @@ jobs:
with:
fetch-depth: 0
+ - run: git fetch --force --tags
+
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: bun install
+ env:
+ BUN_INSTALL_ALLOW_SCRIPTS: "@ast-grep/napi"
+
- name: Generate changelog
- id: changelog
run: |
- VERSION="${{ needs.publish-main.outputs.version }}"
-
- PREV_TAG=""
- if [[ "$VERSION" == *"-beta."* ]]; then
- BASE="${VERSION%-beta.*}"
- NUM="${VERSION##*-beta.}"
- PREV_NUM=$((NUM - 1))
- if [ $PREV_NUM -ge 1 ]; then
- PREV_TAG="${BASE}-beta.${PREV_NUM}"
- git rev-parse "v${PREV_TAG}" >/dev/null 2>&1 || PREV_TAG=""
- fi
- fi
-
- if [ -z "$PREV_TAG" ]; then
- PREV_TAG=$(curl -s https://registry.npmjs.org/oh-my-opencode/latest | jq -r '.version // "0.0.0"')
- fi
-
- echo "Comparing v${PREV_TAG}..v${VERSION}"
-
- # Get all commits between tags
- COMMITS=$(git log "v${PREV_TAG}..v${VERSION}" --format="%s" 2>/dev/null || echo "")
-
- # Initialize sections
- FEATURES=""
- FIXES=""
- REFACTOR=""
- DOCS=""
- OTHER=""
-
- # Store regexes in variables for bash 5.2+ compatibility
- # (bash 5.2 changed how parentheses are parsed inside [[ =~ ]])
- re_skip='^(chore|ci|release|test|ignore)'
- re_feat_scoped='^feat\(([^)]+)\): (.+)$'
- re_fix_scoped='^fix\(([^)]+)\): (.+)$'
- re_refactor_scoped='^refactor\(([^)]+)\): (.+)$'
- re_docs_scoped='^docs\(([^)]+)\): (.+)$'
-
- while IFS= read -r commit; do
- [ -z "$commit" ] && continue
- # Skip chore, ci, release, test commits
- [[ "$commit" =~ $re_skip ]] && continue
-
- if [[ "$commit" =~ ^feat ]]; then
- # Extract scope and message: feat(scope): message -> **scope**: message
- if [[ "$commit" =~ $re_feat_scoped ]]; then
- FEATURES="${FEATURES}\n- **${BASH_REMATCH[1]}**: ${BASH_REMATCH[2]}"
- else
- MSG="${commit#feat: }"
- FEATURES="${FEATURES}\n- ${MSG}"
- fi
- elif [[ "$commit" =~ ^fix ]]; then
- if [[ "$commit" =~ $re_fix_scoped ]]; then
- FIXES="${FIXES}\n- **${BASH_REMATCH[1]}**: ${BASH_REMATCH[2]}"
- else
- MSG="${commit#fix: }"
- FIXES="${FIXES}\n- ${MSG}"
- fi
- elif [[ "$commit" =~ ^refactor ]]; then
- if [[ "$commit" =~ $re_refactor_scoped ]]; then
- REFACTOR="${REFACTOR}\n- **${BASH_REMATCH[1]}**: ${BASH_REMATCH[2]}"
- else
- MSG="${commit#refactor: }"
- REFACTOR="${REFACTOR}\n- ${MSG}"
- fi
- elif [[ "$commit" =~ ^docs ]]; then
- if [[ "$commit" =~ $re_docs_scoped ]]; then
- DOCS="${DOCS}\n- **${BASH_REMATCH[1]}**: ${BASH_REMATCH[2]}"
- else
- MSG="${commit#docs: }"
- DOCS="${DOCS}\n- ${MSG}"
- fi
- else
- OTHER="${OTHER}\n- ${commit}"
- fi
- done <<< "$COMMITS"
-
- # Build release notes
- {
- echo "## What's Changed"
- echo ""
- if [ -n "$FEATURES" ]; then
- echo "### Features"
- echo -e "$FEATURES"
- echo ""
- fi
- if [ -n "$FIXES" ]; then
- echo "### Bug Fixes"
- echo -e "$FIXES"
- echo ""
- fi
- if [ -n "$REFACTOR" ]; then
- echo "### Refactoring"
- echo -e "$REFACTOR"
- echo ""
- fi
- if [ -n "$DOCS" ]; then
- echo "### Documentation"
- echo -e "$DOCS"
- echo ""
- fi
- if [ -n "$OTHER" ]; then
- echo "### Other Changes"
- echo -e "$OTHER"
- echo ""
- fi
- echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/v${PREV_TAG}...v${VERSION}"
- } > /tmp/changelog.md
-
+ bun run script/generate-changelog.ts > /tmp/changelog.md
cat /tmp/changelog.md
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub release
run: |
diff --git a/.opencode/command/publish.md b/.opencode/command/publish.md
index 945ec539..718bf246 100644
--- a/.opencode/command/publish.md
+++ b/.opencode/command/publish.md
@@ -31,9 +31,9 @@ You are the release manager for oh-my-opencode. Execute the FULL publish workflo
{ "id": "sync-remote", "content": "Sync with remote (pull --rebase && push if unpushed commits)", "status": "pending", "priority": "high" },
{ "id": "run-workflow", "content": "Trigger GitHub Actions publish workflow", "status": "pending", "priority": "high" },
{ "id": "wait-workflow", "content": "Wait for workflow completion (poll every 30s)", "status": "pending", "priority": "high" },
- { "id": "verify-release", "content": "Verify GitHub release was created", "status": "pending", "priority": "high" },
- { "id": "draft-release-notes", "content": "Draft enhanced release notes content", "status": "pending", "priority": "high" },
- { "id": "update-release-notes", "content": "Update GitHub release with enhanced notes", "status": "pending", "priority": "high" },
+ { "id": "verify-and-preview", "content": "Verify release created + preview auto-generated changelog & contributor thanks", "status": "pending", "priority": "high" },
+ { "id": "draft-summary", "content": "Draft enhanced release summary (minor/major only, skip for patch)", "status": "pending", "priority": "high" },
+ { "id": "apply-summary", "content": "Prepend enhanced summary to release (minor/major only)", "status": "pending", "priority": "high" },
{ "id": "verify-npm", "content": "Verify npm package published successfully", "status": "pending", "priority": "high" },
{ "id": "wait-platform-workflow", "content": "Wait for publish-platform workflow completion", "status": "pending", "priority": "high" },
{ "id": "verify-platform-binaries", "content": "Verify all 7 platform binary packages published", "status": "pending", "priority": "high" },
@@ -111,102 +111,165 @@ gh run view {run_id} --log-failed
---
-## STEP 5: VERIFY GITHUB RELEASE
+## STEP 5: VERIFY RELEASE & PREVIEW AUTO-GENERATED CONTENT
+
+Two goals: confirm the release exists, then show the user what the workflow already generated.
-Get the new version and verify release exists:
```bash
-# Get new version from package.json (workflow updates it)
+# Pull latest (workflow committed version bump)
git pull --rebase
NEW_VERSION=$(node -p "require('./package.json').version")
-gh release view "v${NEW_VERSION}"
+
+# Verify release exists on GitHub
+gh release view "v${NEW_VERSION}" --json tagName,url --jq '{tag: .tagName, url: .url}'
```
----
-
-## STEP 6: DRAFT ENHANCED RELEASE NOTES
-
-Analyze commits since the previous version and draft release notes following project conventions:
-
-### For PATCH releases:
-Keep simple format - just list commits:
-```markdown
-- {hash} {conventional commit message}
-- ...
-```
-
-### For MINOR releases:
-Use feature-focused format:
-```markdown
-## New Features
-
-### Feature Name
-- Description of what it does
-- Why it matters
-
-## Bug Fixes
-- fix(scope): description
-
-## Improvements
-- refactor(scope): description
-```
-
-### For MAJOR releases:
-Full changelog format:
-```markdown
-# v{version}
-
-Brief description of the release.
-
-## What's New Since v{previous}
-
-### Breaking Changes
-- Description of breaking change
-
-### Features
-- **Feature Name**: Description
-
-### Bug Fixes
-- Description
-
-### Documentation
-- Description
-
-## Migration Guide (if applicable)
-...
-```
-
-**CRITICAL: The enhanced notes must ADD to existing workflow-generated notes, not replace them.**
-
----
-
-## STEP 7: UPDATE GITHUB RELEASE
-
-**ZERO CONTENT LOSS POLICY:**
-- First, fetch the existing release body with `gh release view`
-- Your enhanced notes must be PREPENDED to the existing content
-- **NOT A SINGLE CHARACTER of existing content may be removed or modified**
-- The final release body = `{your_enhanced_notes}\n\n---\n\n{existing_body_exactly_as_is}`
+**After verifying, generate a local preview of the auto-generated content:**
```bash
-# Get existing body
-EXISTING_BODY=$(gh release view "v${NEW_VERSION}" --json body --jq '.body')
+bun run script/generate-changelog.ts
+```
-# Write enhanced notes to temp file (prepend to existing)
-cat > /tmp/release-notes-v${NEW_VERSION}.md << 'EOF'
-{your_enhanced_notes}
+
+After running the preview, present the output to the user and say:
+
+> **The following content is ALREADY included in the release automatically:**
+> - Commit changelog (grouped by feat/fix/refactor)
+> - Contributor thank-you messages (for non-team contributors)
+>
+> You do NOT need to write any of this. It's handled.
+>
+> **For a patch release**, this is sufficient — no additional notes needed.
+> **For a minor/major release**, I'll draft a human-readable summary to prepend above this content.
+
+Wait for the user to acknowledge before proceeding.
+
---
-EOF
+## STEP 6: DRAFT ENHANCED RELEASE SUMMARY
-# Append existing body EXACTLY as-is (zero modifications)
-echo "$EXISTING_BODY" >> /tmp/release-notes-v${NEW_VERSION}.md
+
-# Update release
-gh release edit "v${NEW_VERSION}" --notes-file /tmp/release-notes-v${NEW_VERSION}.md
+| Release Type | Action |
+|-------------|--------|
+| **patch** | SKIP this step entirely. The auto-generated changelog from Step 5 is sufficient. Proceed to Step 8. |
+| **minor** | Draft a concise feature summary. |
+| **major** | Draft a full release narrative with migration notes if applicable. |
+
+
+
+### What You're Writing (and What You're NOT)
+
+You are writing the **headline layer** — a product announcement that sits ABOVE the auto-generated commit log. Think "release blog post", not "git log".
+
+
+- NEVER duplicate commit messages. The auto-generated section already lists every commit.
+- NEVER write generic filler like "Various bug fixes and improvements" or "Several enhancements".
+- ALWAYS focus on USER IMPACT: what can users DO now that they couldn't before?
+- ALWAYS group by THEME or CAPABILITY, not by commit type (feat/fix/refactor).
+- ALWAYS use concrete language: "You can now do X" not "Added X feature".
+
+
+
+
+## What's New
+- feat(auth): add JWT refresh token rotation
+- fix(auth): handle expired token edge case
+- refactor(auth): extract middleware
+
+
+
+## 🔐 Smarter Authentication
+
+Token refresh is now automatic and seamless. Sessions no longer expire mid-task — the system silently rotates credentials in the background. If you've been frustrated by random logouts, this release fixes that.
+
+
+
+## Improvements
+- Various performance improvements
+- Bug fixes and stability enhancements
+
+
+
+## ⚡ 3x Faster Rule Parsing
+
+Rules are now cached by file modification time. If your project has 50+ rule files, you'll notice startup is noticeably faster — we measured a 3x improvement in our test suite.
+
+
+
+### Drafting Process
+
+1. **Analyze** the commit list from Step 5's preview. Identify 2-5 themes that matter to users.
+2. **Write** the summary to `/tmp/release-summary-v${NEW_VERSION}.md`.
+3. **Present** the draft to the user for review and approval before applying.
+
+```bash
+# Write your draft here
+cat > /tmp/release-summary-v${NEW_VERSION}.md << 'SUMMARY_EOF'
+{your_enhanced_summary}
+SUMMARY_EOF
+
+cat /tmp/release-summary-v${NEW_VERSION}.md
```
-**CRITICAL: This is ADDITIVE ONLY. You are adding your notes on top. The existing content remains 100% intact.**
+
+After drafting, ask the user:
+> "Here's the release summary I drafted. This will appear AT THE TOP of the release notes, above the auto-generated commit changelog and contributor thanks. Want me to adjust anything before applying?"
+
+Do NOT proceed to Step 7 without user confirmation.
+
+
+---
+
+## STEP 7: APPLY ENHANCED SUMMARY TO RELEASE
+
+**Skip this step if patch release** — proceed directly to Step 8.
+
+
+The final release note structure:
+
+```
+┌─────────────────────────────────────┐
+│ Enhanced Summary (from Step 6) │ ← You wrote this
+│ - Theme-based, user-impact focused │
+├─────────────────────────────────────┤
+│ --- (separator) │
+├─────────────────────────────────────┤
+│ Auto-generated Commit Changelog │ ← Workflow wrote this
+│ - feat/fix/refactor grouped │
+│ - Contributor thank-you messages │
+└─────────────────────────────────────┘
+```
+
+
+
+- Fetch the existing release body FIRST
+- PREPEND your summary above it
+- The existing auto-generated content must remain 100% INTACT
+- NOT A SINGLE CHARACTER of existing content may be removed or modified
+
+
+```bash
+# 1. Fetch existing auto-generated body
+EXISTING_BODY=$(gh release view "v${NEW_VERSION}" --json body --jq '.body')
+
+# 2. Combine: enhanced summary on top, auto-generated below
+{
+ cat /tmp/release-summary-v${NEW_VERSION}.md
+ echo ""
+ echo "---"
+ echo ""
+ echo "$EXISTING_BODY"
+} > /tmp/final-release-v${NEW_VERSION}.md
+
+# 3. Update the release (additive only)
+gh release edit "v${NEW_VERSION}" --notes-file /tmp/final-release-v${NEW_VERSION}.md
+
+# 4. Confirm
+echo "✅ Release v${NEW_VERSION} updated with enhanced summary."
+gh release view "v${NEW_VERSION}" --json url --jq '.url'
+```
---