#!/usr/bin/env node /** * audit-all.mjs — Three-Layer Protocol Audit Orchestrator * * Runs all three protocol audits sequentially: * 1. AGENTS.md — Execution Protocol * 2. GOVERNANCE.md — Execution Governance * 3. PORTFOLIO.md — Portfolio Management * * Outputs: * - Console: colorized summary with PASS / WARN / FAIL * - File: audit-report.md (generated in workspace root) * * Usage: * node scripts/audit-all.mjs * * npm script (add to package.json): * "audit": "node scripts/audit-all.mjs" */ import { fileURLToPath } from 'url'; import path from 'path'; import fs from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const WORKSPACE = path.resolve(__dirname, '..'); // Dynamic imports for the three audit modules const [agentsMod, govMod, portMod] = await Promise.all([ import(path.join(__dirname, 'audit-agents.mjs')), import(path.join(__dirname, 'audit-governance.mjs')), import(path.join(__dirname, 'audit-portfolio.mjs')), ]); /** @returns {{ layer: string, checks: import('./audit-agents.mjs').CheckResult[] }[]} */ function runAll() { const layers = [ { layer: 'Execution Protocol (AGENTS.md)', fn: () => agentsMod.runAgentAudit(WORKSPACE) }, { layer: 'Execution Governance (GOVERNANCE.md)', fn: () => govMod.runGovernanceAudit(WORKSPACE) }, { layer: 'Portfolio Management (PORTFOLIO.md)', fn: () => portMod.runPortfolioAudit(WORKSPACE) }, ]; return layers.map(({ layer, fn }) => ({ layer, checks: fn() })); } /** Colorized console output */ function printConsole(results) { let totalPass = 0, totalWarn = 0, totalFail = 0, totalChecks = 0; for (const { layer, checks } of results) { // Skip summary rows for individual counts const realChecks = checks.filter(c => !c.id.endsWith('-SUMMARY')); const pass = realChecks.filter(c => c.status === 'PASS').length; const warn = realChecks.filter(c => c.status === 'WARN').length; const fail = realChecks.filter(c => c.status === 'FAIL').length; totalPass += pass; totalWarn += warn; totalFail += fail; totalChecks += realChecks.length; const overall = fail > 0 ? '\x1b[31mFAIL\x1b[0m' : warn > 0 ? '\x1b[33mWARN\x1b[0m' : '\x1b[32mPASS\x1b[0m'; console.log(`\n\x1b[1m${'='.repeat(60)}\x1b[0m`); console.log(`\x1b[1m ${layer}\x1b[0m ${overall}`); console.log(`\x1b[1m${'='.repeat(60)}\x1b[0m`); for (const r of checks) { if (r.id.endsWith('-SUMMARY')) continue; const icon = r.status === 'PASS' ? '\x1b[32m✅\x1b[0m' : r.status === 'WARN' ? '\x1b[33m⚠️\x1b[0m' : '\x1b[31m❌\x1b[0m'; const statusTag = r.status === 'PASS' ? '\x1b[32mPASS\x1b[0m' : r.status === 'WARN' ? '\x1b[33mWARN\x1b[0m' : '\x1b[31mFAIL\x1b[0m'; console.log(` ${icon} [${statusTag}] [${r.id}] ${r.name}`); console.log(` ${r.evidence}`); if (r.suggestion) console.log(` \x1b[90m→ ${r.suggestion}\x1b[0m`); console.log(); } } // Final summary console.log(`\x1b[1m${'='.repeat(60)}\x1b[0m`); console.log(`\x1b[1m THREE-LAYER AUDIT COMPLETE\x1b[0m`); console.log(`\x1b[1m${'='.repeat(60)}\x1b[0m`); console.log(` Total checks: ${totalChecks}`); console.log(` \x1b[32mPASS: ${totalPass}\x1b[0m`); console.log(` \x1b[33mWARN: ${totalWarn}\x1b[0m`); console.log(` \x1b[31mFAIL: ${totalFail}\x1b[0m`); const verdict = totalFail > 0 ? '\x1b[31mFAIL' : totalWarn > 0 ? '\x1b[33mWARN' : '\x1b[32mPASS'; console.log(`\n VERDICT: ${verdict}\x1b[0m`); console.log(); } /** Generate audit-report.md */ function writeReport(results) { const reportPath = path.join(WORKSPACE, 'audit-report.md'); const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19) + ' +08:00'; let md = `# Audit Report — ${timestamp}\n\n`; md += `> Generated by \`scripts/audit-all.mjs\`\n\n`; md += `## Global Summary\n\n`; let totalPass = 0, totalWarn = 0, totalFail = 0, totalChecks = 0; // First pass: aggregate all checks across layers const allChecksFlat = []; for (const { layer, checks } of results) { for (const r of checks) { if (r.id.endsWith('-SUMMARY')) continue; allChecksFlat.push({ ...r, layer }); if (r.status === 'PASS') totalPass++; else if (r.status === 'WARN') totalWarn++; else if (r.status === 'FAIL') totalFail++; totalChecks++; } } md += `| Metric | Value |\n|--------|-------|\n`; md += `| Total Checks | ${totalChecks} |\n`; md += `| ✅ PASS | ${totalPass} |\n`; md += `| ⚠️ WARN | ${totalWarn} |\n`; md += `| ❌ FAIL | ${totalFail} |\n`; md += `| **Verdict** | **${totalFail > 0 ? '❌ FAIL' : totalWarn > 0 ? '⚠️ WARN' : '✅ PASS'}** |\n\n`; const verdictOverall = totalFail > 0 ? 'FAIL' : totalWarn > 0 ? 'WARN' : 'PASS'; md += `**Verdict: ${verdictOverall}**\n\n`; // Layer-by-layer tables for (const { layer, checks } of results) { const real = checks.filter(c => !c.id.endsWith('-SUMMARY')); const lp = real.filter(c => c.status === 'PASS').length; const lw = real.filter(c => c.status === 'WARN').length; const lf = real.filter(c => c.status === 'FAIL').length; const lv = lf > 0 ? '❌ FAIL' : lw > 0 ? '⚠️ WARN' : '✅ PASS'; md += `## ${layer} — ${lv}\n\n`; md += `| ID | Name | Status | Evidence | Suggestion |\n`; md += `|----|------|--------|----------|------------|\n`; for (const r of real) { const icon = r.status === 'PASS' ? '✅' : r.status === 'WARN' ? '⚠️' : '❌'; const sug = r.suggestion ? r.suggestion.replace(/\|/g, '\\|') : ''; const ev = r.evidence.replace(/\|/g, '\\|').replace(/\n/g, ' ').slice(0, 120); md += `| ${r.id} | ${r.name} | ${icon} ${r.status} | ${ev} | ${sug} |\n`; } md += `| **Layer Summary** | **${lp} PASS / ${lw} WARN / ${lf} FAIL** | **${lv}** | | |\n\n`; } // Fails list const fails = allChecksFlat.filter(c => c.status === 'FAIL'); if (fails.length > 0) { md += `## ❌ Failures (repair required)\n\n`; for (const f of fails) { md += `- **[${f.id}]** ${f.name} — ${f.evidence}\n`; if (f.suggestion) md += ` - *Repair:* ${f.suggestion}\n`; } md += '\n'; } // Warnings list const warns = allChecksFlat.filter(c => c.status === 'WARN'); if (warns.length > 0) { md += `## ⚠️ Warnings (improvement recommended)\n\n`; for (const w of warns) { md += `- **[${w.id}]** ${w.name} — ${w.evidence}\n`; if (w.suggestion) md += ` - *Suggestion:* ${w.suggestion}\n`; } md += '\n'; } // Coverage summary md += `## Coverage\n\n`; md += `| Layer | Sections | Checks |\n|-------|----------|--------|\n`; const agentsReal = results[0].checks.filter(c => !c.id.endsWith('-SUMMARY')); const govReal = results[1].checks.filter(c => !c.id.endsWith('-SUMMARY')); const portReal = results[2].checks.filter(c => !c.id.endsWith('-SUMMARY')); md += `| AGENTS.md | §0-§7, Learning Loop, Context Budget | ${agentsReal.length} |\n`; md += `| GOVERNANCE.md | §1-§12 | ${govReal.length} |\n`; md += `| PORTFOLIO.md | §1-§14 | ${portReal.length} |\n`; md += `| **Total** | **3 layers** | **${totalChecks}** |\n\n`; md += `---\n*Audit complete. ${timestamp}*\n`; fs.writeFileSync(reportPath, md, 'utf8'); console.log(`\n 📄 Report written to: audit-report.md`); return reportPath; } // ---- Main ---- console.log(`\x1b[1m\x1b[36m`); console.log(` ╔══════════════════════════════════════════════════╗`); console.log(` ║ THREE-LAYER PROTOCOL AUDIT ║`); console.log(` ║ AGENTS.md · GOVERNANCE.md · PORTFOLIO.md ║`); console.log(` ╚══════════════════════════════════════════════════╝`); console.log(`\x1b[0m`); const results = runAll(); printConsole(results); writeReport(results);