#!/usr/bin/env node /** * Analyze ProhibitedTermsScanner violations * Filter to only show violations that need user review */ const { execSync } = require('child_process'); // Run scanner and capture output (ignore exit code - scanner exits with 1 if violations found) let output; try { output = execSync('node scripts/framework-components/ProhibitedTermsScanner.js --details', { encoding: 'utf8' }); } catch (err) { // Scanner exits with code 1 when violations found (for pre-commit hooks) output = err.stdout; } // Parse violations const violations = []; const lines = output.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.match(/^ \/home\/theflow\/projects\/tractatus\//)) { const filePath = line.trim().split(':')[0]; const lineNum = line.trim().split(':')[1]; // Get next 4 lines for context const rule = lines[i + 1]?.match(/Rule: (inst_\d+)/)?.[1]; const found = lines[i + 2]?.match(/Found: "(.+)"/)?.[1]; const context = lines[i + 3]?.match(/Context: (.+)/)?.[1]; const suggestion = lines[i + 4]?.match(/Suggestion: (.+)/)?.[1]; if (rule && found) { violations.push({ file: filePath.replace('/home/theflow/projects/tractatus/', ''), line: lineNum, rule, found, context: context || '', suggestion: suggestion || '' }); } } } // Filter to only relevant violations const relevantViolations = violations.filter(v => { const file = v.file; // Public-facing UI if (file.startsWith('public/')) return true; // GitHub repository files if (file === 'README.md') return true; if (file === 'CLAUDE.md') return true; if (file.startsWith('governance/')) return true; if (file.startsWith('docs/markdown/')) return true; // Published docs return false; }); // Categorize by type and file const byFile = {}; relevantViolations.forEach(v => { if (!byFile[v.file]) { byFile[v.file] = { inst_017: [], inst_018: [], inst_016: [] }; } byFile[v.file][v.rule].push(v); }); // Print summary console.log('\n═══════════════════════════════════════════════════════════'); console.log(' VIOLATIONS REQUIRING REVIEW'); console.log('═══════════════════════════════════════════════════════════\n'); console.log(`Total violations in codebase: ${violations.length}`); console.log(`Violations requiring review: ${relevantViolations.length}`); console.log(`Files to review: ${Object.keys(byFile).length}\n`); console.log('───────────────────────────────────────────────────────────'); console.log('CATEGORIZED BY FILE AND VIOLATION TYPE'); console.log('───────────────────────────────────────────────────────────\n'); const fileCategories = { 'Public UI Files': [], 'GitHub Repo Files': [], 'Published Documentation': [] }; Object.keys(byFile).forEach(file => { if (file.startsWith('public/')) { fileCategories['Public UI Files'].push(file); } else if (file === 'README.md' || file === 'CLAUDE.md' || file.startsWith('governance/')) { fileCategories['GitHub Repo Files'].push(file); } else { fileCategories['Published Documentation'].push(file); } }); Object.entries(fileCategories).forEach(([category, files]) => { if (files.length === 0) return; console.log(`\n📁 ${category.toUpperCase()}\n`); files.forEach(file => { const fileCounts = byFile[file]; const total = fileCounts.inst_017.length + fileCounts.inst_018.length + fileCounts.inst_016.length; console.log(` ${file} (${total} violations)`); if (fileCounts.inst_017.length > 0) { console.log(` inst_017 (Absolute Assurance): ${fileCounts.inst_017.length}`); fileCounts.inst_017.slice(0, 3).forEach(v => { console.log(` Line ${v.line}: "${v.found}" → ${v.suggestion}`); }); if (fileCounts.inst_017.length > 3) { console.log(` ... and ${fileCounts.inst_017.length - 3} more`); } } if (fileCounts.inst_018.length > 0) { console.log(` inst_018 (Unverified Claims): ${fileCounts.inst_018.length}`); fileCounts.inst_018.slice(0, 3).forEach(v => { console.log(` Line ${v.line}: "${v.found}" → ${v.suggestion}`); }); if (fileCounts.inst_018.length > 3) { console.log(` ... and ${fileCounts.inst_018.length - 3} more`); } } if (fileCounts.inst_016.length > 0) { console.log(` inst_016 (Fabricated Statistics): ${fileCounts.inst_016.length}`); fileCounts.inst_016.slice(0, 3).forEach(v => { console.log(` Line ${v.line}: "${v.found}" → ${v.suggestion}`); }); if (fileCounts.inst_016.length > 3) { console.log(` ... and ${fileCounts.inst_016.length - 3} more`); } } console.log(''); }); }); console.log('═══════════════════════════════════════════════════════════'); console.log('SUMMARY BY VIOLATION TYPE'); console.log('═══════════════════════════════════════════════════════════\n'); const totals = { inst_017: relevantViolations.filter(v => v.rule === 'inst_017').length, inst_018: relevantViolations.filter(v => v.rule === 'inst_018').length, inst_016: relevantViolations.filter(v => v.rule === 'inst_016').length }; console.log(`inst_017 (Absolute Assurance - "guarantee", "never fails"): ${totals.inst_017}`); console.log(`inst_018 (Unverified Claims - "production-ready", "battle-tested"): ${totals.inst_018}`); console.log(`inst_016 (Fabricated Statistics - percentages without sources): ${totals.inst_016}`); console.log(`\nTotal: ${totals.inst_017 + totals.inst_018 + totals.inst_016}\n`); console.log('═══════════════════════════════════════════════════════════\n');