add-doc-frontmatter.js
1 #!/usr/bin/env node 2 3 /** 4 * Add YAML frontmatter to documentation files 5 * Usage: node scripts/add-doc-frontmatter.js <file-path> 6 */ 7 8 import fs from 'fs'; 9 import path from 'path'; 10 11 // Category mapping 12 const CATEGORIES = { 13 '01-getting-started': 'getting-started', 14 '02-architecture': 'architecture', 15 '03-pipeline': 'pipeline', 16 '04-proposals': 'proposals', 17 '05-outreach': 'outreach', 18 '06-automation': 'automation', 19 '07-integrations': 'integrations', 20 '08-operations': 'operations', 21 '09-business': 'business', 22 '90-archive': 'archive', 23 }; 24 25 // File to title mapping (can be customized) 26 function fileToTitle(filename) { 27 return filename 28 .replace('.md', '') 29 .split('-') 30 .map(word => word.charAt(0).toUpperCase() + word.slice(1)) 31 .join(' '); 32 } 33 34 // Get related files from doc content (basic grep for common patterns) 35 function getRelatedFiles(content) { 36 const related = []; 37 const patterns = [ 38 /src\/[a-zA-Z0-9/_\-.]+\.js/g, 39 /tests\/[a-zA-Z0-9/_\-.]+\.test\.js/g, 40 /db\/migrations\/[a-zA-Z0-9_-]+\.sql/g, 41 /scripts\/[a-zA-Z0-9/_\-.]+\.(js|sh)/g, 42 ]; 43 44 patterns.forEach(pattern => { 45 const matches = content.match(pattern); 46 if (matches) { 47 related.push(...matches); 48 } 49 }); 50 51 return [...new Set(related)].slice(0, 5); // Limit to 5 unique files 52 } 53 54 // Extract tags from filename and content 55 function getTags(filename, content) { 56 const tags = []; 57 const lowerContent = content.toLowerCase(); 58 59 // Add filename-based tags 60 const filenameParts = filename.replace('.md', '').split('-'); 61 tags.push(...filenameParts); 62 63 // Add content-based tags 64 if (lowerContent.includes('cron')) tags.push('cron', 'scheduling'); 65 if (lowerContent.includes('test')) tags.push('testing'); 66 if (lowerContent.includes('security')) tags.push('security'); 67 if (lowerContent.includes('database')) tags.push('database'); 68 if (lowerContent.includes('api')) tags.push('api'); 69 if (lowerContent.includes('ai') || lowerContent.includes('llm')) tags.push('ai', 'llm'); 70 if (lowerContent.includes('email')) tags.push('email'); 71 if (lowerContent.includes('sms')) tags.push('sms'); 72 if (lowerContent.includes('scoring')) tags.push('scoring'); 73 if (lowerContent.includes('proposal')) tags.push('proposals'); 74 if (lowerContent.includes('outreach')) tags.push('outreach'); 75 76 return [...new Set(tags)].slice(0, 8); // Limit to 8 unique tags 77 } 78 79 // Detect replaces from content 80 function getReplaces(content) { 81 const replaces = []; 82 const replaceMatch = content.match(/\*\*Replaces:\*\*\s+([^\n]+)/); 83 if (replaceMatch) { 84 const files = replaceMatch[1].split(',').map(f => f.trim()); 85 replaces.push(...files); 86 } 87 return replaces; 88 } 89 90 function addFrontmatter(filePath) { 91 const content = fs.readFileSync(filePath, 'utf8'); 92 93 // Check if frontmatter already exists 94 if (content.startsWith('---\n')) { 95 console.log(`Skipping ${filePath} (already has frontmatter)`); 96 return; 97 } 98 99 const filename = path.basename(filePath); 100 const dirPath = path.dirname(filePath); 101 const categoryDir = path.basename(dirPath); 102 const category = CATEGORIES[categoryDir] || 'other'; 103 104 const title = fileToTitle(filename); 105 const relatedFiles = getRelatedFiles(content); 106 const tags = getTags(filename, content); 107 const replaces = getReplaces(content); 108 109 // Build frontmatter 110 const frontmatter = [ 111 '---', 112 `title: "${title}"`, 113 `category: "${category}"`, 114 `last_verified: "2026-02-15"`, 115 ]; 116 117 if (relatedFiles.length > 0) { 118 frontmatter.push('related_files:'); 119 relatedFiles.forEach(file => { 120 frontmatter.push(` - "${file}"`); 121 }); 122 } 123 124 if (tags.length > 0) { 125 frontmatter.push(`tags: [${tags.map(t => `"${t}"`).join(', ')}]`); 126 } 127 128 const status = category === 'archive' ? 'archived' : 'current'; 129 frontmatter.push(`status: "${status}"`); 130 131 if (replaces.length > 0) { 132 frontmatter.push(`replaces: [${replaces.map(r => `"${r}"`).join(', ')}]`); 133 } 134 135 frontmatter.push('---', ''); 136 137 const newContent = frontmatter.join('\n') + content; 138 139 fs.writeFileSync(filePath, newContent, 'utf8'); 140 console.log(`✓ Added frontmatter to ${filePath}`); 141 } 142 143 // Main execution 144 const filePath = process.argv[2]; 145 if (!filePath) { 146 console.error('Usage: node add-doc-frontmatter.js <file-path>'); 147 process.exit(1); 148 } 149 150 addFrontmatter(filePath);