#!/usr/bin/env node /** * Audit branding patterns (footers, headers, badges) across all markdown files * Wave 4C: Current-State Audit for unified branding agent */ import fs from "fs"; import path from "path"; import * as yaml from "js-yaml"; import { globSync } from "glob"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const REPO_ROOT = path.join(__dirname, "..", ".."); const MD_FILES_PATTERN = "**/*.md"; // Note: These constants are used for reference/documentation purposes. // The actual detection logic is implemented in the detectFooter() and countBadges() functions below. // Category inference based on file path function inferCategory(filePath) { const relative = path.relative(REPO_ROOT, filePath); if (relative.startsWith(".github")) return "github-config"; if (relative.startsWith("agents")) return "agent-specs"; if (relative.startsWith("instructions")) return "instructions"; if (relative.startsWith("skills")) return "skills"; if (relative.startsWith("workflows")) return "workflows"; if (relative.startsWith("docs")) return "documentation"; if (relative.startsWith("plugins")) return "plugins"; if (relative.startsWith("hooks")) return "hooks"; if (relative.startsWith("cookbook")) return "cookbook"; if (relative.startsWith("scripts")) return "scripts"; if (relative.startsWith("tests")) return "tests"; if (relative.startsWith("ai")) return "ai-configs"; if (relative.startsWith("wceu-2026/agent-slides")) return "slides"; if (relative.startsWith("wceu-2026")) return "wceu-talk"; return "root"; } // Extract frontmatter from markdown function extractFrontmatter(content) { const match = content.match(/^---\n([\s\S]*?)\n---\n/); if (!match) return null; try { return yaml.load(match[1]); } catch (_e) { return null; } } // Check for footer presence function detectFooter(content) { const lines = content.split("\n"); const lastLines = lines.slice(-10); const lastContent = lastLines.join("\n"); // Common footer indicators const footerIndicators = [ /\*Last updated:/i, /\*Maintained by:/i, /\*Made with/i, /\*Generated by/i, /---\s*$/, /\[Contact\]/i, /©/, ]; return footerIndicators.some((pattern) => pattern.test(lastContent)); } // Check for badges function countBadges(content) { const matches = content.match(/!\[.*?\]\(.*?\)/g) || []; return matches.length; } // Scan all markdown files function scanMarkdownFiles() { const files = globSync(path.join(REPO_ROOT, MD_FILES_PATTERN), { ignore: ["**/node_modules/**", "**/.git/**", "**/coverage/**"], }); const results = { totalFiles: files.length, byCategory: {}, filesWithFooters: 0, filesWithBadges: 0, totalBadges: 0, frontmatterCompliance: { hasRequiredFields: 0, missingFields: 0, }, findings: [], }; files.forEach((filePath) => { try { const content = fs.readFileSync(filePath, "utf-8"); const category = inferCategory(filePath); const relative = path.relative(REPO_ROOT, filePath); // Initialize category if (!results.byCategory[category]) { results.byCategory[category] = { count: 0, withFooters: 0, withBadges: 0, frontmatterIssues: [], }; } results.byCategory[category].count += 1; // Check for footer if (detectFooter(content)) { results.filesWithFooters += 1; results.byCategory[category].withFooters += 1; } // Check for badges const badgeCount = countBadges(content); if (badgeCount > 0) { results.filesWithBadges += 1; results.totalBadges += badgeCount; results.byCategory[category].withBadges += 1; } // Check frontmatter compliance const frontmatter = extractFrontmatter(content); const requiredFields = ["file_type", "title", "last_updated"]; const hasAllRequired = requiredFields.every( (field) => frontmatter && frontmatter[field], ); if (hasAllRequired) { results.frontmatterCompliance.hasRequiredFields += 1; } else { results.frontmatterCompliance.missingFields += 1; const missing = requiredFields.filter( (field) => !frontmatter || !frontmatter[field], ); results.byCategory[category].frontmatterIssues.push({ file: relative, missing, }); } } catch (_e) { // Skip files that can't be read } }); return results; } // Generate audit report function generateReport(results) { const report = []; report.push("# Wave 4C: Current-State Audit Report"); report.push(""); report.push("## Executive Summary"); report.push(""); report.push(`- **Total Markdown Files Scanned**: ${results.totalFiles}`); report.push( `- **Files with Footers**: ${results.filesWithFooters} (${((results.filesWithFooters / results.totalFiles) * 100).toFixed(1)}%)`, ); report.push( `- **Files with Badges**: ${results.filesWithBadges} (${((results.filesWithBadges / results.totalFiles) * 100).toFixed(1)}%)`, ); report.push(`- **Total Badges Found**: ${results.totalBadges}`); report.push(""); report.push("## Frontmatter Compliance"); report.push(""); const compliantPct = ( (results.frontmatterCompliance.hasRequiredFields / results.totalFiles) * 100 ).toFixed(1); report.push( `- **Files with Required Fields**: ${results.frontmatterCompliance.hasRequiredFields} (${compliantPct}%)`, ); report.push( `- **Files Missing Required Fields**: ${results.frontmatterCompliance.missingFields}`, ); report.push(""); report.push("## By Category"); report.push(""); Object.entries(results.byCategory).forEach(([category, stats]) => { const footerPct = ((stats.withFooters / stats.count) * 100).toFixed(1); const badgePct = ((stats.withBadges / stats.count) * 100).toFixed(1); report.push(`### ${category}`); report.push(`- **Total Files**: ${stats.count}`); report.push(`- **With Footers**: ${stats.withFooters} (${footerPct}%)`); report.push(`- **With Badges**: ${stats.withBadges} (${badgePct}%)`); report.push(""); }); report.push("## Recommendations"); report.push(""); report.push("### High Priority"); report.push("- Add missing required frontmatter fields to files"); report.push("- Standardize footer format across all documents"); report.push("- Implement category-aware footer selection"); report.push(""); report.push("### Medium Priority"); report.push("- Update stale footer content"); report.push("- Ensure badge placement consistency"); report.push("- Validate header formatting"); report.push(""); report.push("### Low Priority"); report.push("- Enhance visual consistency"); report.push("- Add missing badges where appropriate"); report.push(""); return report.join("\n"); } // Main execution try { console.log("Starting Wave 4C audit..."); const results = scanMarkdownFiles(); const report = generateReport(results); const reportPath = path.join( REPO_ROOT, ".github", "reports", "wave-4c-audit-report.md", ); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, report); console.log(`✅ Audit complete! Report saved to: ${reportPath}`); console.log(""); console.log(`📊 Summary:`); console.log(` - Files scanned: ${results.totalFiles}`); console.log( ` - Files with footers: ${results.filesWithFooters} (${((results.filesWithFooters / results.totalFiles) * 100).toFixed(1)}%)`, ); console.log( ` - Files with badges: ${results.filesWithBadges} (${((results.filesWithBadges / results.totalFiles) * 100).toFixed(1)}%)`, ); console.log( ` - Frontmatter compliant: ${results.frontmatterCompliance.hasRequiredFields} (${((results.frontmatterCompliance.hasRequiredFields / results.totalFiles) * 100).toFixed(1)}%)`, ); } catch (error) { console.error("❌ Audit failed:", error.message); process.exit(1); }