#!/usr/bin/env node // Personalization score calculator for linkedin-studio plugin // Checks 8 asset categories for real user data vs placeholder templates // Standalone: outputs SCORE:N|M/8 assets personalized // Import: export function calculateScore(pluginRoot) => { score, personalized, categories } import { readFileSync, existsSync, readdirSync } from 'node:fs'; import { join, basename, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); export function calculateScore(pluginRoot) { let score = 0; let personalized = 0; const categories = 8; // --- 1. Voice samples (25 points) --- const voiceFile = join(pluginRoot, 'assets', 'voice-samples', 'authentic-voice-samples.md'); if (existsSync(voiceFile)) { const content = readFileSync(voiceFile, 'utf-8'); const lineCount = content.split('\n').length; if (lineCount > 50 && !content.includes('[Your Name]')) { score += 25; personalized += 1; } } // --- 2. User profile (20 points) --- const profileFile = join(pluginRoot, 'config', 'user-profile.local.md'); if (existsSync(profileFile)) { const content = readFileSync(profileFile, 'utf-8'); const placeholderCount = (content.match(/\[Your /g) || []).length; if (placeholderCount < 3) { score += 20; personalized += 1; } } // --- 3. Case studies (15 points) --- const caseDir = join(pluginRoot, 'assets', 'case-studies'); if (existsSync(caseDir)) { let realCases = 0; try { for (const f of readdirSync(caseDir)) { if (!f.endsWith('.md')) continue; if (f === 'case-study-template.md') continue; realCases++; } } catch { /* ignore */ } if (realCases >= 2) { score += 15; personalized += 1; } else if (realCases >= 1) { score += 8; } } // --- 4. Frameworks (10 points) --- const fwDir = join(pluginRoot, 'assets', 'frameworks'); if (existsSync(fwDir)) { let realFw = 0; try { for (const f of readdirSync(fwDir)) { if (!f.endsWith('.md')) continue; if (f === 'framework-template.md') continue; realFw++; } } catch { /* ignore */ } if (realFw >= 2) { score += 10; personalized += 1; } else if (realFw >= 1) { score += 5; } } // --- 5. High-engagement posts (10 points) --- const postsFile = join(pluginRoot, 'assets', 'examples', 'high-engagement-posts.md'); if (existsSync(postsFile)) { const content = readFileSync(postsFile, 'utf-8'); const postCount = (content.match(/^## Post [0-9]/gm) || []).length; if (postCount >= 3) { score += 10; personalized += 1; } else if (postCount >= 1) { score += 4; } } // --- 6. Demographics (8 points) --- const demoFile = join(pluginRoot, 'assets', 'audience-insights', 'demographics.md'); if (existsSync(demoFile)) { const content = readFileSync(demoFile, 'utf-8'); const placeholderCount = (content.match(/\[Industry name\]|\[Function\]|\[Country\]|\[X\]%/g) || []).length; if (placeholderCount < 5) { score += 8; personalized += 1; } } // --- 7. Engagement patterns (7 points) --- const patternsFile = join(pluginRoot, 'assets', 'audience-insights', 'engagement-patterns.md'); if (existsSync(patternsFile)) { const content = readFileSync(patternsFile, 'utf-8'); const placeholderCount = (content.match(/\[Day\]|\[Time\]|\[Topic\]|\[Format\]|\[Hook type\]/g) || []).length; if (placeholderCount < 5) { score += 7; personalized += 1; } } // --- 8. Post templates (5 points) --- const templatesFile = join(pluginRoot, 'assets', 'templates', 'my-post-templates.md'); if (existsSync(templatesFile)) { const content = readFileSync(templatesFile, 'utf-8'); const unfilled = (content.match(/\[Name - e\.g\./g) || []).length; const totalTemplates = (content.match(/^## Template [0-9]/gm) || []).length; const filled = totalTemplates - unfilled; if (filled >= 2) { score += 5; personalized += 1; } else if (filled >= 1) { score += 2; } } return { score, personalized, categories }; } // Standalone execution (guarded to prevent stdout contamination on import) if (process.argv[1] === fileURLToPath(import.meta.url)) { const pluginRoot = join(__dirname, '..', '..'); const { score, personalized, categories } = calculateScore(pluginRoot); process.stdout.write(`SCORE:${score}|${personalized}/${categories} assets personalized\n`); }