mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 14:16:28 +03:00
chore(release): print a tweet-ready string after a successful release
Pulls the bold lead text from each <li><strong>...</strong> in the changelog entry as a tweet-grade summary, fits as many bullets as possible under the 280-char limit (first highlight always wins since it's already the most user-facing line), and prints inside a labeled box with a live char count so the user can copy-paste into @impeccable_ai. Adds tweetHeader and tweetCta to each component config (skill / cli / extension); skill uses the npx skills install line, CLI uses npm i -g, extension drops the CTA entirely (link to the release page is enough since the user has to upload to Chrome Web Store separately). Falls back to header + URL only if even the first highlight overflows. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c332c7aa91
commit
856b90e52b
@@ -26,6 +26,8 @@ const COMPONENTS = {
|
||||
buildCmd: 'bun run build',
|
||||
artifacts: ['dist/universal.zip'],
|
||||
postReleaseHint: null,
|
||||
tweetHeader: (v) => `Impeccable v${v} is out.`,
|
||||
tweetCta: 'Install / update: npx skills add pbakaus/impeccable',
|
||||
},
|
||||
cli: {
|
||||
manifest: 'package.json',
|
||||
@@ -35,6 +37,8 @@ const COMPONENTS = {
|
||||
buildCmd: null,
|
||||
artifacts: [],
|
||||
postReleaseHint: 'Run `npm publish` next to push the package to the npm registry.',
|
||||
tweetHeader: (v) => `Impeccable CLI v${v} is out.`,
|
||||
tweetCta: 'npm i -g impeccable',
|
||||
},
|
||||
extension: {
|
||||
manifest: 'extension/manifest.json',
|
||||
@@ -44,9 +48,14 @@ const COMPONENTS = {
|
||||
buildCmd: 'bun run build:extension',
|
||||
artifacts: ['dist/extension.zip'],
|
||||
postReleaseHint: 'Upload `dist/extension.zip` to the Chrome Web Store dashboard to publish.',
|
||||
tweetHeader: (v) => `Impeccable Chrome extension v${v} is out.`,
|
||||
tweetCta: null,
|
||||
},
|
||||
};
|
||||
|
||||
const REPO_URL = 'https://github.com/pbakaus/impeccable';
|
||||
const TWEET_LIMIT = 280;
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const dryRun = args.includes('--dry-run');
|
||||
const component = args.find((a) => !a.startsWith('--'));
|
||||
@@ -196,6 +205,60 @@ if (cfg.postReleaseHint) {
|
||||
console.log(`\n→ Next step: ${cfg.postReleaseHint}`);
|
||||
}
|
||||
|
||||
const tweet = renderTweet(cfg, version, entryHtml, tag);
|
||||
console.log(`\n--- Tweet (${tweet.length}/${TWEET_LIMIT} chars) for @impeccable_ai ---`);
|
||||
console.log(tweet);
|
||||
console.log('--- end tweet ---');
|
||||
|
||||
// Pull the bold lead text from each changelog bullet. Each <li> reads
|
||||
// "<strong>Headline.</strong> Body...", so the strong text alone is a
|
||||
// tweet-grade summary. Returns a list ordered by appearance.
|
||||
function extractHighlights(entryHtml) {
|
||||
const highlights = [];
|
||||
const liRe = /<li>([\s\S]*?)<\/li>/g;
|
||||
let match;
|
||||
while ((match = liRe.exec(entryHtml))) {
|
||||
const strong = match[1].match(/<strong>([\s\S]*?)<\/strong>/);
|
||||
if (!strong) continue;
|
||||
const text = strong[1]
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.replace(/×/g, '×')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/[.!?]+\s*$/, '')
|
||||
.trim();
|
||||
if (text) highlights.push(text);
|
||||
}
|
||||
return highlights;
|
||||
}
|
||||
|
||||
function renderTweet(cfg, version, entryHtml, tag) {
|
||||
const releaseUrl = `${REPO_URL}/releases/tag/${tag}`;
|
||||
const header = cfg.tweetHeader(version);
|
||||
const highlights = extractHighlights(entryHtml);
|
||||
const tail = [cfg.tweetCta, releaseUrl].filter(Boolean).join('\n');
|
||||
|
||||
// Greedy: include as many highlights as fit. Always include the URL.
|
||||
let bullets = '';
|
||||
const bulletPrefix = '• ';
|
||||
for (const h of highlights) {
|
||||
const candidate = bullets + bulletPrefix + h + '\n';
|
||||
const draft = [header, '', candidate.trimEnd(), '', tail].join('\n');
|
||||
if (draft.length > TWEET_LIMIT) break;
|
||||
bullets = candidate;
|
||||
}
|
||||
|
||||
// Fallback if even the first highlight overflows: drop bullets entirely.
|
||||
if (!bullets) {
|
||||
return [header, '', tail].join('\n');
|
||||
}
|
||||
return [header, '', bullets.trimEnd(), '', tail].join('\n');
|
||||
}
|
||||
|
||||
function htmlToMarkdown(html) {
|
||||
let md = html;
|
||||
md = md.replace(/<div class="changelog-version-header"[\s\S]*?<\/div>/, '');
|
||||
|
||||
Reference in New Issue
Block a user