diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0eb13951..c24fd2ad 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "plugins": [ { "name": "claude-mem", - "version": "7.4.2", + "version": "7.4.3", "source": "./plugin", "description": "Persistent memory system for Claude Code - context compression across sessions" } diff --git a/.claude/skills/version-bump/SKILL.md b/.claude/skills/version-bump/SKILL.md index 38b61df9..8b598fd9 100644 --- a/.claude/skills/version-bump/SKILL.md +++ b/.claude/skills/version-bump/SKILL.md @@ -42,6 +42,7 @@ See [operations/workflow.md](operations/workflow.md) for detailed step-by-step p 7. Commit and create git tag 8. Push and create GitHub release 9. Generate CHANGELOG.md from releases and commit +10. Post Discord notification ## Common Scenarios @@ -57,6 +58,7 @@ See [operations/scenarios.md](operations/scenarios.md) for examples: - Create git tag with format `vX.Y.Z` - Create GitHub release from the tag - Generate CHANGELOG.md from releases after creating release +- Post Discord notification after release - Ask user if version type is unclear **NEVER:** @@ -74,6 +76,7 @@ Before considering the task complete: - [ ] Commit and tags pushed to remote - [ ] GitHub release created from the tag - [ ] CHANGELOG.md generated and committed +- [ ] Discord notification sent ## Reference Commands diff --git a/.claude/skills/version-bump/operations/workflow.md b/.claude/skills/version-bump/operations/workflow.md index c48318ee..6e2e4137 100644 --- a/.claude/skills/version-bump/operations/workflow.md +++ b/.claude/skills/version-bump/operations/workflow.md @@ -197,6 +197,17 @@ git push - No manual editing required - Single source of truth: GitHub releases +## Step 11: Discord Notification + +Post release announcement to the Discord updates channel: + +```bash +# Send Discord notification with release details +npm run discord:notify vX.Y.Z +``` + +This fetches the release notes from GitHub and posts a formatted embed to the Discord updates channel configured in `.env`. + ## Verification After completing all steps, verify: diff --git a/package.json b/package.json index f9082bf5..50c67d81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-mem", - "version": "7.4.2", + "version": "7.4.3", "description": "Memory compression system for Claude Code - persist context across sessions", "keywords": [ "claude", @@ -46,6 +46,7 @@ "worker:status": "bun plugin/scripts/worker-cli.js status", "worker:logs": "tail -n 50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log", "changelog:generate": "node scripts/generate-changelog.js", + "discord:notify": "node scripts/discord-release-notify.js", "usage:analyze": "node scripts/analyze-usage.js", "usage:today": "node scripts/analyze-usage.js $(date +%Y-%m-%d)", "translate-readme": "bun scripts/translate-readme/cli.ts -v -o docs/i18n README.md", diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 1298948b..7ec23ee3 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "claude-mem", - "version": "7.4.2", + "version": "7.4.3", "description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions", "author": { "name": "Alex Newman" diff --git a/scripts/discord-release-notify.js b/scripts/discord-release-notify.js new file mode 100644 index 00000000..8d8dca23 --- /dev/null +++ b/scripts/discord-release-notify.js @@ -0,0 +1,137 @@ +#!/usr/bin/env node + +/** + * Post release notification to Discord + * + * Usage: + * node scripts/discord-release-notify.js v7.4.2 + * node scripts/discord-release-notify.js v7.4.2 "Custom release notes" + * + * Requires DISCORD_UPDATES_WEBHOOK in .env file + */ + +import { execSync } from 'child_process'; +import { readFileSync, existsSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const projectRoot = resolve(__dirname, '..'); + +function loadEnv() { + const envPath = resolve(projectRoot, '.env'); + if (!existsSync(envPath)) { + console.error('❌ .env file not found'); + process.exit(1); + } + + const envContent = readFileSync(envPath, 'utf-8'); + const webhookMatch = envContent.match(/DISCORD_UPDATES_WEBHOOK=(.+)/); + + if (!webhookMatch) { + console.error('❌ DISCORD_UPDATES_WEBHOOK not found in .env'); + process.exit(1); + } + + return webhookMatch[1].trim(); +} + +function getReleaseNotes(version) { + try { + const notes = execSync(`gh release view ${version} --json body --jq '.body'`, { + encoding: 'utf-8', + cwd: projectRoot, + }).trim(); + return notes; + } catch { + return null; + } +} + +function cleanNotes(notes) { + // Remove Claude Code footer and clean up + return notes + .replace(/🤖 Generated with \[Claude Code\].*$/s, '') + .replace(/---\n*$/s, '') + .trim(); +} + +function truncate(text, maxLength) { + if (text.length <= maxLength) return text; + return text.slice(0, maxLength - 3) + '...'; +} + +async function postToDiscord(webhookUrl, version, notes) { + const cleanedNotes = notes ? cleanNotes(notes) : 'No release notes available.'; + const repoUrl = 'https://github.com/thedotmack/claude-mem'; + + const payload = { + embeds: [ + { + title: `🚀 claude-mem ${version} released`, + url: `${repoUrl}/releases/tag/${version}`, + description: truncate(cleanedNotes, 2000), + color: 0x7c3aed, // Purple + fields: [ + { + name: '📦 Install', + value: 'Update via Claude Code plugin marketplace', + inline: true, + }, + { + name: '📚 Docs', + value: '[docs.claude-mem.ai](https://docs.claude-mem.ai)', + inline: true, + }, + ], + footer: { + text: 'claude-mem • Persistent memory for Claude Code', + }, + timestamp: new Date().toISOString(), + }, + ], + }; + + const response = await fetch(webhookUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Discord API error: ${response.status} - ${errorText}`); + } + + return true; +} + +async function main() { + const version = process.argv[2]; + const customNotes = process.argv[3]; + + if (!version) { + console.error('Usage: node scripts/discord-release-notify.js [notes]'); + console.error('Example: node scripts/discord-release-notify.js v7.4.2'); + process.exit(1); + } + + console.log(`📣 Posting release notification for ${version}...`); + + const webhookUrl = loadEnv(); + const notes = customNotes || getReleaseNotes(version); + + if (!notes && !customNotes) { + console.warn('⚠️ Could not fetch release notes from GitHub, proceeding without them'); + } + + try { + await postToDiscord(webhookUrl, version, notes); + console.log('✅ Discord notification sent successfully!'); + } catch (error) { + console.error('❌ Failed to send Discord notification:', error.message); + process.exit(1); + } +} + +main();