cto-ai-cli 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +151 -0
- package/README.md +124 -15
- package/dist/action/index.js +23 -1
- package/dist/api/dashboard.js +23 -1
- package/dist/api/dashboard.js.map +1 -1
- package/dist/api/server.js +23 -1
- package/dist/api/server.js.map +1 -1
- package/dist/cli/score.js +754 -13
- package/dist/cli/v2/index.js +23 -1
- package/dist/cli/v2/index.js.map +1 -1
- package/dist/engine/index.js +23 -1
- package/dist/engine/index.js.map +1 -1
- package/dist/govern/index.d.ts +284 -0
- package/dist/govern/index.js +155 -1
- package/dist/govern/index.js.map +1 -1
- package/dist/interact/index.js +23 -1
- package/dist/interact/index.js.map +1 -1
- package/dist/mcp/v2.js +23 -1
- package/dist/mcp/v2.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/dashboard.ts","../../src/engine/analyzer.ts","../../src/types/engine.ts","../../src/types/config.ts","../../src/engine/tokenizer.ts","../../src/engine/graph.ts","../../src/engine/risk.ts","../../src/engine/cache.ts","../../src/engine/selector.ts","../../src/govern/secrets.ts","../../src/engine/pruner.ts","../../src/engine/graph-utils.ts","../../src/engine/coverage.ts","../../src/engine/budget.ts","../../src/engine/score.ts","../../src/engine/benchmark.ts"],"sourcesContent":["import { resolve, join } from 'node:path';\nimport { writeFile, mkdir, readFile } from 'node:fs/promises';\nimport { analyzeProject } from '../engine/analyzer.js';\nimport { getCachedAnalysis } from '../engine/cache.js';\nimport { computeContextScore } from '../engine/score.js';\nimport { runBenchmark } from '../engine/benchmark.js';\nimport type { ContextScore } from '../engine/score.js';\nimport type { BenchmarkResult } from '../engine/benchmark.js';\n\n// ===== CTO Dashboard =====\n//\n// Generates a self-contained HTML dashboard with:\n// 1. Context Score™ gauge\n// 2. Dimension breakdown (radar chart via CSS)\n// 3. Benchmark comparison table\n// 4. Score history (stored in .cto/history.json)\n// 5. Top risk files\n// 6. Insights & recommendations\n// 7. Shareable badge embed code\n//\n// Output: .cto/dashboard.html (opens in any browser)\n\nexport interface DashboardData {\n score: ContextScore;\n benchmark: BenchmarkResult;\n history: ScoreHistoryEntry[];\n generatedAt: Date;\n}\n\nexport interface ScoreHistoryEntry {\n date: string;\n overall: number;\n grade: string;\n files: number;\n tokens: number;\n savedPercent: number;\n}\n\n// ===== HISTORY =====\n\nasync function loadHistory(ctoDir: string): Promise<ScoreHistoryEntry[]> {\n try {\n const raw = await readFile(join(ctoDir, 'history.json'), 'utf-8');\n return JSON.parse(raw);\n } catch {\n return [];\n }\n}\n\nasync function saveHistory(ctoDir: string, history: ScoreHistoryEntry[]): Promise<void> {\n await mkdir(ctoDir, { recursive: true });\n await writeFile(join(ctoDir, 'history.json'), JSON.stringify(history, null, 2));\n}\n\n// ===== MAIN API =====\n\nexport async function generateDashboard(\n projectPath: string,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<{ htmlPath: string; data: DashboardData }> {\n const absPath = resolve(projectPath);\n const ctoDir = join(absPath, '.cto');\n\n const analysis = await getCachedAnalysis(absPath);\n const score = await computeContextScore(analysis, task, budget);\n const benchmark = await runBenchmark(analysis, task, budget);\n\n // Load + update history\n const history = await loadHistory(ctoDir);\n const today = new Date().toISOString().split('T')[0];\n\n // Replace today's entry if exists, otherwise append\n const existingIdx = history.findIndex((h) => h.date === today);\n const entry: ScoreHistoryEntry = {\n date: today,\n overall: score.overall,\n grade: score.grade,\n files: score.meta.totalFiles,\n tokens: score.meta.totalTokens,\n savedPercent: score.comparison.savedPercent,\n };\n\n if (existingIdx >= 0) {\n history[existingIdx] = entry;\n } else {\n history.push(entry);\n }\n\n // Keep last 90 days\n const cutoff = history.length > 90 ? history.length - 90 : 0;\n const trimmedHistory = history.slice(cutoff);\n await saveHistory(ctoDir, trimmedHistory);\n\n const data: DashboardData = {\n score,\n benchmark,\n history: trimmedHistory,\n generatedAt: new Date(),\n };\n\n const html = renderDashboardHTML(data);\n const htmlPath = join(ctoDir, 'dashboard.html');\n await mkdir(ctoDir, { recursive: true });\n await writeFile(htmlPath, html, 'utf-8');\n\n return { htmlPath, data };\n}\n\n// ===== BADGE =====\n\nexport function generateBadgeURL(score: ContextScore): string {\n const color = score.grade.startsWith('A') ? 'brightgreen'\n : score.grade.startsWith('B') ? 'blue'\n : score.grade.startsWith('C') ? 'yellow'\n : 'red';\n return `https://img.shields.io/badge/CTO_Score-${score.overall}%2F100_${encodeURIComponent(score.grade)}-${color}`;\n}\n\nexport function generateBadgeMarkdown(score: ContextScore): string {\n const url = generateBadgeURL(score);\n return `[](https://github.com/cto-ai/cto-ai-cli)`;\n}\n\n// ===== HTML RENDERER =====\n\nfunction renderDashboardHTML(data: DashboardData): string {\n const { score, benchmark, history } = data;\n const s = score;\n const b = benchmark;\n\n const gradeColor = s.grade.startsWith('A') ? '#22c55e'\n : s.grade.startsWith('B') ? '#3b82f6'\n : s.grade.startsWith('C') ? '#eab308'\n : '#ef4444';\n\n const dims = [\n { name: 'Efficiency', score: s.dimensions.efficiency.score, detail: s.dimensions.efficiency.detail },\n { name: 'Coverage', score: s.dimensions.coverage.score, detail: s.dimensions.coverage.detail },\n { name: 'Risk Control', score: s.dimensions.riskControl.score, detail: s.dimensions.riskControl.detail },\n { name: 'Structure', score: s.dimensions.structure.score, detail: s.dimensions.structure.detail },\n { name: 'Governance', score: s.dimensions.governance.score, detail: s.dimensions.governance.detail },\n ];\n\n const historyPoints = history.map((h) => `{date:'${h.date}',score:${h.overall}}`).join(',');\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>CTO Dashboard — ${s.meta.projectName}</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0f172a;color:#e2e8f0;min-height:100vh}\n.container{max-width:1200px;margin:0 auto;padding:24px}\nh1{font-size:28px;font-weight:700;margin-bottom:4px}\nh2{font-size:20px;font-weight:600;margin-bottom:16px;color:#94a3b8}\nh3{font-size:16px;font-weight:600;margin-bottom:12px;color:#cbd5e1}\n.subtitle{color:#64748b;font-size:14px;margin-bottom:32px}\n.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(350px,1fr));gap:24px;margin-bottom:24px}\n.card{background:#1e293b;border-radius:12px;padding:24px;border:1px solid #334155}\n.score-hero{text-align:center;padding:40px 24px}\n.score-number{font-size:72px;font-weight:800;color:${gradeColor}}\n.score-grade{font-size:36px;font-weight:700;color:${gradeColor};margin-left:8px}\n.score-label{font-size:14px;color:#64748b;margin-top:4px}\n.dim-bar{display:flex;align-items:center;margin-bottom:12px}\n.dim-label{width:120px;font-size:13px;color:#94a3b8}\n.dim-track{flex:1;height:8px;background:#334155;border-radius:4px;overflow:hidden}\n.dim-fill{height:100%;border-radius:4px;transition:width .5s}\n.dim-value{width:50px;text-align:right;font-size:13px;font-weight:600}\n.table{width:100%;border-collapse:collapse;font-size:13px}\n.table th{text-align:left;padding:8px 12px;border-bottom:2px solid #334155;color:#64748b;font-weight:600}\n.table td{padding:8px 12px;border-bottom:1px solid #1e293b}\n.table tr:hover td{background:#1e293b}\n.badge{display:inline-block;padding:4px 10px;border-radius:12px;font-size:12px;font-weight:600}\n.badge-cto{background:#3b82f620;color:#60a5fa}\n.badge-naive{background:#64748b20;color:#94a3b8}\n.badge-random{background:#64748b20;color:#94a3b8}\n.insight{display:flex;gap:8px;margin-bottom:8px;font-size:13px}\n.insight-icon{width:20px;text-align:center}\n.stat{text-align:center}\n.stat-value{font-size:28px;font-weight:700}\n.stat-label{font-size:12px;color:#64748b;margin-top:2px}\n.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px}\n.history-chart{width:100%;height:120px;display:flex;align-items:flex-end;gap:2px;padding:8px 0}\n.history-bar{flex:1;background:#3b82f6;border-radius:2px 2px 0 0;min-width:3px;transition:height .3s}\n.history-bar:hover{background:#60a5fa}\n.badge-embed{background:#0f172a;border:1px solid #334155;border-radius:8px;padding:12px;font-family:monospace;font-size:12px;color:#94a3b8;word-break:break-all;cursor:pointer}\n.badge-embed:hover{border-color:#60a5fa}\nfooter{text-align:center;padding:24px;color:#475569;font-size:12px}\n</style>\n</head>\n<body>\n<div class=\"container\">\n<h1>⚡ CTO Dashboard</h1>\n<p class=\"subtitle\">${s.meta.projectName} · ${s.meta.totalFiles} files · ${Math.round(s.meta.totalTokens / 1000)}K tokens · Generated ${data.generatedAt.toISOString().split('T')[0]}</p>\n\n<div class=\"stats-row\">\n<div class=\"card stat\"><div class=\"stat-value\" style=\"color:${gradeColor}\">${s.overall}</div><div class=\"stat-label\">Context Score™</div></div>\n<div class=\"card stat\"><div class=\"stat-value\" style=\"color:${gradeColor}\">${s.grade}</div><div class=\"stat-label\">Grade</div></div>\n<div class=\"card stat\"><div class=\"stat-value\">${s.comparison.savedPercent}%</div><div class=\"stat-label\">Tokens Saved</div></div>\n<div class=\"card stat\"><div class=\"stat-value\">$${s.comparison.monthlySavingsUSD.toFixed(0)}</div><div class=\"stat-label\">Monthly Savings</div></div>\n</div>\n\n<div class=\"grid\">\n\n<div class=\"card\">\n<h3>Dimensions</h3>\n${dims.map((d) => `\n<div class=\"dim-bar\">\n<span class=\"dim-label\">${d.name}</span>\n<div class=\"dim-track\"><div class=\"dim-fill\" style=\"width:${d.score}%;background:${d.score >= 80 ? '#22c55e' : d.score >= 60 ? '#3b82f6' : d.score >= 40 ? '#eab308' : '#ef4444'}\"></div></div>\n<span class=\"dim-value\">${d.score}%</span>\n</div>`).join('')}\n<div style=\"margin-top:16px;font-size:11px;color:#475569\">\n${dims.map((d) => `${d.name}: ${d.detail}`).join('<br>')}\n</div>\n</div>\n\n<div class=\"card\">\n<h3>⚡ Benchmark: CTO vs Naive vs Random</h3>\n<table class=\"table\">\n<tr><th>Metric</th><th><span class=\"badge badge-cto\">CTO</span></th><th><span class=\"badge badge-naive\">Naive</span></th><th><span class=\"badge badge-random\">Random</span></th></tr>\n<tr><td>Files</td><td>${b.strategies.cto.filesSelected}</td><td>${b.strategies.naive.filesSelected}</td><td>${b.strategies.random.filesSelected}</td></tr>\n<tr><td>Tokens</td><td>${fmt(b.strategies.cto.tokensUsed)}</td><td>${fmt(b.strategies.naive.tokensUsed)}</td><td>${fmt(b.strategies.random.tokensUsed)}</td></tr>\n<tr><td>Coverage</td><td><b>${b.strategies.cto.coverageScore}%</b></td><td>${b.strategies.naive.coverageScore}%</td><td>${b.strategies.random.coverageScore}%</td></tr>\n<tr><td>High-Risk</td><td><b>${b.strategies.cto.highRiskCovered}/${b.strategies.cto.highRiskTotal}</b></td><td>${b.strategies.naive.highRiskCovered}/${b.strategies.naive.highRiskTotal}</td><td>${b.strategies.random.highRiskCovered}/${b.strategies.random.highRiskTotal}</td></tr>\n<tr><td>Cost/call</td><td>$${b.strategies.cto.costPerInteractionUSD.toFixed(4)}</td><td>$${b.strategies.naive.costPerInteractionUSD.toFixed(4)}</td><td>$${b.strategies.random.costPerInteractionUSD.toFixed(4)}</td></tr>\n</table>\n</div>\n\n<div class=\"card\">\n<h3>💡 Insights</h3>\n${s.insights.slice(0, 8).map((i) => {\n const icon = i.type === 'strength' ? '✅' : i.type === 'weakness' ? '⚠️' : '💡';\n return `<div class=\"insight\"><span class=\"insight-icon\">${icon}</span><span><b>${i.title}</b> — ${i.detail}</span></div>`;\n}).join('')}\n</div>\n\n<div class=\"card\">\n<h3>📈 Score History</h3>\n${history.length > 1 ? `\n<div class=\"history-chart\">\n${history.map((h) => `<div class=\"history-bar\" style=\"height:${h.overall}%\" title=\"${h.date}: ${h.overall}/100 (${h.grade})\"></div>`).join('')}\n</div>\n<div style=\"display:flex;justify-content:space-between;font-size:11px;color:#475569\">\n<span>${history[0]?.date}</span>\n<span>${history[history.length - 1]?.date}</span>\n</div>\n` : '<p style=\"color:#64748b;font-size:13px\">Run <code>cto2 dashboard</code> daily to build score history.</p>'}\n</div>\n\n</div>\n\n<div class=\"card\" style=\"margin-bottom:24px\">\n<h3>🏷️ Badge</h3>\n<p style=\"margin-bottom:12px\"><img src=\"${generateBadgeURL(s)}\" alt=\"CTO Score\"></p>\n<p style=\"font-size:12px;color:#64748b;margin-bottom:8px\">Add this to your README:</p>\n<div class=\"badge-embed\" onclick=\"navigator.clipboard.writeText(this.textContent)\">${generateBadgeMarkdown(s)}</div>\n</div>\n\n<footer>\nCTO Context Score™ · Generated by <a href=\"https://github.com/cto-ai/cto-ai-cli\" style=\"color:#60a5fa\">cto-ai-cli</a>\n</footer>\n\n</div>\n</body>\n</html>`;\n}\n\nfunction fmt(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n","import { readFile, readdir, stat } from 'node:fs/promises';\nimport { join, extname, relative, resolve, basename } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n FileKind,\n ProjectAnalysis,\n WalkEntry,\n WalkOptions,\n} from '../types/engine.js';\nimport { DEFAULT_RISK_WEIGHTS } from '../types/engine.js';\nimport type { CTOConfig } from '../types/config.js';\nimport { DEFAULT_CONFIG } from '../types/config.js';\nimport { estimateTokens, countTokensChars4 } from './tokenizer.js';\nimport { buildProjectGraph } from './graph.js';\nimport { scoreAllFiles } from './risk.js';\n\n// ===== FILE WALKING =====\n\nfunction matchesPattern(filename: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern.startsWith('*.')) {\n const ext = pattern.slice(1);\n if (filename.endsWith(ext)) return true;\n } else if (filename === pattern) {\n return true;\n }\n }\n return false;\n}\n\nexport async function walkProject(\n rootPath: string,\n options: WalkOptions,\n): Promise<WalkEntry[]> {\n const results: WalkEntry[] = [];\n const { ignoreDirs, ignorePatterns, extensions, maxDepth = 20 } = options;\n const ignoreDirSet = new Set(ignoreDirs);\n\n async function walk(dir: string, depth: number): Promise<void> {\n if (depth > maxDepth) return;\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n const promises: Promise<void>[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!ignoreDirSet.has(entry.name) && !entry.name.startsWith('.')) {\n promises.push(walk(fullPath, depth + 1));\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name).slice(1).toLowerCase();\n if (ext && extensions.includes(ext) && !matchesPattern(entry.name, ignorePatterns)) {\n promises.push(\n (async () => {\n const fileStat = await stat(fullPath).catch(() => null);\n if (!fileStat) return;\n\n let lines = 0;\n try {\n const content = await readFile(fullPath, 'utf-8');\n lines = content.split('\\n').length;\n } catch {\n lines = 0;\n }\n\n results.push({\n path: fullPath,\n relativePath: relative(rootPath, fullPath),\n extension: ext,\n size: fileStat.size,\n lastModified: fileStat.mtime,\n lines,\n });\n })(),\n );\n }\n }\n }\n\n await Promise.all(promises);\n }\n\n await walk(rootPath, 0);\n return results;\n}\n\n// ===== FILE KIND DETECTION =====\n\nconst TYPE_PATTERNS = [/types?\\//i, /\\.d\\.ts$/, /interfaces?\\//i];\nconst TEST_PATTERNS = [/\\.test\\.[jt]sx?$/, /\\.spec\\.[jt]sx?$/, /\\/__tests__\\//, /\\/tests?\\//];\nconst CONFIG_PATTERNS = [/\\.config\\.[jt]s$/, /rc\\.[jt]s$/, /\\.env/, /tsconfig/, /package\\.json$/, /\\.yml$/, /\\.yaml$/, /\\.toml$/];\nconst ENTRY_PATTERNS = [/^index\\.[jt]sx?$/, /^main\\.[jt]sx?$/, /^app\\.[jt]sx?$/, /^server\\.[jt]sx?$/];\n\nexport function classifyFileKind(relativePath: string): FileKind {\n const filename = basename(relativePath);\n\n if (TYPE_PATTERNS.some((p) => p.test(relativePath))) return 'type';\n if (TEST_PATTERNS.some((p) => p.test(relativePath))) return 'test';\n if (CONFIG_PATTERNS.some((p) => p.test(relativePath) || p.test(filename))) return 'config';\n if (ENTRY_PATTERNS.some((p) => p.test(filename))) return 'entry';\n return 'source';\n}\n\n// ===== STACK DETECTION =====\n\nexport function detectStack(files: WalkEntry[]): string[] {\n const stack: string[] = [];\n const extensions = new Set(files.map((f) => f.extension));\n const paths = files.map((f) => f.relativePath.toLowerCase());\n\n if (extensions.has('ts') || extensions.has('tsx')) stack.push('TypeScript');\n else if (extensions.has('js') || extensions.has('jsx')) stack.push('JavaScript');\n if (extensions.has('py')) stack.push('Python');\n if (extensions.has('go')) stack.push('Go');\n if (extensions.has('rs')) stack.push('Rust');\n if (extensions.has('java')) stack.push('Java');\n if (extensions.has('kt')) stack.push('Kotlin');\n if (extensions.has('rb')) stack.push('Ruby');\n if (extensions.has('php')) stack.push('PHP');\n if (extensions.has('cs')) stack.push('C#');\n if (extensions.has('c') || extensions.has('cpp')) stack.push('C/C++');\n\n if (paths.some((p) => p.includes('next.config'))) stack.push('Next.js');\n if (paths.some((p) => p.includes('nuxt.config'))) stack.push('Nuxt');\n if (paths.some((p) => p.includes('angular.json'))) stack.push('Angular');\n\n return stack;\n}\n\n// ===== MAIN ANALYSIS PIPELINE =====\n\nexport async function analyzeProject(\n projectPath: string,\n config?: Partial<CTOConfig>,\n): Promise<ProjectAnalysis> {\n const absPath = resolve(projectPath);\n const projectName = basename(absPath);\n const mergedConfig = mergeConfig(DEFAULT_CONFIG, config);\n\n // 1. Walk project files\n const allExtensions = [\n ...mergedConfig.analysis.extensions.code,\n ...mergedConfig.analysis.extensions.config,\n ...mergedConfig.analysis.extensions.docs,\n ];\n\n const walkEntries = await walkProject(absPath, {\n ignoreDirs: mergedConfig.analysis.ignore.dirs,\n ignorePatterns: mergedConfig.analysis.ignore.patterns,\n extensions: allExtensions,\n maxDepth: mergedConfig.analysis.maxDepth,\n });\n\n // 2. Estimate tokens and build initial AnalyzedFile[]\n const tokenMethod = mergedConfig.tokens.method;\n const files: AnalyzedFile[] = [];\n\n for (const entry of walkEntries) {\n let tokens: number;\n if (tokenMethod === 'tiktoken') {\n try {\n const content = await readFile(entry.path, 'utf-8');\n tokens = estimateTokens(content, entry.size, 'tiktoken');\n } catch {\n tokens = countTokensChars4(entry.size);\n }\n } else {\n tokens = countTokensChars4(entry.size);\n }\n\n files.push({\n path: entry.path,\n relativePath: entry.relativePath,\n extension: entry.extension,\n size: entry.size,\n tokens,\n lines: entry.lines,\n lastModified: entry.lastModified,\n kind: classifyFileKind(entry.relativePath),\n\n // Graph data — populated by graph analysis\n imports: [],\n importedBy: [],\n isHub: false,\n complexity: 0,\n\n // Risk data — populated by risk analysis\n riskScore: 0,\n riskFactors: [],\n exclusionImpact: 'none',\n });\n }\n\n // 3. Build dependency graph (AST-based, best-effort)\n const graph = buildProjectGraph(absPath, files);\n\n // 4. Enrich files with graph data\n for (const file of files) {\n const nodeImports: string[] = [];\n const nodeImportedBy: string[] = [];\n\n for (const edge of graph.edges) {\n if (edge.from === file.relativePath) nodeImports.push(edge.to);\n if (edge.to === file.relativePath) nodeImportedBy.push(edge.from);\n }\n\n file.imports = nodeImports;\n file.importedBy = nodeImportedBy;\n file.isHub = graph.hubs.some((h) => h.relativePath === file.relativePath);\n }\n\n // 5. Score risk for all files\n const riskWeights = mergedConfig.risk.weights;\n scoreAllFiles(files, graph, riskWeights);\n\n // 6. Build risk profile\n const riskProfile = {\n distribution: {\n critical: files.filter((f) => f.riskScore >= 80).length,\n high: files.filter((f) => f.riskScore >= 60 && f.riskScore < 80).length,\n medium: files.filter((f) => f.riskScore >= 30 && f.riskScore < 60).length,\n low: files.filter((f) => f.riskScore < 30).length,\n },\n topRiskFiles: [...files].sort((a, b) => b.riskScore - a.riskScore).slice(0, 10),\n overallComplexity: files.length > 0\n ? files.reduce((s, f) => s + f.complexity, 0) / files.length\n : 0,\n };\n\n // 7. Build analysis hash for determinism\n const totalTokens = files.reduce((s, f) => s + f.tokens, 0);\n const hashInput = files\n .map((f) => `${f.relativePath}:${f.tokens}:${f.riskScore}`)\n .sort()\n .join('|');\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n const stack = detectStack(walkEntries);\n\n return {\n projectPath: absPath,\n projectName,\n analyzedAt: new Date(),\n hash,\n files,\n totalFiles: files.length,\n totalTokens,\n graph,\n riskProfile,\n stack,\n tokenMethod,\n };\n}\n\n// ===== CONFIG MERGE =====\n\nfunction mergeConfig(base: CTOConfig, overrides?: Partial<CTOConfig>): CTOConfig {\n if (!overrides) return base;\n\n return {\n ...base,\n ...overrides,\n analysis: {\n ...base.analysis,\n ...overrides.analysis,\n extensions: {\n ...base.analysis.extensions,\n ...overrides.analysis?.extensions,\n },\n ignore: {\n ...base.analysis.ignore,\n ...overrides.analysis?.ignore,\n },\n },\n risk: {\n ...base.risk,\n ...overrides.risk,\n weights: {\n ...base.risk.weights,\n ...overrides.risk?.weights,\n },\n },\n interaction: {\n ...base.interaction,\n ...overrides.interaction,\n },\n tokens: {\n ...base.tokens,\n ...overrides.tokens,\n },\n governance: {\n ...base.governance,\n ...overrides.governance,\n },\n };\n}\n","// ===== Layer 1: Context Intelligence Engine Types =====\n\n// ===== FILE ANALYSIS =====\n\nexport interface AnalyzedFile {\n path: string;\n relativePath: string;\n extension: string;\n size: number;\n tokens: number;\n lines: number;\n lastModified: Date;\n kind: FileKind;\n\n // Graph data (populated after graph analysis)\n imports: string[];\n importedBy: string[];\n isHub: boolean;\n complexity: number;\n\n // Risk data (populated after risk analysis)\n riskScore: number;\n riskFactors: RiskFactor[];\n exclusionImpact: ExclusionImpact;\n}\n\nexport type FileKind = 'source' | 'type' | 'test' | 'config' | 'entry' | 'asset';\nexport type ExclusionImpact = 'critical' | 'high' | 'medium' | 'low' | 'none';\n\n// ===== PROJECT ANALYSIS =====\n\nexport interface ProjectAnalysis {\n projectPath: string;\n projectName: string;\n analyzedAt: Date;\n hash: string;\n\n files: AnalyzedFile[];\n totalFiles: number;\n totalTokens: number;\n\n graph: ProjectGraph;\n riskProfile: RiskProfile;\n stack: string[];\n tokenMethod: 'chars4' | 'tiktoken';\n}\n\n// ===== DEPENDENCY GRAPH =====\n\nexport interface ProjectGraph {\n nodes: string[];\n edges: GraphEdge[];\n hubs: HubNode[];\n leaves: string[];\n orphans: string[];\n clusters: FileCluster[];\n}\n\nexport interface GraphEdge {\n from: string;\n to: string;\n type: 'import' | 'export' | 're-export';\n}\n\nexport interface HubNode {\n relativePath: string;\n dependents: number;\n dependencies: number;\n score: number;\n}\n\nexport interface FileCluster {\n id: string;\n name: string;\n files: string[];\n totalTokens: number;\n internalEdges: number;\n externalEdges: number;\n cohesion: number;\n}\n\n// ===== RISK MODEL =====\n\nexport interface RiskProfile {\n distribution: {\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n topRiskFiles: AnalyzedFile[];\n overallComplexity: number;\n}\n\nexport interface RiskFactor {\n type: RiskFactorType;\n score: number;\n weight: number;\n detail: string;\n}\n\nexport type RiskFactorType =\n | 'hub'\n | 'type-provider'\n | 'complexity'\n | 'recency'\n | 'config'\n | 'churn';\n\nexport interface RiskWeights {\n hub: number;\n typeProvider: number;\n complexity: number;\n recency: number;\n config: number;\n churn: number;\n}\n\nexport const DEFAULT_RISK_WEIGHTS: RiskWeights = {\n hub: 30,\n typeProvider: 25,\n complexity: 15,\n recency: 15,\n config: 10,\n churn: 5,\n};\n\n// ===== CONTEXT COVERAGE =====\n\nexport interface CoverageResult {\n score: number;\n relevantFiles: string[];\n includedRelevant: string[];\n missingRelevant: string[];\n missingCritical: string[];\n explanation: string;\n}\n\n// ===== CONTEXT SELECTION =====\n\nexport interface ContextSelection {\n files: SelectedFile[];\n totalTokens: number;\n budget: number;\n usedPercent: number;\n\n coverage: CoverageResult;\n riskScore: number;\n deterministic: boolean;\n hash: string;\n\n decisions: SelectionDecision[];\n}\n\nexport interface SelectedFile {\n relativePath: string;\n tokens: number;\n originalTokens: number;\n pruneLevel: PruneLevel;\n riskScore: number;\n reason: string;\n}\n\nexport type PruneLevel = 'full' | 'signatures' | 'skeleton' | 'excluded';\n\nexport interface SelectionDecision {\n file: string;\n action: 'include-full' | 'include-signatures' | 'include-skeleton' | 'exclude';\n reason: string;\n alternatives?: string;\n}\n\n// ===== BUDGET =====\n\nexport interface BudgetPlan {\n budget: number;\n used: number;\n remaining: number;\n fillPercent: number;\n files: BudgetEntry[];\n}\n\nexport interface BudgetEntry {\n relativePath: string;\n originalTokens: number;\n allocatedTokens: number;\n pruneLevel: PruneLevel;\n included: boolean;\n reason: string;\n}\n\n// ===== PRUNING =====\n\nexport interface PrunedContent {\n relativePath: string;\n originalTokens: number;\n prunedTokens: number;\n pruneLevel: PruneLevel;\n content: string;\n savingsPercent: number;\n}\n\n// ===== WALKER =====\n\nexport interface WalkEntry {\n path: string;\n relativePath: string;\n extension: string;\n size: number;\n lastModified: Date;\n lines: number;\n}\n\nexport interface WalkOptions {\n ignoreDirs: string[];\n ignorePatterns: string[];\n extensions: string[];\n maxDepth?: number;\n}\n","// ===== Configuration Types =====\n\nimport type { RiskWeights } from './engine.js';\n\nexport interface CTOConfig {\n version: string;\n\n analysis: {\n extensions: {\n code: string[];\n config: string[];\n docs: string[];\n };\n ignore: {\n dirs: string[];\n patterns: string[];\n };\n maxDepth: number;\n };\n\n risk: {\n weights: RiskWeights;\n };\n\n interaction: {\n defaultBudget: number;\n defaultModel: string;\n };\n\n tokens: {\n method: 'chars4' | 'tiktoken';\n };\n\n governance: {\n auditEnabled: boolean;\n secretDetection: boolean;\n retentionDays: number;\n };\n}\n\nexport const DEFAULT_CONFIG: CTOConfig = {\n version: '2.0',\n\n analysis: {\n extensions: {\n code: ['ts', 'tsx', 'js', 'jsx', 'py', 'go', 'rs', 'java', 'kt', 'rb', 'php', 'c', 'cpp', 'h', 'hpp', 'cs'],\n config: ['json', 'yml', 'yaml', 'toml'],\n docs: ['md', 'txt', 'rst'],\n },\n ignore: {\n dirs: ['node_modules', 'dist', 'build', '.git', 'coverage', '__pycache__', '.next', 'vendor', '.cto'],\n patterns: ['*.min.js', '*.map', '*.lock', '*.generated.*'],\n },\n maxDepth: 20,\n },\n\n risk: {\n weights: {\n hub: 30,\n typeProvider: 25,\n complexity: 15,\n recency: 15,\n config: 10,\n churn: 5,\n },\n },\n\n interaction: {\n defaultBudget: 50_000,\n defaultModel: 'claude-sonnet-4',\n },\n\n tokens: {\n method: 'chars4',\n },\n\n governance: {\n auditEnabled: true,\n secretDetection: true,\n retentionDays: 90,\n },\n};\n","import { encodingForModel } from 'js-tiktoken';\nimport { readFile, stat } from 'node:fs/promises';\n\nconst CHARS_PER_TOKEN = 4;\n\nlet encoder: ReturnType<typeof encodingForModel> | null = null;\n\nfunction getEncoder() {\n if (!encoder) {\n encoder = encodingForModel('claude-3-5-sonnet-20241022' as Parameters<typeof encodingForModel>[0]);\n }\n return encoder;\n}\n\nexport function countTokensTiktoken(text: string): number {\n try {\n const enc = getEncoder();\n const tokens = enc.encode(text);\n return tokens.length;\n } catch {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n }\n}\n\nexport function countTokensChars4(sizeInBytes: number): number {\n return Math.ceil(sizeInBytes / CHARS_PER_TOKEN);\n}\n\nexport function estimateTokens(\n content: string,\n sizeInBytes: number,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): number {\n if (method === 'tiktoken') {\n return countTokensTiktoken(content);\n }\n return countTokensChars4(sizeInBytes);\n}\n\nexport async function estimateFileTokens(\n filePath: string,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): Promise<number> {\n if (method === 'chars4') {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n\n try {\n const content = await readFile(filePath, 'utf-8');\n return countTokensTiktoken(content);\n } catch {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n}\n\nexport function freeEncoder(): void {\n encoder = null;\n}\n","import { Project, SyntaxKind, type SourceFile, type Node } from 'ts-morph';\nimport { resolve, relative, dirname, join, basename } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport type {\n AnalyzedFile,\n ProjectGraph,\n GraphEdge,\n HubNode,\n FileCluster,\n} from '../types/engine.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs', 'cts', 'cjs']);\n\n// ===== PROJECT CREATION =====\n\nexport function createProject(projectPath: string, filePaths: string[]): Project {\n const tsConfigPath = join(projectPath, 'tsconfig.json');\n const hasTsConfig = existsSync(tsConfigPath);\n\n const project = new Project({\n tsConfigFilePath: hasTsConfig ? tsConfigPath : undefined,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: hasTsConfig\n ? undefined\n : {\n allowJs: true,\n jsx: 4 as any, // JsxEmit.ReactJSX\n esModuleInterop: true,\n moduleResolution: 100 as any, // Bundler\n },\n });\n\n const tsFiles = filePaths.filter((f) => {\n const ext = f.split('.').pop()?.toLowerCase() ?? '';\n return TS_EXTENSIONS.has(ext);\n });\n\n for (const filePath of tsFiles) {\n try {\n project.addSourceFileAtPath(filePath);\n } catch {\n // skip files that can't be parsed\n }\n }\n\n return project;\n}\n\n// ===== DEPENDENCY GRAPH =====\n\nexport function buildProjectGraph(\n projectPath: string,\n files: AnalyzedFile[],\n): ProjectGraph {\n const absPath = resolve(projectPath);\n\n const tsFiles = files\n .filter((f) => TS_EXTENSIONS.has(f.extension))\n .map((f) => f.path);\n\n if (tsFiles.length === 0) {\n return emptyGraph(files);\n }\n\n let project: Project;\n try {\n project = createProject(projectPath, tsFiles);\n } catch {\n return emptyGraph(files);\n }\n\n const edges: GraphEdge[] = [];\n const nodeSet = new Set<string>();\n\n for (const sourceFile of project.getSourceFiles()) {\n const fromRel = relative(absPath, sourceFile.getFilePath());\n if (fromRel.startsWith('..') || fromRel.includes('node_modules')) continue;\n nodeSet.add(fromRel);\n\n // Imports\n for (const imp of sourceFile.getImportDeclarations()) {\n const moduleSpecifier = imp.getModuleSpecifierValue();\n const resolved = resolveImport(sourceFile, moduleSpecifier, absPath);\n if (resolved) {\n nodeSet.add(resolved);\n edges.push({ from: fromRel, to: resolved, type: 'import' });\n }\n }\n\n // Re-exports\n for (const exp of sourceFile.getExportDeclarations()) {\n const moduleSpecifier = exp.getModuleSpecifierValue();\n if (moduleSpecifier) {\n const resolved = resolveImport(sourceFile, moduleSpecifier, absPath);\n if (resolved) {\n nodeSet.add(resolved);\n edges.push({ from: fromRel, to: resolved, type: 're-export' });\n }\n }\n }\n }\n\n const nodes = Array.from(nodeSet);\n\n // Calculate import counts\n const importedByCount = new Map<string, number>();\n const importCount = new Map<string, number>();\n\n for (const edge of edges) {\n importedByCount.set(edge.to, (importedByCount.get(edge.to) ?? 0) + 1);\n importCount.set(edge.from, (importCount.get(edge.from) ?? 0) + 1);\n }\n\n // Identify hubs using normalized in-degree centrality (Freeman, 1978).\n // centrality = in-degree / (N - 1), then combined with out-degree for a\n // \"betweenness-lite\" score: score = (in-degree × 2 + out-degree) / (N - 1 + 1)\n // This normalizes by graph size so hub detection is consistent across projects.\n const N = Math.max(nodes.length, 1);\n const hubs: HubNode[] = nodes\n .map((node) => {\n const inDeg = importedByCount.get(node) ?? 0;\n const outDeg = importCount.get(node) ?? 0;\n // Normalized centrality score (0-100 scale)\n const centrality = N > 1 ? (inDeg / (N - 1)) * 100 : 0;\n const score = Math.round(centrality + outDeg * (100 / (2 * N)));\n return {\n relativePath: node,\n dependents: inDeg,\n dependencies: outDeg,\n score: Math.min(100, score),\n };\n })\n .filter((h) => h.dependents >= 3 || h.score >= 15)\n .sort((a, b) => b.score - a.score);\n\n // Identify leaves (files that import but are not imported by anyone)\n const leaves = nodes.filter(\n (node) => (importedByCount.get(node) ?? 0) === 0 && (importCount.get(node) ?? 0) > 0,\n );\n\n // Identify orphans (files with no connections at all)\n const connectedNodes = new Set<string>();\n for (const edge of edges) {\n connectedNodes.add(edge.from);\n connectedNodes.add(edge.to);\n }\n const allFileNodes = new Set(files.map((f) => f.relativePath));\n const orphans = Array.from(allFileNodes).filter((n) => !connectedNodes.has(n));\n\n // Detect clusters using Union-Find for true connected components,\n // then refine with directory grouping for naming.\n const clusters = detectClusters(nodes, edges, files);\n\n // Enrich files with complexity (AST-based)\n enrichComplexity(project, absPath, files);\n\n return { nodes, edges, hubs, leaves, orphans, clusters };\n}\n\n// ===== CLUSTER DETECTION (Union-Find + directory naming) =====\n\nclass UnionFind {\n parent: Map<string, string>;\n rank: Map<string, number>;\n\n constructor(nodes: string[]) {\n this.parent = new Map();\n this.rank = new Map();\n for (const n of nodes) {\n this.parent.set(n, n);\n this.rank.set(n, 0);\n }\n }\n\n find(x: string): string {\n const p = this.parent.get(x);\n if (p === undefined) return x;\n if (p !== x) {\n this.parent.set(x, this.find(p)); // path compression\n }\n return this.parent.get(x)!;\n }\n\n union(a: string, b: string): void {\n const ra = this.find(a);\n const rb = this.find(b);\n if (ra === rb) return;\n const rankA = this.rank.get(ra) ?? 0;\n const rankB = this.rank.get(rb) ?? 0;\n if (rankA < rankB) {\n this.parent.set(ra, rb);\n } else if (rankA > rankB) {\n this.parent.set(rb, ra);\n } else {\n this.parent.set(rb, ra);\n this.rank.set(ra, rankA + 1);\n }\n }\n}\n\nfunction detectClusters(\n nodes: string[],\n edges: GraphEdge[],\n files: AnalyzedFile[],\n): FileCluster[] {\n // 1. Build connected components using Union-Find (O(E × α(V)))\n const uf = new UnionFind(nodes);\n for (const edge of edges) {\n uf.union(edge.from, edge.to);\n }\n\n // 2. Group by component root\n const components = new Map<string, string[]>();\n for (const node of nodes) {\n const root = uf.find(node);\n if (!components.has(root)) components.set(root, []);\n components.get(root)!.push(node);\n }\n\n // 3. For each component, derive a human-readable name from common directory prefix\n const tokenMap = new Map(files.map((f) => [f.relativePath, f.tokens]));\n const clusters: FileCluster[] = [];\n\n for (const [, groupFiles] of components) {\n if (groupFiles.length < 2) continue;\n\n // Find common directory prefix for naming\n const name = commonPrefix(groupFiles);\n const fileSet = new Set(groupFiles);\n let internalEdges = 0;\n let externalEdges = 0;\n\n for (const edge of edges) {\n const fromIn = fileSet.has(edge.from);\n const toIn = fileSet.has(edge.to);\n if (fromIn && toIn) internalEdges++;\n else if (fromIn || toIn) externalEdges++;\n }\n\n const totalEdges = internalEdges + externalEdges;\n const cohesion = totalEdges > 0 ? internalEdges / totalEdges : 0;\n const totalTokens = groupFiles.reduce((s, f) => s + (tokenMap.get(f) ?? 0), 0);\n\n clusters.push({\n id: name.replace(/[^a-zA-Z0-9]/g, '-') || `cluster-${clusters.length}`,\n name: name || `cluster-${clusters.length}`,\n files: groupFiles,\n totalTokens,\n internalEdges,\n externalEdges,\n cohesion: Math.round(cohesion * 100) / 100,\n });\n }\n\n return clusters.sort((a, b) => b.files.length - a.files.length);\n}\n\nfunction commonPrefix(paths: string[]): string {\n if (paths.length === 0) return '';\n const parts = paths.map((p) => p.split('/'));\n const prefix: string[] = [];\n for (let i = 0; i < parts[0].length - 1; i++) {\n const segment = parts[0][i];\n if (parts.every((p) => p[i] === segment)) {\n prefix.push(segment);\n } else break;\n }\n return prefix.join('/') || parts[0][0];\n}\n\n// ===== COMPLEXITY ANALYSIS =====\n\nfunction enrichComplexity(\n project: Project,\n absPath: string,\n files: AnalyzedFile[],\n): void {\n const fileMap = new Map(files.map((f) => [f.relativePath, f]));\n\n for (const sourceFile of project.getSourceFiles()) {\n const relPath = relative(absPath, sourceFile.getFilePath());\n if (relPath.startsWith('..') || relPath.includes('node_modules')) continue;\n\n const file = fileMap.get(relPath);\n if (!file) continue;\n\n let totalComplexity = 0;\n\n // Function declarations\n for (const func of sourceFile.getFunctions()) {\n totalComplexity += calculateCyclomaticComplexity(func);\n }\n\n // Class methods\n for (const cls of sourceFile.getClasses()) {\n for (const method of cls.getMethods()) {\n totalComplexity += calculateCyclomaticComplexity(method);\n }\n }\n\n // Arrow functions assigned to variables\n for (const varDecl of sourceFile.getVariableDeclarations()) {\n const init = varDecl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n totalComplexity += calculateCyclomaticComplexity(init);\n }\n }\n\n file.complexity = Math.max(1, totalComplexity);\n }\n}\n\nfunction calculateCyclomaticComplexity(node: Node): number {\n let complexity = 1;\n\n node.forEachDescendant((descendant) => {\n switch (descendant.getKind()) {\n case SyntaxKind.IfStatement:\n case SyntaxKind.ConditionalExpression:\n case SyntaxKind.ForStatement:\n case SyntaxKind.ForInStatement:\n case SyntaxKind.ForOfStatement:\n case SyntaxKind.WhileStatement:\n case SyntaxKind.DoStatement:\n case SyntaxKind.CaseClause:\n case SyntaxKind.CatchClause:\n complexity++;\n break;\n case SyntaxKind.BinaryExpression: {\n // FIX: Check the operator token kind directly instead of getText()\n // to avoid double-counting nested logical expressions.\n // getText() on `a && (b || c)` returns the full text including\n // the inner `||`, which would match both operators from the outer node.\n const opToken = (descendant as any).getOperatorToken?.();\n if (opToken) {\n const kind = opToken.getKind();\n if (\n kind === SyntaxKind.AmpersandAmpersandToken ||\n kind === SyntaxKind.BarBarToken ||\n kind === SyntaxKind.QuestionQuestionToken\n ) {\n complexity++;\n }\n }\n break;\n }\n }\n });\n\n return complexity;\n}\n\n// ===== IMPORT RESOLUTION =====\n\nfunction resolveImport(\n sourceFile: SourceFile,\n moduleSpecifier: string,\n projectRoot: string,\n): string | null {\n if (!moduleSpecifier.startsWith('.')) return null;\n\n const sourceDir = dirname(sourceFile.getFilePath());\n const basePath = resolve(sourceDir, moduleSpecifier);\n\n const extensions = ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx', '/index.js', '/index.jsx'];\n\n for (const ext of extensions) {\n const candidate = basePath.endsWith(ext) ? basePath : basePath + ext;\n if (existsSync(candidate)) {\n const rel = relative(projectRoot, candidate);\n if (!rel.startsWith('..')) return rel;\n }\n }\n\n // .js → .ts resolution\n if (moduleSpecifier.endsWith('.js')) {\n const tsPath = basePath.replace(/\\.js$/, '.ts');\n if (existsSync(tsPath)) {\n const rel = relative(projectRoot, tsPath);\n if (!rel.startsWith('..')) return rel;\n }\n }\n\n return null;\n}\n\n// ===== HELPERS =====\n\nfunction emptyGraph(files: AnalyzedFile[]): ProjectGraph {\n return {\n nodes: files.map((f) => f.relativePath),\n edges: [],\n hubs: [],\n leaves: [],\n orphans: files.map((f) => f.relativePath),\n clusters: [],\n };\n}\n","import type {\n AnalyzedFile,\n RiskFactor,\n RiskFactorType,\n RiskWeights,\n ExclusionImpact,\n ProjectGraph,\n} from '../types/engine.js';\nimport { DEFAULT_RISK_WEIGHTS } from '../types/engine.js';\n\n// ===== RISK SCORING ENGINE =====\n//\n// Each file gets a riskScore ∈ [0, 100] representing the risk of EXCLUDING it\n// from AI context. Higher score = more dangerous to exclude.\n//\n// riskScore(file) = Σ(factor.score × factor.weight) / Σ(factor.weight)\n\nexport function scoreAllFiles(\n files: AnalyzedFile[],\n graph: ProjectGraph,\n weights: RiskWeights = DEFAULT_RISK_WEIGHTS,\n): void {\n // Pre-compute type provider map: files that export types used by others\n const typeProviderUsage = computeTypeProviderUsage(files, graph);\n\n for (const file of files) {\n const factors = computeRiskFactors(file, graph, typeProviderUsage, weights);\n file.riskFactors = factors;\n file.riskScore = computeWeightedScore(factors);\n file.exclusionImpact = scoreToImpact(file.riskScore);\n }\n}\n\nexport function scoreFile(\n file: AnalyzedFile,\n graph: ProjectGraph,\n weights: RiskWeights = DEFAULT_RISK_WEIGHTS,\n): number {\n const typeProviderUsage = computeTypeProviderUsage([file], graph);\n const factors = computeRiskFactors(file, graph, typeProviderUsage, weights);\n file.riskFactors = factors;\n file.riskScore = computeWeightedScore(factors);\n file.exclusionImpact = scoreToImpact(file.riskScore);\n return file.riskScore;\n}\n\n// ===== FACTOR COMPUTATION =====\n\nfunction computeRiskFactors(\n file: AnalyzedFile,\n graph: ProjectGraph,\n typeProviderUsage: Map<string, number>,\n weights: RiskWeights,\n): RiskFactor[] {\n const factors: RiskFactor[] = [];\n\n // 1. Hub factor — how many files depend on this file\n factors.push(computeHubFactor(file, weights.hub));\n\n // 2. Type provider factor — exports types used by other files\n factors.push(computeTypeProviderFactor(file, typeProviderUsage, weights.typeProvider));\n\n // 3. Complexity factor — cyclomatic complexity\n factors.push(computeComplexityFactor(file, weights.complexity));\n\n // 4. Recency factor — how recently modified\n factors.push(computeRecencyFactor(file, weights.recency));\n\n // 5. Config factor — is it a config or entry point\n factors.push(computeConfigFactor(file, weights.config));\n\n // 6. Churn factor — based on complexity as proxy (git integration deferred)\n factors.push(computeChurnFactor(file, weights.churn));\n\n return factors;\n}\n\nfunction computeHubFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const dependents = file.importedBy.length;\n\n // Logarithmic scaling: score = min(100, 100 × log₂(1 + dependents) / log₂(1 + K))\n // K=12 means 12 dependents → score 100. Based on information theory:\n // each doubling of dependents adds equal marginal risk.\n const K = 12;\n const score = dependents === 0\n ? 0\n : Math.min(100, Math.round(100 * Math.log2(1 + dependents) / Math.log2(1 + K)));\n\n const detail = dependents === 0\n ? 'No dependents'\n : `Hub: ${dependents} file(s) depend on this (score ${score}/100)`;\n\n return { type: 'hub', score, weight, detail };\n}\n\nfunction computeTypeProviderFactor(\n file: AnalyzedFile,\n usage: Map<string, number>,\n weight: number,\n): RiskFactor {\n const isTypeFile = file.kind === 'type';\n const consumers = usage.get(file.relativePath) ?? 0;\n\n let score: number;\n let detail: string;\n\n if (isTypeFile && consumers >= 4) {\n score = 100;\n detail = `Type provider: used by ${consumers} files (critical type source)`;\n } else if (isTypeFile && consumers >= 1) {\n score = 50;\n detail = `Type provider: used by ${consumers} files`;\n } else if (isTypeFile) {\n score = 30;\n detail = 'Type file (no detected consumers)';\n } else {\n score = 0;\n detail = 'Not a type provider';\n }\n\n return { type: 'type-provider', score, weight, detail };\n}\n\nfunction computeComplexityFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const c = file.complexity;\n\n // Logarithmic scaling: score = min(100, 100 × ln(1 + c) / ln(1 + K))\n // K=30 means complexity 30 → score 100. ln provides diminishing returns\n // for very complex files (consistent with McCabe 1976 thresholds).\n const K = 30;\n const score = Math.min(100, Math.round(100 * Math.log(1 + c) / Math.log(1 + K)));\n\n const detail = c >= 30\n ? `Very high complexity: ${c} (AI needs full context)`\n : c >= 10\n ? `High complexity: ${c}`\n : `Complexity: ${c}`;\n\n return { type: 'complexity', score, weight, detail };\n}\n\nfunction computeRecencyFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const now = Date.now();\n const modified = new Date(file.lastModified).getTime();\n const daysAgo = (now - modified) / (1000 * 60 * 60 * 24);\n\n // Exponential decay with half-life of 7 days.\n // score = 100 × 2^(-daysAgo / halfLife)\n // This is the standard temporal decay model used in information retrieval\n // (Lv & Zhai, 2011 — \"Adaptive Relevance Feedback\").\n const HALF_LIFE = 7;\n const score = Math.round(100 * Math.pow(2, -daysAgo / HALF_LIFE));\n\n const detail = daysAgo <= 1\n ? 'Modified today'\n : `Modified ${Math.round(daysAgo)} days ago (decay score ${score})`;\n\n return { type: 'recency', score, weight, detail };\n}\n\nfunction computeConfigFactor(file: AnalyzedFile, weight: number): RiskFactor {\n let score: number;\n let detail: string;\n\n if (file.kind === 'entry') {\n score = 90;\n detail = 'Entry point — critical for understanding app structure';\n } else if (file.kind === 'config') {\n score = 80;\n detail = 'Configuration file — affects runtime behavior';\n } else {\n score = 0;\n detail = 'Regular source file';\n }\n\n return { type: 'config', score, weight, detail };\n}\n\nfunction computeChurnFactor(file: AnalyzedFile, weight: number): RiskFactor {\n // PROXY: Without git integration, approximate churn as geometric mean of\n // complexity signal and recency signal. This avoids double-weighting\n // (since complexity and recency have their own factors) by using the\n // *interaction* term only — high churn requires BOTH high complexity AND\n // recent modification simultaneously.\n //\n // TODO: Replace with real git churn (commit count in last 30 days) when\n // git integration is added. Proxy weight is intentionally low (5%).\n const complexitySignal = Math.min(file.complexity / 20, 1);\n const now = Date.now();\n const daysAgo = (now - new Date(file.lastModified).getTime()) / (1000 * 60 * 60 * 24);\n const recencySignal = Math.pow(2, -daysAgo / 7); // same half-life as recency factor\n\n // Geometric mean ensures both signals must be high for a high score\n const score = Math.round(Math.sqrt(complexitySignal * recencySignal) * 100);\n const detail = score >= 50\n ? 'Likely under active development (complex + recent)'\n : score >= 20\n ? 'Some recent activity'\n : 'Stable — low churn (proxy estimate)';\n\n return { type: 'churn', score, weight, detail };\n}\n\n// ===== HELPERS =====\n\nfunction computeWeightedScore(factors: RiskFactor[]): number {\n let totalWeightedScore = 0;\n let totalWeight = 0;\n\n for (const factor of factors) {\n totalWeightedScore += factor.score * factor.weight;\n totalWeight += factor.weight;\n }\n\n if (totalWeight === 0) return 0;\n return Math.round(totalWeightedScore / totalWeight);\n}\n\nfunction scoreToImpact(score: number): ExclusionImpact {\n if (score >= 80) return 'critical';\n if (score >= 60) return 'high';\n if (score >= 30) return 'medium';\n if (score > 0) return 'low';\n return 'none';\n}\n\nfunction computeTypeProviderUsage(\n files: AnalyzedFile[],\n graph: ProjectGraph,\n): Map<string, number> {\n const usage = new Map<string, number>();\n\n // Count how many files import each type file\n const typeFiles = new Set(\n files.filter((f) => f.kind === 'type').map((f) => f.relativePath),\n );\n\n for (const edge of graph.edges) {\n if (typeFiles.has(edge.to)) {\n usage.set(edge.to, (usage.get(edge.to) ?? 0) + 1);\n }\n }\n\n return usage;\n}\n","import { createHash } from 'node:crypto';\nimport { readdir, stat } from 'node:fs/promises';\nimport { join, extname, relative, resolve } from 'node:path';\nimport type { ProjectAnalysis } from '../types/engine.js';\nimport type { CTOConfig } from '../types/config.js';\nimport { DEFAULT_CONFIG } from '../types/config.js';\nimport { analyzeProject } from './analyzer.js';\n\n// ===== Analysis Cache =====\n//\n// Problem: analyzeProject() is expensive (file walk + AST parse + token count + graph build).\n// MCP tools and CLI commands call it on every invocation.\n//\n// Solution: Two-level cache with lightweight fingerprint invalidation.\n// 1. Compute a fast fingerprint: hash of (sorted file paths + mtimes)\n// Cost: O(n) readdir + stat, no file reads, no AST, no token counting\n// 2. If fingerprint matches cached entry → return cached analysis (instant)\n// 3. If fingerprint differs → full re-analysis, update cache\n//\n// TTL: Safety net. Even if fingerprint matches, expire after maxAgeMs.\n//\n// Reference: Content-addressable caching (Git object model principle)\n\ninterface CacheEntry {\n analysis: ProjectAnalysis;\n fingerprint: string;\n createdAt: number;\n hits: number;\n}\n\ninterface CacheOptions {\n maxAgeMs: number;\n maxEntries: number;\n enabled: boolean;\n}\n\nconst DEFAULT_CACHE_OPTIONS: CacheOptions = {\n maxAgeMs: 5 * 60 * 1000, // 5 minutes\n maxEntries: 10,\n enabled: true,\n};\n\n// In-memory LRU-ish cache\nconst cache = new Map<string, CacheEntry>();\nlet cacheOptions: CacheOptions = { ...DEFAULT_CACHE_OPTIONS };\n\n// ===== FINGERPRINT =====\n\n// Fast fingerprint: walk directory, collect paths + mtimes, hash them.\n// ~10-100x faster than full analysis (no file reads, no AST, no tokenization).\nasync function computeFingerprint(\n rootPath: string,\n config: CTOConfig = DEFAULT_CONFIG,\n): Promise<string> {\n const entries: string[] = [];\n const allExtensions = new Set([\n ...config.analysis.extensions.code,\n ...config.analysis.extensions.config,\n ...config.analysis.extensions.docs,\n ]);\n const ignoreDirSet = new Set(config.analysis.ignore.dirs);\n\n async function walk(dir: string, depth: number): Promise<void> {\n if (depth > config.analysis.maxDepth) return;\n\n let dirEntries;\n try {\n dirEntries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n const promises: Promise<void>[] = [];\n\n for (const entry of dirEntries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!ignoreDirSet.has(entry.name) && !entry.name.startsWith('.')) {\n promises.push(walk(fullPath, depth + 1));\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name).slice(1).toLowerCase();\n if (ext && allExtensions.has(ext)) {\n promises.push(\n (async () => {\n try {\n const s = await stat(fullPath);\n const rel = relative(rootPath, fullPath);\n // Include path + mtime + size for change detection\n entries.push(`${rel}:${s.mtimeMs.toFixed(0)}:${s.size}`);\n } catch {\n // skip inaccessible files\n }\n })(),\n );\n }\n }\n }\n\n await Promise.all(promises);\n }\n\n await walk(rootPath, 0);\n\n // Sort for determinism\n entries.sort();\n return createHash('sha256').update(entries.join('|')).digest('hex').substring(0, 16);\n}\n\n// ===== PUBLIC API =====\n\n/**\n * Get a project analysis, using cache when possible.\n *\n * Cache hit: ~1-5ms (fingerprint check only)\n * Cache miss: full analyzeProject() + cache update\n */\nexport async function getCachedAnalysis(\n projectPath: string,\n config?: Partial<CTOConfig>,\n): Promise<ProjectAnalysis> {\n const absPath = resolve(projectPath);\n\n if (!cacheOptions.enabled) {\n return analyzeProject(absPath, config);\n }\n\n const existing = cache.get(absPath);\n\n // Check TTL first (cheap)\n if (existing) {\n const age = Date.now() - existing.createdAt;\n if (age > cacheOptions.maxAgeMs) {\n cache.delete(absPath);\n }\n }\n\n // Compute fingerprint\n const mergedConfig = config\n ? { ...DEFAULT_CONFIG, ...config } as CTOConfig\n : DEFAULT_CONFIG;\n const fingerprint = await computeFingerprint(absPath, mergedConfig);\n\n // Cache hit: fingerprint matches\n const cached = cache.get(absPath);\n if (cached && cached.fingerprint === fingerprint) {\n cached.hits++;\n return cached.analysis;\n }\n\n // Cache miss: full analysis\n const analysis = await analyzeProject(absPath, config);\n\n // Evict oldest entry if at capacity\n if (cache.size >= cacheOptions.maxEntries) {\n const oldest = [...cache.entries()].sort(\n (a, b) => a[1].createdAt - b[1].createdAt,\n )[0];\n if (oldest) cache.delete(oldest[0]);\n }\n\n cache.set(absPath, {\n analysis,\n fingerprint,\n createdAt: Date.now(),\n hits: 0,\n });\n\n return analysis;\n}\n\n/**\n * Invalidate cache for a specific project (e.g., after a known file change).\n */\nexport function invalidateCache(projectPath?: string): void {\n if (projectPath) {\n cache.delete(resolve(projectPath));\n } else {\n cache.clear();\n }\n}\n\n/**\n * Get cache statistics for debugging/monitoring.\n */\nexport function getCacheStats(): {\n entries: number;\n totalHits: number;\n projects: { path: string; hits: number; ageMs: number }[];\n} {\n const now = Date.now();\n const projects = [...cache.entries()].map(([path, entry]) => ({\n path,\n hits: entry.hits,\n ageMs: now - entry.createdAt,\n }));\n\n return {\n entries: cache.size,\n totalHits: projects.reduce((s, p) => s + p.hits, 0),\n projects,\n };\n}\n\n/**\n * Configure cache behavior.\n */\nexport function configureCache(options: Partial<CacheOptions>): void {\n cacheOptions = { ...cacheOptions, ...options };\n if (!cacheOptions.enabled) {\n cache.clear();\n }\n}\n","import { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n ProjectAnalysis,\n ContextSelection,\n SelectedFile,\n SelectionDecision,\n PruneLevel,\n CoverageResult,\n} from '../types/engine.js';\nimport type { PolicySet, PolicyRule } from '../types/govern.js';\nimport { scanFileForSecrets } from '../govern/secrets.js';\nimport { pruneFile } from './pruner.js';\nimport { calculateCoverage } from './coverage.js';\nimport { getPruneLevelForRisk } from './budget.js';\nimport { buildAdjacencyList, bfsBidirectional, matchGlob } from './graph-utils.js';\n\n// ===== DETERMINISTIC CONTEXT SELECTION =====\n//\n// Given a task, analysis, budget, and policies → always returns the same file set.\n// No randomness, no heuristics that change over time.\n//\n// Algorithm:\n// 1. Identify target files from task description\n// 2. Expand via dependency graph (BFS, depth=2)\n// 3. Apply policies (include-always, exclude-always)\n// 4. Score & rank by riskScore\n// 5. Greedy allocation with cascading prune levels\n// 6. Validate policies\n// 7. Calculate coverage score\n// 8. Hash for reproducibility\n\nexport interface SelectionInput {\n task: string;\n analysis: ProjectAnalysis;\n budget: number;\n policies?: PolicySet;\n depth?: number;\n}\n\nexport async function selectContext(input: SelectionInput): Promise<ContextSelection> {\n const { task, analysis, budget, policies, depth = 2 } = input;\n const decisions: SelectionDecision[] = [];\n\n // 1. Identify target files from task description\n const targetPaths = identifyTargetFiles(task, analysis.files);\n if (targetPaths.length > 0) {\n decisions.push({\n file: targetPaths.join(', '),\n action: 'include-full',\n reason: `Target file(s) identified from task description`,\n });\n }\n\n // 2. Expand via dependency graph (O(V+E) using adjacency list)\n const adj = buildAdjacencyList(analysis.graph.edges);\n const expandedPaths = targetPaths.length > 0\n ? Array.from(bfsBidirectional(targetPaths, adj, depth))\n : [];\n const expansionCount = expandedPaths.length - targetPaths.length;\n if (expansionCount > 0) {\n decisions.push({\n file: `${expansionCount} dependencies`,\n action: 'include-full',\n reason: `Expanded ${targetPaths.length} target(s) to ${expandedPaths.length} files via dependency graph (depth ${depth})`,\n });\n }\n\n // 2b. Add type providers of expanded files as candidates.\n // This aligns with coverage.ts which counts type providers as \"relevant\".\n // Without this, coverage would flag missing critical type files that the\n // selector never considered as candidates — a consistency bug.\n const allFileMap = new Map(analysis.files.map((f) => [f.relativePath, f]));\n if (targetPaths.length > 0) {\n for (const path of expandedPaths) {\n const file = allFileMap.get(path);\n if (!file) continue;\n for (const imp of file.imports) {\n const impFile = allFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n expandedPaths.push(imp);\n }\n }\n }\n }\n\n // 3. Apply policies\n const { mustInclude, mustExclude } = applyPolicies(analysis.files, policies);\n\n // Merge: targets + expanded + must-include → candidate set\n const candidateSet = new Set([...expandedPaths, ...mustInclude]);\n\n // If no targets identified, use all files (filtered by exclude)\n if (targetPaths.length === 0) {\n for (const f of analysis.files) {\n candidateSet.add(f.relativePath);\n }\n }\n\n // Remove must-exclude\n for (const ex of mustExclude) {\n candidateSet.delete(ex);\n decisions.push({\n file: ex,\n action: 'exclude',\n reason: 'Excluded by policy',\n });\n }\n\n // 3b. secret-block: scan candidate files for secrets and exclude them\n const hasSecretBlock = policies?.rules.some(\n (r) => r.type === 'secret-block' && r.enabled,\n );\n if (hasSecretBlock) {\n for (const path of Array.from(candidateSet)) {\n const file = allFileMap.get(path);\n if (!file) continue;\n const findings = await scanFileForSecrets(\n file.path,\n analysis.projectPath,\n );\n if (findings.length > 0) {\n candidateSet.delete(path);\n decisions.push({\n file: path,\n action: 'exclude',\n reason: `Blocked: ${findings.length} secret(s) detected (${findings.map((f) => f.type).join(', ')})`,\n });\n }\n }\n }\n\n // 4. Sort candidates by riskScore descending\n const candidates = Array.from(candidateSet)\n .map((p) => allFileMap.get(p))\n .filter((f): f is AnalyzedFile => f !== undefined)\n .sort((a, b) => {\n // Targets always first\n const aIsTarget = targetPaths.includes(a.relativePath) ? 0 : 1;\n const bIsTarget = targetPaths.includes(b.relativePath) ? 0 : 1;\n if (aIsTarget !== bIsTarget) return aIsTarget - bIsTarget;\n\n // Then must-include\n const aIsMust = mustInclude.has(a.relativePath) ? 0 : 1;\n const bIsMust = mustInclude.has(b.relativePath) ? 0 : 1;\n if (aIsMust !== bIsMust) return aIsMust - bIsMust;\n\n // Then by riskScore\n return b.riskScore - a.riskScore;\n });\n\n // 5. Greedy allocation with cascading prune levels\n const selectedFiles: SelectedFile[] = [];\n let usedTokens = 0;\n\n for (const file of candidates) {\n const isTarget = targetPaths.includes(file.relativePath);\n const isMustInclude = mustInclude.has(file.relativePath);\n const defaultLevel = isTarget ? 'full' : getPruneLevelForRisk(file.riskScore);\n const levels = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levels) {\n if (level === 'excluded') break;\n\n let tokens: number;\n if (level === 'full') {\n tokens = file.tokens;\n } else {\n const pruned = await pruneFile(file, level);\n tokens = pruned.prunedTokens;\n }\n\n if (usedTokens + tokens <= budget) {\n usedTokens += tokens;\n selectedFiles.push({\n relativePath: file.relativePath,\n tokens,\n originalTokens: file.tokens,\n pruneLevel: level,\n riskScore: file.riskScore,\n reason: buildReason(file, level, isTarget, isMustInclude),\n });\n\n if (level !== defaultLevel) {\n decisions.push({\n file: file.relativePath,\n action: `include-${level}` as SelectionDecision['action'],\n reason: `Downgraded from ${defaultLevel} to ${level} due to budget constraint`,\n alternatives: `Would need ${file.tokens - tokens} more tokens for ${defaultLevel}`,\n });\n }\n\n included = true;\n break;\n }\n }\n\n if (!included) {\n decisions.push({\n file: file.relativePath,\n action: 'exclude',\n reason: `Budget exhausted (risk: ${file.riskScore}, needs ${file.tokens} tokens)`,\n });\n }\n }\n\n // 6. Calculate coverage\n const includedPaths = selectedFiles.map((f) => f.relativePath);\n const coverage = calculateCoverage(\n targetPaths,\n includedPaths,\n analysis.files,\n analysis.graph,\n depth,\n );\n\n // 7. Calculate overall risk of the selection (lower = better)\n const includedSet = new Set(includedPaths);\n const excludedFiles = analysis.files.filter(\n (f) => !includedSet.has(f.relativePath),\n );\n const excludedRisk = excludedFiles.length > 0\n ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length)\n : 0;\n\n // 8. Hash for determinism\n const hashInput = selectedFiles\n .map((f) => `${f.relativePath}:${f.pruneLevel}`)\n .sort()\n .join('|') + `|budget:${budget}`;\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n return {\n files: selectedFiles,\n totalTokens: usedTokens,\n budget,\n usedPercent: budget > 0 ? Math.round((usedTokens / budget) * 100 * 10) / 10 : 0,\n coverage,\n riskScore: excludedRisk,\n deterministic: true,\n hash,\n decisions,\n };\n}\n\n// ===== TARGET IDENTIFICATION =====\n\nfunction identifyTargetFiles(task: string, files: AnalyzedFile[]): string[] {\n const targets: string[] = [];\n\n // Extract file paths mentioned in the task\n // Matches patterns like: src/foo/bar.ts, ./utils.js, components/Header.tsx\n const pathPattern = /(?:^|\\s|[\"'`])([.\\w/-]+\\.[a-zA-Z]{1,4})(?:\\s|$|[\"'`]|,|:)/g;\n let match: RegExpExecArray | null;\n\n while ((match = pathPattern.exec(task)) !== null) {\n const candidate = match[1];\n // Find matching file\n const found = files.find(\n (f) => f.relativePath === candidate || f.relativePath.endsWith(candidate),\n );\n if (found && !targets.includes(found.relativePath)) {\n targets.push(found.relativePath);\n }\n }\n\n return targets;\n}\n\n// ===== POLICY APPLICATION =====\n\nfunction applyPolicies(\n files: AnalyzedFile[],\n policies?: PolicySet,\n): { mustInclude: Set<string>; mustExclude: Set<string> } {\n const mustInclude = new Set<string>();\n const mustExclude = new Set<string>();\n\n if (!policies) return { mustInclude, mustExclude };\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n if (rule.type === 'include-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustInclude.add(file.relativePath);\n }\n }\n }\n\n if (rule.type === 'exclude-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustExclude.add(file.relativePath);\n }\n }\n }\n\n // secret-block is handled separately in applyPoliciesAsync\n // because it requires file I/O (scanning file contents)\n }\n\n return { mustInclude, mustExclude };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n\nfunction buildReason(\n file: AnalyzedFile,\n level: PruneLevel,\n isTarget: boolean,\n isMustInclude: boolean,\n): string {\n if (isTarget) return 'Target file';\n if (isMustInclude) return 'Required by policy';\n\n const impact = file.exclusionImpact;\n const levelStr = level === 'full' ? 'full content' : level;\n\n if (impact === 'critical') return `Critical dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'high') return `High-risk dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'medium') return `Medium relevance (risk ${file.riskScore}) — ${levelStr}`;\n return `Low relevance (risk ${file.riskScore}) — ${levelStr}`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, relative } from 'node:path';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n];\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const patterns: SecretPattern[] = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import { Project, SyntaxKind, type SourceFile } from 'ts-morph';\nimport { readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { AnalyzedFile, PruneLevel, PrunedContent } from '../types/engine.js';\nimport { countTokensChars4 } from './tokenizer.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs']);\n\n// ===== PUBLIC API =====\n\nexport async function pruneFile(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n if (level === 'excluded') {\n return emptyResult(file, 'excluded');\n }\n\n if (level === 'full') {\n return fullContent(file);\n }\n\n const ext = file.extension.toLowerCase();\n const isTS = TS_EXTENSIONS.has(ext);\n\n if (isTS) {\n return pruneTypeScript(file, level);\n }\n\n return pruneGeneric(file, level);\n}\n\nexport async function pruneFiles(\n files: AnalyzedFile[],\n levelFn: (file: AnalyzedFile) => PruneLevel,\n): Promise<PrunedContent[]> {\n const results: PrunedContent[] = [];\n\n for (const file of files) {\n const level = levelFn(file);\n const pruned = await pruneFile(file, level);\n results.push(pruned);\n }\n\n return results;\n}\n\n// ===== TYPESCRIPT AST-BASED PRUNING =====\n\nasync function pruneTypeScript(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n let project: Project;\n try {\n const tsConfigPath = findTsConfig(file.path);\n project = new Project({\n tsConfigFilePath: tsConfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: tsConfigPath\n ? undefined\n : { allowJs: true, esModuleInterop: true },\n });\n project.createSourceFile(file.path, content, { overwrite: true });\n } catch {\n // Fallback to generic pruning if AST fails\n return pruneGenericFromContent(file, content, level);\n }\n\n const sourceFile = project.getSourceFiles()[0];\n if (!sourceFile) {\n return pruneGenericFromContent(file, content, level);\n }\n\n const prunedContent = level === 'signatures'\n ? extractSignaturesAST(sourceFile)\n : extractSkeletonAST(sourceFile);\n\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== SIGNATURES: Keep imports, type defs, function signatures, class outlines =====\n\nfunction extractSignaturesAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases\n for (const ta of sf.getTypeAliases()) {\n addJSDoc(ta, parts);\n parts.push(ta.getText());\n }\n\n // Interfaces\n for (const iface of sf.getInterfaces()) {\n addJSDoc(iface, parts);\n parts.push(iface.getText());\n }\n\n // Enums\n for (const en of sf.getEnums()) {\n addJSDoc(en, parts);\n parts.push(en.getText());\n }\n\n // Function declarations — signature only\n for (const fn of sf.getFunctions()) {\n addJSDoc(fn, parts);\n const isExported = fn.isExported();\n const isAsync = fn.isAsync();\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n const returnType = fn.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n\n const prefix = isExported ? 'export ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);\n }\n\n // Variable declarations (const/let with arrow functions)\n for (const stmt of sf.getVariableStatements()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n addJSDoc(stmt, parts);\n const isExported = stmt.isExported();\n const prefix = isExported ? 'export ' : '';\n const kind = stmt.getDeclarationKind();\n const name = decl.getName();\n const typeNode = decl.getTypeNode()?.getText();\n const typeStr = typeNode ? `: ${typeNode}` : '';\n parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);\n } else {\n // Non-function variables: keep full declaration\n addJSDoc(stmt, parts);\n parts.push(stmt.getText());\n }\n }\n }\n\n // Classes — outline with method signatures\n for (const cls of sf.getClasses()) {\n addJSDoc(cls, parts);\n const isExported = cls.isExported();\n const prefix = isExported ? 'export ' : '';\n const name = cls.getName() ?? '<anonymous>';\n const ext = cls.getExtends()?.getText();\n const impl = cls.getImplements().map((i) => i.getText()).join(', ');\n let header = `${prefix}class ${name}`;\n if (ext) header += ` extends ${ext}`;\n if (impl) header += ` implements ${impl}`;\n header += ' {';\n parts.push(header);\n\n // Properties\n for (const prop of cls.getProperties()) {\n parts.push(` ${prop.getText()}`);\n }\n\n // Constructor\n const ctor = cls.getConstructors()[0];\n if (ctor) {\n const ctorParams = ctor.getParameters().map((p) => p.getText()).join(', ');\n parts.push(` constructor(${ctorParams}) { /* ... */ }`);\n }\n\n // Methods — signature only\n for (const method of cls.getMethods()) {\n const isStatic = method.isStatic();\n const isAsync = method.isAsync();\n const methodName = method.getName();\n const methodParams = method.getParameters().map((p) => p.getText()).join(', ');\n const returnType = method.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n const staticStr = isStatic ? 'static ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);\n }\n\n parts.push('}');\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n // Export assignments\n for (const exp of sf.getExportAssignments()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== SKELETON: Keep only declarations, no bodies at all =====\n\nfunction extractSkeletonAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases — full\n for (const ta of sf.getTypeAliases()) {\n if (ta.isExported()) parts.push(ta.getText());\n }\n\n // Interfaces — name + extends only\n for (const iface of sf.getInterfaces()) {\n if (!iface.isExported()) continue;\n const ext = iface.getExtends().map((e) => e.getText());\n const extStr = ext.length > 0 ? ` extends ${ext.join(', ')}` : '';\n parts.push(`export interface ${iface.getName()}${extStr} { /* ${iface.getProperties().length} props */ }`);\n }\n\n // Enums — name only\n for (const en of sf.getEnums()) {\n if (!en.isExported()) continue;\n const members = en.getMembers().map((m) => m.getName());\n parts.push(`export enum ${en.getName()} { ${members.join(', ')} }`);\n }\n\n // Functions — name + params only\n for (const fn of sf.getFunctions()) {\n if (!fn.isExported()) continue;\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n parts.push(`export function ${name}(${params});`);\n }\n\n // Classes — name + method names only\n for (const cls of sf.getClasses()) {\n if (!cls.isExported()) continue;\n const methods = cls.getMethods().map((m) => m.getName());\n parts.push(`export class ${cls.getName()} { /* methods: ${methods.join(', ')} */ }`);\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== GENERIC PRUNING (non-TS files) =====\n\nasync function pruneGeneric(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n return pruneGenericFromContent(file, content, level);\n}\n\nfunction pruneGenericFromContent(\n file: AnalyzedFile,\n content: string,\n level: PruneLevel,\n): PrunedContent {\n const lines = content.split('\\n');\n let result: string[];\n\n if (level === 'signatures') {\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t === '' ||\n t.startsWith('#') ||\n t.startsWith('//') ||\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('async def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n t.startsWith('const ') ||\n t.startsWith('let ') ||\n t.startsWith('var ') ||\n /^(pub |fn |struct |enum |impl |mod |use )/.test(t)\n );\n });\n } else {\n // skeleton: even more aggressive — only declarations\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n /^(pub |fn |struct |enum |mod |use )/.test(t)\n );\n });\n }\n\n const prunedContent = result.join('\\n');\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== HELPERS =====\n\nasync function fullContent(file: AnalyzedFile): Promise<PrunedContent> {\n let content = '';\n try {\n content = await readFile(file.path, 'utf-8');\n } catch { /* empty */ }\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: file.tokens,\n pruneLevel: 'full',\n content,\n savingsPercent: 0,\n };\n}\n\nfunction emptyResult(file: AnalyzedFile, level: PruneLevel): PrunedContent {\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: 0,\n pruneLevel: level,\n content: '',\n savingsPercent: 100,\n };\n}\n\nfunction addJSDoc(node: { getJsDocs?: () => { getText(): string }[] }, parts: string[]): void {\n if (!node.getJsDocs) return;\n const docs = node.getJsDocs();\n if (docs.length > 0) {\n parts.push(docs[0].getText());\n }\n}\n\nfunction findTsConfig(filePath: string): string | undefined {\n let dir = filePath;\n for (let i = 0; i < 10; i++) {\n dir = join(dir, '..');\n const candidate = join(dir, 'tsconfig.json');\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n AnalyzedFile,\n ProjectGraph,\n CoverageResult,\n} from '../types/engine.js';\nimport { buildAdjacencyList, bfsBidirectional } from './graph-utils.js';\n\n// ===== CONTEXT COVERAGE SCORING =====\n//\n// Given a set of included files and a task's target files, calculate\n// how much of the \"relevant universe\" is covered.\n//\n// coverageScore = |includedRelevant| / |relevantFiles| × 100\n//\n// \"Relevant\" = target files + their dependencies (up to `depth` levels)\n// + type providers used by included files.\n\nexport function calculateCoverage(\n targetPaths: string[],\n includedPaths: string[],\n allFiles: AnalyzedFile[],\n graph: ProjectGraph,\n depth: number = 2,\n): CoverageResult {\n // 1. Find all relevant files: targets + their dependency cone\n // Uses adjacency list for O(V+E) BFS instead of scanning all edges per node.\n const adj = buildAdjacencyList(graph.edges);\n const relevantSet = targetPaths.length > 0\n ? bfsBidirectional(targetPaths, adj, depth)\n : new Set<string>();\n const includedSet = new Set(includedPaths);\n\n // Also add type providers used by included files\n const tempFileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n for (const path of includedPaths) {\n const file = tempFileMap.get(path);\n if (!file) continue;\n\n for (const imp of file.imports) {\n const impFile = tempFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n relevantSet.add(imp);\n }\n }\n }\n\n // 2. Calculate coverage\n const relevantFiles = Array.from(relevantSet);\n const includedRelevant = relevantFiles.filter((f) => includedSet.has(f));\n const missingRelevant = relevantFiles.filter((f) => !includedSet.has(f));\n\n // Missing critical = missing files with critical/high exclusion impact\n const missingCritical = missingRelevant.filter((f) => {\n const file = tempFileMap.get(f);\n return file && (file.exclusionImpact === 'critical' || file.exclusionImpact === 'high');\n });\n\n // Risk-weighted coverage: missing a high-risk file hurts more than missing a low-risk one.\n // score = Σ(riskScore of included relevant) / Σ(riskScore of all relevant) × 100\n // Falls back to count-based if no risk data is available.\n const fileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n let totalRelevantRisk = 0;\n let includedRelevantRisk = 0;\n\n for (const f of relevantFiles) {\n const risk = fileMap.get(f)?.riskScore ?? 1;\n totalRelevantRisk += risk;\n if (includedSet.has(f)) {\n includedRelevantRisk += risk;\n }\n }\n\n const score = totalRelevantRisk > 0\n ? Math.round((includedRelevantRisk / totalRelevantRisk) * 100)\n : relevantFiles.length > 0\n ? Math.round((includedRelevant.length / relevantFiles.length) * 100)\n : 100;\n\n // Build explanation\n let explanation: string;\n if (score >= 90) {\n explanation = `Excellent coverage (${score}%): AI has nearly all relevant context.`;\n } else if (score >= 70) {\n explanation = `Good coverage (${score}%): Most relevant files included.`;\n if (missingCritical.length > 0) {\n explanation += ` Warning: ${missingCritical.length} critical file(s) missing.`;\n }\n } else if (score >= 50) {\n explanation = `Partial coverage (${score}%): Significant context is missing.`;\n if (missingCritical.length > 0) {\n explanation += ` ${missingCritical.length} critical file(s) not included — AI quality will degrade.`;\n }\n } else {\n explanation = `Low coverage (${score}%): Most relevant files are excluded. AI response quality will be poor.`;\n }\n\n return {\n score,\n relevantFiles,\n includedRelevant,\n missingRelevant,\n missingCritical,\n explanation,\n };\n}\n","import type { AnalyzedFile, BudgetPlan, BudgetEntry, PruneLevel } from '../types/engine.js';\nimport { pruneFile } from './pruner.js';\n\n// ===== BUDGET OPTIMIZER =====\n//\n// Greedy knapsack with cascading prune levels.\n// Files are sorted by riskScore (descending) — highest risk first.\n// Each file is tried at progressively smaller prune levels until it fits.\n\nexport function getPruneLevelForRisk(riskScore: number): PruneLevel {\n if (riskScore >= 80) return 'full'; // critical — include everything, no cascading\n if (riskScore >= 60) return 'full'; // high — try full first, can cascade\n if (riskScore >= 30) return 'signatures'; // medium — signatures by default\n return 'skeleton'; // low — skeleton\n}\n\nexport function isCriticalRisk(riskScore: number): boolean {\n return riskScore >= 80;\n}\n\nexport async function optimizeBudget(\n files: AnalyzedFile[],\n budget: number,\n): Promise<BudgetPlan> {\n const entries: BudgetEntry[] = [];\n let used = 0;\n\n // Sort by riskScore descending — most important files first\n const sorted = [...files].sort((a, b) => b.riskScore - a.riskScore);\n\n for (const file of sorted) {\n const defaultLevel = getPruneLevelForRisk(file.riskScore);\n\n // Try cascading prune levels: full → signatures → skeleton → excluded\n const levelsToTry = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levelsToTry) {\n if (level === 'excluded') break;\n\n if (level === 'full') {\n if (used + file.tokens <= budget) {\n used += file.tokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: file.tokens,\n pruneLevel: 'full',\n included: true,\n reason: `Risk ${file.riskScore} — included in full`,\n });\n included = true;\n break;\n }\n } else {\n const pruned = await pruneFile(file, level);\n if (pruned.prunedTokens > 0 && used + pruned.prunedTokens <= budget) {\n used += pruned.prunedTokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: pruned.prunedTokens,\n pruneLevel: level,\n included: true,\n reason: `Risk ${file.riskScore} — pruned to ${level} (budget constraint)`,\n });\n included = true;\n break;\n }\n }\n }\n\n if (!included) {\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: 0,\n pruneLevel: 'excluded',\n included: false,\n reason: `Risk ${file.riskScore} — excluded (budget exhausted, ${file.tokens} tokens)`,\n });\n }\n }\n\n return {\n budget,\n used,\n remaining: budget - used,\n fillPercent: budget > 0 ? Math.round((used / budget) * 100 * 10) / 10 : 0,\n files: entries,\n };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n","import type { ProjectAnalysis, AnalyzedFile } from '../types/engine.js';\nimport type { ContextSelection } from '../types/engine.js';\nimport { selectContext } from './selector.js';\nimport { calculateCoverage } from './coverage.js';\nimport { buildAdjacencyList } from './graph-utils.js';\n\n// ===== Context Score™ =====\n//\n// A single 0-100 metric that quantifies how well a project's context\n// can be optimized for AI consumption.\n//\n// The score answers: \"If I send this project to an AI with CTO,\n// how much value am I getting per token?\"\n//\n// Dimensions:\n// 1. Efficiency (30%) — How much can we compress without losing value?\n// 2. Coverage (25%) — How much of the relevant universe do we capture?\n// 3. Risk Control (20%) — Are high-risk files properly prioritized?\n// 4. Structure (15%) — How well-organized is the codebase for AI consumption?\n// 5. Governance (10%) — Is the project set up for safe, auditable AI use?\n//\n// Grade scale:\n// A+ (95-100) | A (90-94) | A- (85-89)\n// B+ (80-84) | B (75-79) | B- (70-74)\n// C+ (65-69) | C (60-64) | C- (55-59)\n// D (40-54) | F (0-39)\n\nexport type Grade = 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' | 'C+' | 'C' | 'C-' | 'D' | 'F';\n\nexport interface ContextScore {\n overall: number;\n grade: Grade;\n\n dimensions: {\n efficiency: DimensionScore;\n coverage: DimensionScore;\n riskControl: DimensionScore;\n structure: DimensionScore;\n governance: DimensionScore;\n };\n\n insights: ScoreInsight[];\n\n comparison: {\n naiveTokens: number;\n optimizedTokens: number;\n savedTokens: number;\n savedPercent: number;\n monthlySavingsUSD: number;\n };\n\n meta: {\n projectName: string;\n totalFiles: number;\n totalTokens: number;\n analyzedAt: Date;\n };\n}\n\nexport interface DimensionScore {\n score: number;\n weight: number;\n weighted: number;\n detail: string;\n}\n\nexport interface ScoreInsight {\n type: 'strength' | 'weakness' | 'opportunity';\n title: string;\n detail: string;\n impact: 'high' | 'medium' | 'low';\n}\n\n// ===== MAIN API =====\n\n/**\n * Compute the Context Score™ for a project.\n *\n * @param analysis - Project analysis (from analyzeProject or getCachedAnalysis)\n * @param task - Representative task (default: \"general code review\")\n * @param budget - Token budget (default: 50000)\n */\nexport async function computeContextScore(\n analysis: ProjectAnalysis,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<ContextScore> {\n // Run context selection to get real numbers\n const selection = await selectContext({ task, analysis, budget });\n\n const insights: ScoreInsight[] = [];\n\n // 1. EFFICIENCY (30%) — compression ratio + pruning effectiveness\n const efficiency = scoreEfficiency(analysis, selection, insights);\n\n // 2. COVERAGE (25%) — how much of the relevant universe is captured\n const coverage = scoreCoverage(analysis, selection, insights);\n\n // 3. RISK CONTROL (20%) — high-risk file prioritization\n const riskControl = scoreRiskControl(analysis, selection, insights);\n\n // 4. STRUCTURE (15%) — codebase organization for AI\n const structure = scoreStructure(analysis, insights);\n\n // 5. GOVERNANCE (10%) — project setup for safe AI use\n const governance = scoreGovernance(analysis, insights);\n\n // Overall\n const overall = Math.round(\n efficiency.weighted + coverage.weighted + riskControl.weighted +\n structure.weighted + governance.weighted\n );\n\n const grade = scoreToGrade(overall);\n\n // Comparison: CTO vs naive (send everything)\n const naiveTokens = analysis.totalTokens;\n const optimizedTokens = selection.totalTokens;\n const savedTokens = naiveTokens - optimizedTokens;\n const savedPercent = naiveTokens > 0 ? Math.round((savedTokens / naiveTokens) * 100) : 0;\n\n // Monthly savings estimate: assume 40 interactions/day, 20 working days\n // Claude Sonnet: $3/M input tokens\n const interactionsPerMonth = 40 * 20;\n const costPerMToken = 3; // $/M tokens (Sonnet)\n const naiveMonthlyCost = (naiveTokens / 1_000_000) * costPerMToken * interactionsPerMonth;\n const optimizedMonthlyCost = (optimizedTokens / 1_000_000) * costPerMToken * interactionsPerMonth;\n const monthlySavingsUSD = Math.round((naiveMonthlyCost - optimizedMonthlyCost) * 100) / 100;\n\n return {\n overall,\n grade,\n dimensions: {\n efficiency,\n coverage,\n riskControl,\n structure,\n governance,\n },\n insights: insights.sort((a, b) => {\n const order = { high: 0, medium: 1, low: 2 };\n return order[a.impact] - order[b.impact];\n }),\n comparison: {\n naiveTokens,\n optimizedTokens,\n savedTokens,\n savedPercent,\n monthlySavingsUSD,\n },\n meta: {\n projectName: analysis.projectName,\n totalFiles: analysis.totalFiles,\n totalTokens: analysis.totalTokens,\n analyzedAt: analysis.analyzedAt,\n },\n };\n}\n\n// ===== DIMENSION SCORERS =====\n\nfunction scoreEfficiency(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 30;\n\n // Compression ratio: how much we reduced tokens\n const ratio = analysis.totalTokens > 0\n ? 1 - (selection.totalTokens / analysis.totalTokens)\n : 0;\n\n // File selectivity: what % of files were excluded\n const selectivity = analysis.totalFiles > 0\n ? 1 - (selection.files.length / analysis.totalFiles)\n : 0;\n\n // Pruning effectiveness: how many files got pruned (signatures/skeleton)\n const prunedFiles = selection.files.filter(\n (f) => f.pruneLevel === 'signatures' || f.pruneLevel === 'skeleton'\n ).length;\n const pruneRatio = selection.files.length > 0\n ? prunedFiles / selection.files.length\n : 0;\n\n // Weighted combination\n const raw = (ratio * 0.5 + selectivity * 0.3 + pruneRatio * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (ratio > 0.7) {\n insights.push({\n type: 'strength',\n title: 'Excellent compression',\n detail: `${Math.round(ratio * 100)}% token reduction while maintaining context quality`,\n impact: 'high',\n });\n }\n\n if (ratio < 0.3 && analysis.totalTokens > 20_000) {\n insights.push({\n type: 'weakness',\n title: 'Low compression opportunity',\n detail: 'Most files are needed. Consider splitting the project into smaller modules.',\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${Math.round(ratio * 100)}% compression, ${prunedFiles}/${selection.files.length} files pruned`,\n };\n}\n\nfunction scoreCoverage(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 25;\n\n const coverageScore = selection.coverage.score;\n const missingCritical = selection.coverage.missingCritical.length;\n\n // Penalize missing critical files heavily\n let penalty = 0;\n if (missingCritical > 0) {\n penalty = Math.min(30, missingCritical * 10);\n insights.push({\n type: 'weakness',\n title: `${missingCritical} critical file(s) missing from context`,\n detail: `Missing: ${selection.coverage.missingCritical.slice(0, 3).join(', ')}${missingCritical > 3 ? ` +${missingCritical - 3} more` : ''}`,\n impact: 'high',\n });\n }\n\n const score = Math.min(100, Math.max(0, Math.round(coverageScore - penalty)));\n const weighted = (score / 100) * weight;\n\n if (coverageScore >= 90 && missingCritical === 0) {\n insights.push({\n type: 'strength',\n title: 'Excellent context coverage',\n detail: `${coverageScore}% of the relevant universe captured with zero critical gaps`,\n impact: 'high',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${coverageScore}% coverage, ${missingCritical} critical gaps`,\n };\n}\n\nfunction scoreRiskControl(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 20;\n\n const dist = analysis.riskProfile.distribution;\n const totalFiles = analysis.totalFiles;\n\n // What % of critical+high risk files are included in the selection?\n const criticalFiles = analysis.files.filter((f) => f.riskScore >= 80);\n const highFiles = analysis.files.filter((f) => f.riskScore >= 60 && f.riskScore < 80);\n const selectedPaths = new Set(selection.files.map((f) => f.relativePath));\n\n const criticalIncluded = criticalFiles.filter((f) => selectedPaths.has(f.relativePath)).length;\n const highIncluded = highFiles.filter((f) => selectedPaths.has(f.relativePath)).length;\n\n const criticalCoverage = criticalFiles.length > 0\n ? criticalIncluded / criticalFiles.length\n : 1;\n const highCoverage = highFiles.length > 0\n ? highIncluded / highFiles.length\n : 1;\n\n // Risk distribution health: fewer critical files = healthier\n const criticalRatio = totalFiles > 0 ? dist.critical / totalFiles : 0;\n const healthScore = Math.max(0, 1 - criticalRatio * 5); // penalize heavy critical\n\n const raw = (criticalCoverage * 0.5 + highCoverage * 0.3 + healthScore * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (criticalCoverage === 1 && criticalFiles.length > 0) {\n insights.push({\n type: 'strength',\n title: 'All critical files included',\n detail: `${criticalFiles.length} critical-risk files are captured in context`,\n impact: 'high',\n });\n }\n\n if (criticalRatio > 0.2) {\n insights.push({\n type: 'opportunity',\n title: 'High concentration of critical files',\n detail: `${dist.critical} files (${Math.round(criticalRatio * 100)}%) are critical risk. Consider refactoring complex modules.`,\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${criticalIncluded}/${criticalFiles.length} critical + ${highIncluded}/${highFiles.length} high-risk files included`,\n };\n}\n\nfunction scoreStructure(\n analysis: ProjectAnalysis,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 15;\n\n const graph = analysis.graph;\n const totalFiles = analysis.totalFiles;\n\n // Cluster cohesion: well-organized projects have clear clusters\n const avgCohesion = graph.clusters.length > 0\n ? graph.clusters.reduce((s, c) => s + c.cohesion, 0) / graph.clusters.length\n : 0;\n\n // Orphan penalty: too many orphans = poor structure\n const orphanRatio = totalFiles > 0 ? graph.orphans.length / totalFiles : 0;\n\n // Hub concentration: too few hubs = flat, too many = coupled\n const hubRatio = totalFiles > 0 ? graph.hubs.length / totalFiles : 0;\n const hubHealth = hubRatio > 0.02 && hubRatio < 0.15 ? 1 : Math.max(0, 1 - Math.abs(hubRatio - 0.08) * 10);\n\n // Type coverage: having type files is good for AI\n const typeFiles = analysis.files.filter((f) => f.kind === 'type').length;\n const typeRatio = totalFiles > 0 ? typeFiles / totalFiles : 0;\n const typeScore = Math.min(1, typeRatio * 10); // reward up to 10% types\n\n const raw = (avgCohesion * 0.3 + (1 - orphanRatio) * 0.3 + hubHealth * 0.2 + typeScore * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (orphanRatio > 0.5) {\n insights.push({\n type: 'weakness',\n title: 'Many orphan files',\n detail: `${graph.orphans.length} files (${Math.round(orphanRatio * 100)}%) have no imports/exports. AI gets less context from the dependency graph.`,\n impact: 'medium',\n });\n }\n\n if (graph.clusters.length > 0 && avgCohesion > 0.7) {\n insights.push({\n type: 'strength',\n title: 'Well-organized module structure',\n detail: `${graph.clusters.length} cohesive clusters (avg cohesion: ${(avgCohesion * 100).toFixed(0)}%). CTO can efficiently select relevant modules.`,\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${graph.clusters.length} clusters, ${graph.orphans.length} orphans, ${graph.hubs.length} hubs`,\n };\n}\n\nfunction scoreGovernance(\n analysis: ProjectAnalysis,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 10;\n\n // Check for config/policy/type organization\n const hasTypes = analysis.files.some((f) => f.kind === 'type');\n const hasConfig = analysis.files.some((f) => f.kind === 'config');\n const hasTests = analysis.files.some((f) => f.kind === 'test');\n\n let score = 50; // baseline\n\n if (hasTypes) { score += 15; }\n if (hasConfig) { score += 10; }\n if (hasTests) { score += 15; }\n\n // Stack detection: known stacks get better AI support\n if (analysis.stack.length > 0) { score += 10; }\n\n score = Math.min(100, score);\n const weighted = (score / 100) * weight;\n\n if (!hasTests) {\n insights.push({\n type: 'opportunity',\n title: 'No test files detected',\n detail: 'Adding tests helps CTO understand code intent and provides better context boundaries.',\n impact: 'low',\n });\n }\n\n if (!hasTypes) {\n insights.push({\n type: 'opportunity',\n title: 'No type definition files',\n detail: 'Type files dramatically improve AI code generation accuracy. Consider adding interfaces/types.',\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `types:${hasTypes ? '✓' : '✗'} tests:${hasTests ? '✓' : '✗'} config:${hasConfig ? '✓' : '✗'} stack:${analysis.stack.join(',') || 'unknown'}`,\n };\n}\n\n// ===== HELPERS =====\n\nfunction scoreToGrade(score: number): Grade {\n if (score >= 95) return 'A+';\n if (score >= 90) return 'A';\n if (score >= 85) return 'A-';\n if (score >= 80) return 'B+';\n if (score >= 75) return 'B';\n if (score >= 70) return 'B-';\n if (score >= 65) return 'C+';\n if (score >= 60) return 'C';\n if (score >= 55) return 'C-';\n if (score >= 40) return 'D';\n return 'F';\n}\n\n// ===== RENDERING =====\n\n/**\n * Render the Context Score as a beautiful terminal-friendly string.\n */\nexport function renderContextScore(score: ContextScore): string {\n const lines: string[] = [];\n\n const gradeColor = score.grade.startsWith('A') ? '🟢'\n : score.grade.startsWith('B') ? '🔵'\n : score.grade.startsWith('C') ? '🟡'\n : '🔴';\n\n lines.push('');\n lines.push(` ╔══════════════════════════════════════════════════╗`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${gradeColor} Context Score™ ${padCenter(score.overall.toString(), 3)} / 100 Grade: ${padCenter(score.grade, 2)} ║`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${score.meta.projectName.padEnd(20).substring(0, 20)} ║`);\n lines.push(` ║ ${score.meta.totalFiles} files · ${Math.round(score.meta.totalTokens / 1000)}K tokens ║`);\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n\n // Dimensions\n const dims = [\n { label: 'Efficiency', dim: score.dimensions.efficiency },\n { label: 'Coverage', dim: score.dimensions.coverage },\n { label: 'Risk Control', dim: score.dimensions.riskControl },\n { label: 'Structure', dim: score.dimensions.structure },\n { label: 'Governance', dim: score.dimensions.governance },\n ];\n\n lines.push(` ║ ║`);\n\n for (const { label, dim } of dims) {\n const bar = renderBar(dim.score, 20);\n const pct = dim.score.toString().padStart(3);\n lines.push(` ║ ${label.padEnd(14)} ${bar} ${pct}% (×${dim.weight}%) ║`);\n }\n\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n\n // Comparison\n lines.push(` ║ ║`);\n lines.push(` ║ 💰 vs. Sending Everything: ║`);\n lines.push(` ║ Tokens saved: ${formatNumber(score.comparison.savedTokens).padEnd(12)} (${score.comparison.savedPercent}%) ║`);\n lines.push(` ║ Monthly savings: $${score.comparison.monthlySavingsUSD.toFixed(2).padEnd(10)} ║`);\n lines.push(` ║ (${score.comparison.optimizedTokens.toLocaleString()} vs ${score.comparison.naiveTokens.toLocaleString()} tokens) ║`);\n lines.push(` ║ ║`);\n\n // Insights\n if (score.insights.length > 0) {\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n\n const top = score.insights.slice(0, 5);\n for (const insight of top) {\n const icon = insight.type === 'strength' ? '✅'\n : insight.type === 'weakness' ? '⚠️'\n : '💡';\n const title = insight.title.substring(0, 44).padEnd(44);\n lines.push(` ║ ${icon} ${title} ║`);\n }\n\n lines.push(` ║ ║`);\n }\n\n lines.push(` ╚══════════════════════════════════════════════════╝`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction renderBar(pct: number, width: number): string {\n const filled = Math.round((pct / 100) * width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\nfunction padCenter(str: string, width: number): string {\n const pad = Math.max(0, width - str.length);\n const left = Math.floor(pad / 2);\n return ' '.repeat(left) + str + ' '.repeat(pad - left);\n}\n\nfunction formatNumber(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n","import type { ProjectAnalysis } from '../types/engine.js';\nimport { selectContext } from './selector.js';\n\n// ===== Benchmark Engine =====\n//\n// Proves CTO works with real numbers. Compares three strategies:\n// 1. CTO (risk-aware, deterministic selection)\n// 2. Naive (send everything — what most people do)\n// 3. Random (randomly pick files up to budget — baseline)\n//\n// Measures: tokens, coverage, cost, time\n\nexport interface BenchmarkResult {\n project: string;\n totalFiles: number;\n totalTokens: number;\n budget: number;\n task: string;\n\n strategies: {\n cto: StrategyResult;\n naive: StrategyResult;\n random: StrategyResult;\n };\n\n winner: 'cto' | 'naive' | 'random';\n ctoAdvantage: {\n vsNaiveTokensSaved: number;\n vsNaiveTokensSavedPercent: number;\n vsRandomCoverageGain: number;\n vsNaiveCostSavedMonthlyUSD: number;\n };\n}\n\nexport interface StrategyResult {\n filesSelected: number;\n tokensUsed: number;\n coverageScore: number;\n criticalFilesCovered: number;\n criticalFilesTotal: number;\n highRiskCovered: number;\n highRiskTotal: number;\n costPerInteractionUSD: number;\n timeMs: number;\n}\n\n/**\n * Run a full benchmark comparing CTO vs naive vs random selection.\n */\nexport async function runBenchmark(\n analysis: ProjectAnalysis,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<BenchmarkResult> {\n const criticalFiles = analysis.files.filter((f) => f.riskScore >= 80);\n const highRiskFiles = analysis.files.filter((f) => f.riskScore >= 60 && f.riskScore < 80);\n\n // ===== STRATEGY 1: CTO (risk-aware selection) =====\n const ctoStart = performance.now();\n const ctoSelection = await selectContext({ task, analysis, budget });\n const ctoTime = performance.now() - ctoStart;\n\n const ctoSelectedPaths = new Set(ctoSelection.files.map((f) => f.relativePath));\n const ctoCritical = criticalFiles.filter((f) => ctoSelectedPaths.has(f.relativePath)).length;\n const ctoHigh = highRiskFiles.filter((f) => ctoSelectedPaths.has(f.relativePath)).length;\n\n const cto: StrategyResult = {\n filesSelected: ctoSelection.files.length,\n tokensUsed: ctoSelection.totalTokens,\n coverageScore: ctoSelection.coverage.score,\n criticalFilesCovered: ctoCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: ctoHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: (ctoSelection.totalTokens / 1_000_000) * 3, // Sonnet pricing\n timeMs: Math.round(ctoTime),\n };\n\n // ===== STRATEGY 2: NAIVE (send everything up to budget) =====\n const naiveStart = performance.now();\n const naiveFiles = [...analysis.files].sort((a, b) => a.relativePath.localeCompare(b.relativePath));\n let naiveTokens = 0;\n let naiveCount = 0;\n const naiveSelectedPaths = new Set<string>();\n\n for (const f of naiveFiles) {\n if (naiveTokens + f.tokens <= budget) {\n naiveTokens += f.tokens;\n naiveCount++;\n naiveSelectedPaths.add(f.relativePath);\n }\n }\n // If budget exceeds total, include everything\n if (naiveTokens === 0 && analysis.totalTokens <= budget) {\n naiveTokens = analysis.totalTokens;\n naiveCount = analysis.totalFiles;\n for (const f of analysis.files) naiveSelectedPaths.add(f.relativePath);\n }\n const naiveTime = performance.now() - naiveStart;\n\n const naiveCritical = criticalFiles.filter((f) => naiveSelectedPaths.has(f.relativePath)).length;\n const naiveHigh = highRiskFiles.filter((f) => naiveSelectedPaths.has(f.relativePath)).length;\n\n // Naive coverage: just count how many of the relevant files are included\n const naiveCoverage = analysis.totalFiles > 0\n ? Math.round((naiveCount / analysis.totalFiles) * 100)\n : 0;\n\n const naive: StrategyResult = {\n filesSelected: naiveCount,\n tokensUsed: naiveTokens > 0 ? naiveTokens : analysis.totalTokens,\n coverageScore: naiveTokens > 0 ? naiveCoverage : 100,\n criticalFilesCovered: naiveCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: naiveHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: ((naiveTokens > 0 ? naiveTokens : analysis.totalTokens) / 1_000_000) * 3,\n timeMs: Math.round(naiveTime),\n };\n\n // ===== STRATEGY 3: RANDOM (randomly pick files up to budget) =====\n const randomStart = performance.now();\n const shuffled = [...analysis.files].sort(() => Math.random() - 0.5);\n let randomTokens = 0;\n let randomCount = 0;\n const randomSelectedPaths = new Set<string>();\n\n for (const f of shuffled) {\n if (randomTokens + f.tokens <= budget) {\n randomTokens += f.tokens;\n randomCount++;\n randomSelectedPaths.add(f.relativePath);\n }\n }\n const randomTime = performance.now() - randomStart;\n\n const randomCritical = criticalFiles.filter((f) => randomSelectedPaths.has(f.relativePath)).length;\n const randomHigh = highRiskFiles.filter((f) => randomSelectedPaths.has(f.relativePath)).length;\n const randomCoverage = analysis.totalFiles > 0\n ? Math.round((randomCount / analysis.totalFiles) * 100)\n : 0;\n\n const random: StrategyResult = {\n filesSelected: randomCount,\n tokensUsed: randomTokens,\n coverageScore: randomCoverage,\n criticalFilesCovered: randomCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: randomHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: (randomTokens / 1_000_000) * 3,\n timeMs: Math.round(randomTime),\n };\n\n // ===== DETERMINE WINNER =====\n // Score: coverage * 0.5 + criticalCoverage * 0.3 + tokenEfficiency * 0.2\n const ctoScore = computeStrategyScore(cto, budget);\n const naiveScore = computeStrategyScore(naive, budget);\n const randomScore = computeStrategyScore(random, budget);\n\n const winner = ctoScore >= naiveScore && ctoScore >= randomScore ? 'cto'\n : naiveScore >= randomScore ? 'naive' : 'random';\n\n // Monthly savings: 40 interactions/day × 20 days\n const interactionsPerMonth = 800;\n const vsNaiveCostSaved = (naive.costPerInteractionUSD - cto.costPerInteractionUSD) * interactionsPerMonth;\n\n return {\n project: analysis.projectName,\n totalFiles: analysis.totalFiles,\n totalTokens: analysis.totalTokens,\n budget,\n task,\n strategies: { cto, naive, random },\n winner,\n ctoAdvantage: {\n vsNaiveTokensSaved: naive.tokensUsed - cto.tokensUsed,\n vsNaiveTokensSavedPercent: naive.tokensUsed > 0\n ? Math.round(((naive.tokensUsed - cto.tokensUsed) / naive.tokensUsed) * 100)\n : 0,\n vsRandomCoverageGain: cto.coverageScore - random.coverageScore,\n vsNaiveCostSavedMonthlyUSD: Math.round(vsNaiveCostSaved * 100) / 100,\n },\n };\n}\n\nfunction computeStrategyScore(strategy: StrategyResult, budget: number): number {\n const coverageWeight = strategy.coverageScore / 100;\n const criticalWeight = strategy.criticalFilesTotal > 0\n ? strategy.criticalFilesCovered / strategy.criticalFilesTotal\n : 1;\n const efficiency = budget > 0 ? 1 - (strategy.tokensUsed / budget) : 0;\n\n return coverageWeight * 0.5 + criticalWeight * 0.3 + Math.max(0, efficiency) * 0.2;\n}\n\n/**\n * Render benchmark results as a formatted comparison table.\n */\nexport function renderBenchmark(result: BenchmarkResult): string {\n const lines: string[] = [];\n const { cto, naive, random } = result.strategies;\n\n lines.push('');\n lines.push(` ╔══════════════════════════════════════════════════════════════╗`);\n lines.push(` ║ ⚡ CTO Benchmark — ${result.project.substring(0, 20).padEnd(20)} ║`);\n lines.push(` ╠══════════════════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${pad('Metric', 20)} ${pad('CTO', 12)} ${pad('Naive', 12)} ${pad('Random', 12)} ║`);\n lines.push(` ║ ${'─'.repeat(56)} ║`);\n lines.push(` ║ ${pad('Files selected', 20)} ${pad(cto.filesSelected.toString(), 12)} ${pad(naive.filesSelected.toString(), 12)} ${pad(random.filesSelected.toString(), 12)} ║`);\n lines.push(` ║ ${pad('Tokens used', 20)} ${pad(fmt(cto.tokensUsed), 12)} ${pad(fmt(naive.tokensUsed), 12)} ${pad(fmt(random.tokensUsed), 12)} ║`);\n lines.push(` ║ ${pad('Coverage', 20)} ${pad(cto.coverageScore + '%', 12)} ${pad(naive.coverageScore + '%', 12)} ${pad(random.coverageScore + '%', 12)} ║`);\n lines.push(` ║ ${pad('Critical files', 20)} ${pad(`${cto.criticalFilesCovered}/${cto.criticalFilesTotal}`, 12)} ${pad(`${naive.criticalFilesCovered}/${naive.criticalFilesTotal}`, 12)} ${pad(`${random.criticalFilesCovered}/${random.criticalFilesTotal}`, 12)} ║`);\n lines.push(` ║ ${pad('High-risk files', 20)} ${pad(`${cto.highRiskCovered}/${cto.highRiskTotal}`, 12)} ${pad(`${naive.highRiskCovered}/${naive.highRiskTotal}`, 12)} ${pad(`${random.highRiskCovered}/${random.highRiskTotal}`, 12)} ║`);\n lines.push(` ║ ${pad('Cost/interaction', 20)} ${pad('$' + cto.costPerInteractionUSD.toFixed(4), 12)} ${pad('$' + naive.costPerInteractionUSD.toFixed(4), 12)} ${pad('$' + random.costPerInteractionUSD.toFixed(4), 12)} ║`);\n lines.push(` ║ ${pad('Time', 20)} ${pad(cto.timeMs + 'ms', 12)} ${pad(naive.timeMs + 'ms', 12)} ${pad(random.timeMs + 'ms', 12)} ║`);\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n lines.push(` ║ 🏆 Winner: ${result.winner.toUpperCase().padEnd(48)} ║`);\n\n if (result.ctoAdvantage.vsNaiveTokensSavedPercent > 0) {\n lines.push(` ║ 📉 ${result.ctoAdvantage.vsNaiveTokensSavedPercent}% fewer tokens than naive ║`);\n }\n if (result.ctoAdvantage.vsRandomCoverageGain > 0) {\n lines.push(` ║ 📈 +${result.ctoAdvantage.vsRandomCoverageGain}% better coverage than random ║`);\n }\n if (result.ctoAdvantage.vsNaiveCostSavedMonthlyUSD > 0) {\n lines.push(` ║ 💰 $${result.ctoAdvantage.vsNaiveCostSavedMonthlyUSD.toFixed(2)}/mo saved vs naive (800 interactions) ║`);\n }\n\n lines.push(` ║ ║`);\n lines.push(` ╚══════════════════════════════════════════════════════════════╝`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction pad(s: string, w: number): string { return s.padEnd(w).substring(0, w); }\nfunction fmt(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n"],"mappings":"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","names":["resolve","join","readFile","readFile","stat","join","relative","resolve","basename","join","stat","readFile","relative","basename","resolve","createHash","readdir","stat","join","extname","relative","resolve","readdir","join","extname","stat","relative","createHash","resolve","createHash","readFile","resolve","relative","Project","SyntaxKind","readFile","existsSync","join","TS_EXTENSIONS","readFile","Project","SyntaxKind","join","existsSync","createHash","readFile","join","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../src/api/dashboard.ts","../../src/engine/analyzer.ts","../../src/types/engine.ts","../../src/types/config.ts","../../src/engine/tokenizer.ts","../../src/engine/graph.ts","../../src/engine/risk.ts","../../src/engine/cache.ts","../../src/engine/selector.ts","../../src/govern/secrets.ts","../../src/engine/pruner.ts","../../src/engine/graph-utils.ts","../../src/engine/coverage.ts","../../src/engine/budget.ts","../../src/engine/score.ts","../../src/engine/benchmark.ts"],"sourcesContent":["import { resolve, join } from 'node:path';\nimport { writeFile, mkdir, readFile } from 'node:fs/promises';\nimport { analyzeProject } from '../engine/analyzer.js';\nimport { getCachedAnalysis } from '../engine/cache.js';\nimport { computeContextScore } from '../engine/score.js';\nimport { runBenchmark } from '../engine/benchmark.js';\nimport type { ContextScore } from '../engine/score.js';\nimport type { BenchmarkResult } from '../engine/benchmark.js';\n\n// ===== CTO Dashboard =====\n//\n// Generates a self-contained HTML dashboard with:\n// 1. Context Score™ gauge\n// 2. Dimension breakdown (radar chart via CSS)\n// 3. Benchmark comparison table\n// 4. Score history (stored in .cto/history.json)\n// 5. Top risk files\n// 6. Insights & recommendations\n// 7. Shareable badge embed code\n//\n// Output: .cto/dashboard.html (opens in any browser)\n\nexport interface DashboardData {\n score: ContextScore;\n benchmark: BenchmarkResult;\n history: ScoreHistoryEntry[];\n generatedAt: Date;\n}\n\nexport interface ScoreHistoryEntry {\n date: string;\n overall: number;\n grade: string;\n files: number;\n tokens: number;\n savedPercent: number;\n}\n\n// ===== HISTORY =====\n\nasync function loadHistory(ctoDir: string): Promise<ScoreHistoryEntry[]> {\n try {\n const raw = await readFile(join(ctoDir, 'history.json'), 'utf-8');\n return JSON.parse(raw);\n } catch {\n return [];\n }\n}\n\nasync function saveHistory(ctoDir: string, history: ScoreHistoryEntry[]): Promise<void> {\n await mkdir(ctoDir, { recursive: true });\n await writeFile(join(ctoDir, 'history.json'), JSON.stringify(history, null, 2));\n}\n\n// ===== MAIN API =====\n\nexport async function generateDashboard(\n projectPath: string,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<{ htmlPath: string; data: DashboardData }> {\n const absPath = resolve(projectPath);\n const ctoDir = join(absPath, '.cto');\n\n const analysis = await getCachedAnalysis(absPath);\n const score = await computeContextScore(analysis, task, budget);\n const benchmark = await runBenchmark(analysis, task, budget);\n\n // Load + update history\n const history = await loadHistory(ctoDir);\n const today = new Date().toISOString().split('T')[0];\n\n // Replace today's entry if exists, otherwise append\n const existingIdx = history.findIndex((h) => h.date === today);\n const entry: ScoreHistoryEntry = {\n date: today,\n overall: score.overall,\n grade: score.grade,\n files: score.meta.totalFiles,\n tokens: score.meta.totalTokens,\n savedPercent: score.comparison.savedPercent,\n };\n\n if (existingIdx >= 0) {\n history[existingIdx] = entry;\n } else {\n history.push(entry);\n }\n\n // Keep last 90 days\n const cutoff = history.length > 90 ? history.length - 90 : 0;\n const trimmedHistory = history.slice(cutoff);\n await saveHistory(ctoDir, trimmedHistory);\n\n const data: DashboardData = {\n score,\n benchmark,\n history: trimmedHistory,\n generatedAt: new Date(),\n };\n\n const html = renderDashboardHTML(data);\n const htmlPath = join(ctoDir, 'dashboard.html');\n await mkdir(ctoDir, { recursive: true });\n await writeFile(htmlPath, html, 'utf-8');\n\n return { htmlPath, data };\n}\n\n// ===== BADGE =====\n\nexport function generateBadgeURL(score: ContextScore): string {\n const color = score.grade.startsWith('A') ? 'brightgreen'\n : score.grade.startsWith('B') ? 'blue'\n : score.grade.startsWith('C') ? 'yellow'\n : 'red';\n return `https://img.shields.io/badge/CTO_Score-${score.overall}%2F100_${encodeURIComponent(score.grade)}-${color}`;\n}\n\nexport function generateBadgeMarkdown(score: ContextScore): string {\n const url = generateBadgeURL(score);\n return `[](https://github.com/cto-ai/cto-ai-cli)`;\n}\n\n// ===== HTML RENDERER =====\n\nfunction renderDashboardHTML(data: DashboardData): string {\n const { score, benchmark, history } = data;\n const s = score;\n const b = benchmark;\n\n const gradeColor = s.grade.startsWith('A') ? '#22c55e'\n : s.grade.startsWith('B') ? '#3b82f6'\n : s.grade.startsWith('C') ? '#eab308'\n : '#ef4444';\n\n const dims = [\n { name: 'Efficiency', score: s.dimensions.efficiency.score, detail: s.dimensions.efficiency.detail },\n { name: 'Coverage', score: s.dimensions.coverage.score, detail: s.dimensions.coverage.detail },\n { name: 'Risk Control', score: s.dimensions.riskControl.score, detail: s.dimensions.riskControl.detail },\n { name: 'Structure', score: s.dimensions.structure.score, detail: s.dimensions.structure.detail },\n { name: 'Governance', score: s.dimensions.governance.score, detail: s.dimensions.governance.detail },\n ];\n\n const historyPoints = history.map((h) => `{date:'${h.date}',score:${h.overall}}`).join(',');\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>CTO Dashboard — ${s.meta.projectName}</title>\n<style>\n*{margin:0;padding:0;box-sizing:border-box}\nbody{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#0f172a;color:#e2e8f0;min-height:100vh}\n.container{max-width:1200px;margin:0 auto;padding:24px}\nh1{font-size:28px;font-weight:700;margin-bottom:4px}\nh2{font-size:20px;font-weight:600;margin-bottom:16px;color:#94a3b8}\nh3{font-size:16px;font-weight:600;margin-bottom:12px;color:#cbd5e1}\n.subtitle{color:#64748b;font-size:14px;margin-bottom:32px}\n.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(350px,1fr));gap:24px;margin-bottom:24px}\n.card{background:#1e293b;border-radius:12px;padding:24px;border:1px solid #334155}\n.score-hero{text-align:center;padding:40px 24px}\n.score-number{font-size:72px;font-weight:800;color:${gradeColor}}\n.score-grade{font-size:36px;font-weight:700;color:${gradeColor};margin-left:8px}\n.score-label{font-size:14px;color:#64748b;margin-top:4px}\n.dim-bar{display:flex;align-items:center;margin-bottom:12px}\n.dim-label{width:120px;font-size:13px;color:#94a3b8}\n.dim-track{flex:1;height:8px;background:#334155;border-radius:4px;overflow:hidden}\n.dim-fill{height:100%;border-radius:4px;transition:width .5s}\n.dim-value{width:50px;text-align:right;font-size:13px;font-weight:600}\n.table{width:100%;border-collapse:collapse;font-size:13px}\n.table th{text-align:left;padding:8px 12px;border-bottom:2px solid #334155;color:#64748b;font-weight:600}\n.table td{padding:8px 12px;border-bottom:1px solid #1e293b}\n.table tr:hover td{background:#1e293b}\n.badge{display:inline-block;padding:4px 10px;border-radius:12px;font-size:12px;font-weight:600}\n.badge-cto{background:#3b82f620;color:#60a5fa}\n.badge-naive{background:#64748b20;color:#94a3b8}\n.badge-random{background:#64748b20;color:#94a3b8}\n.insight{display:flex;gap:8px;margin-bottom:8px;font-size:13px}\n.insight-icon{width:20px;text-align:center}\n.stat{text-align:center}\n.stat-value{font-size:28px;font-weight:700}\n.stat-label{font-size:12px;color:#64748b;margin-top:2px}\n.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:24px}\n.history-chart{width:100%;height:120px;display:flex;align-items:flex-end;gap:2px;padding:8px 0}\n.history-bar{flex:1;background:#3b82f6;border-radius:2px 2px 0 0;min-width:3px;transition:height .3s}\n.history-bar:hover{background:#60a5fa}\n.badge-embed{background:#0f172a;border:1px solid #334155;border-radius:8px;padding:12px;font-family:monospace;font-size:12px;color:#94a3b8;word-break:break-all;cursor:pointer}\n.badge-embed:hover{border-color:#60a5fa}\nfooter{text-align:center;padding:24px;color:#475569;font-size:12px}\n</style>\n</head>\n<body>\n<div class=\"container\">\n<h1>⚡ CTO Dashboard</h1>\n<p class=\"subtitle\">${s.meta.projectName} · ${s.meta.totalFiles} files · ${Math.round(s.meta.totalTokens / 1000)}K tokens · Generated ${data.generatedAt.toISOString().split('T')[0]}</p>\n\n<div class=\"stats-row\">\n<div class=\"card stat\"><div class=\"stat-value\" style=\"color:${gradeColor}\">${s.overall}</div><div class=\"stat-label\">Context Score™</div></div>\n<div class=\"card stat\"><div class=\"stat-value\" style=\"color:${gradeColor}\">${s.grade}</div><div class=\"stat-label\">Grade</div></div>\n<div class=\"card stat\"><div class=\"stat-value\">${s.comparison.savedPercent}%</div><div class=\"stat-label\">Tokens Saved</div></div>\n<div class=\"card stat\"><div class=\"stat-value\">$${s.comparison.monthlySavingsUSD.toFixed(0)}</div><div class=\"stat-label\">Monthly Savings</div></div>\n</div>\n\n<div class=\"grid\">\n\n<div class=\"card\">\n<h3>Dimensions</h3>\n${dims.map((d) => `\n<div class=\"dim-bar\">\n<span class=\"dim-label\">${d.name}</span>\n<div class=\"dim-track\"><div class=\"dim-fill\" style=\"width:${d.score}%;background:${d.score >= 80 ? '#22c55e' : d.score >= 60 ? '#3b82f6' : d.score >= 40 ? '#eab308' : '#ef4444'}\"></div></div>\n<span class=\"dim-value\">${d.score}%</span>\n</div>`).join('')}\n<div style=\"margin-top:16px;font-size:11px;color:#475569\">\n${dims.map((d) => `${d.name}: ${d.detail}`).join('<br>')}\n</div>\n</div>\n\n<div class=\"card\">\n<h3>⚡ Benchmark: CTO vs Naive vs Random</h3>\n<table class=\"table\">\n<tr><th>Metric</th><th><span class=\"badge badge-cto\">CTO</span></th><th><span class=\"badge badge-naive\">Naive</span></th><th><span class=\"badge badge-random\">Random</span></th></tr>\n<tr><td>Files</td><td>${b.strategies.cto.filesSelected}</td><td>${b.strategies.naive.filesSelected}</td><td>${b.strategies.random.filesSelected}</td></tr>\n<tr><td>Tokens</td><td>${fmt(b.strategies.cto.tokensUsed)}</td><td>${fmt(b.strategies.naive.tokensUsed)}</td><td>${fmt(b.strategies.random.tokensUsed)}</td></tr>\n<tr><td>Coverage</td><td><b>${b.strategies.cto.coverageScore}%</b></td><td>${b.strategies.naive.coverageScore}%</td><td>${b.strategies.random.coverageScore}%</td></tr>\n<tr><td>High-Risk</td><td><b>${b.strategies.cto.highRiskCovered}/${b.strategies.cto.highRiskTotal}</b></td><td>${b.strategies.naive.highRiskCovered}/${b.strategies.naive.highRiskTotal}</td><td>${b.strategies.random.highRiskCovered}/${b.strategies.random.highRiskTotal}</td></tr>\n<tr><td>Cost/call</td><td>$${b.strategies.cto.costPerInteractionUSD.toFixed(4)}</td><td>$${b.strategies.naive.costPerInteractionUSD.toFixed(4)}</td><td>$${b.strategies.random.costPerInteractionUSD.toFixed(4)}</td></tr>\n</table>\n</div>\n\n<div class=\"card\">\n<h3>💡 Insights</h3>\n${s.insights.slice(0, 8).map((i) => {\n const icon = i.type === 'strength' ? '✅' : i.type === 'weakness' ? '⚠️' : '💡';\n return `<div class=\"insight\"><span class=\"insight-icon\">${icon}</span><span><b>${i.title}</b> — ${i.detail}</span></div>`;\n}).join('')}\n</div>\n\n<div class=\"card\">\n<h3>📈 Score History</h3>\n${history.length > 1 ? `\n<div class=\"history-chart\">\n${history.map((h) => `<div class=\"history-bar\" style=\"height:${h.overall}%\" title=\"${h.date}: ${h.overall}/100 (${h.grade})\"></div>`).join('')}\n</div>\n<div style=\"display:flex;justify-content:space-between;font-size:11px;color:#475569\">\n<span>${history[0]?.date}</span>\n<span>${history[history.length - 1]?.date}</span>\n</div>\n` : '<p style=\"color:#64748b;font-size:13px\">Run <code>cto2 dashboard</code> daily to build score history.</p>'}\n</div>\n\n</div>\n\n<div class=\"card\" style=\"margin-bottom:24px\">\n<h3>🏷️ Badge</h3>\n<p style=\"margin-bottom:12px\"><img src=\"${generateBadgeURL(s)}\" alt=\"CTO Score\"></p>\n<p style=\"font-size:12px;color:#64748b;margin-bottom:8px\">Add this to your README:</p>\n<div class=\"badge-embed\" onclick=\"navigator.clipboard.writeText(this.textContent)\">${generateBadgeMarkdown(s)}</div>\n</div>\n\n<footer>\nCTO Context Score™ · Generated by <a href=\"https://github.com/cto-ai/cto-ai-cli\" style=\"color:#60a5fa\">cto-ai-cli</a>\n</footer>\n\n</div>\n</body>\n</html>`;\n}\n\nfunction fmt(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n","import { readFile, readdir, stat } from 'node:fs/promises';\nimport { join, extname, relative, resolve, basename } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n FileKind,\n ProjectAnalysis,\n WalkEntry,\n WalkOptions,\n} from '../types/engine.js';\nimport { DEFAULT_RISK_WEIGHTS } from '../types/engine.js';\nimport type { CTOConfig } from '../types/config.js';\nimport { DEFAULT_CONFIG } from '../types/config.js';\nimport { estimateTokens, countTokensChars4 } from './tokenizer.js';\nimport { buildProjectGraph } from './graph.js';\nimport { scoreAllFiles } from './risk.js';\n\n// ===== FILE WALKING =====\n\nfunction matchesPattern(filename: string, patterns: string[]): boolean {\n for (const pattern of patterns) {\n if (pattern.startsWith('*.')) {\n const ext = pattern.slice(1);\n if (filename.endsWith(ext)) return true;\n } else if (filename === pattern) {\n return true;\n }\n }\n return false;\n}\n\nexport async function walkProject(\n rootPath: string,\n options: WalkOptions,\n): Promise<WalkEntry[]> {\n const results: WalkEntry[] = [];\n const { ignoreDirs, ignorePatterns, extensions, maxDepth = 20 } = options;\n const ignoreDirSet = new Set(ignoreDirs);\n\n async function walk(dir: string, depth: number): Promise<void> {\n if (depth > maxDepth) return;\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n const promises: Promise<void>[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!ignoreDirSet.has(entry.name) && !entry.name.startsWith('.')) {\n promises.push(walk(fullPath, depth + 1));\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name).slice(1).toLowerCase();\n if (ext && extensions.includes(ext) && !matchesPattern(entry.name, ignorePatterns)) {\n promises.push(\n (async () => {\n const fileStat = await stat(fullPath).catch(() => null);\n if (!fileStat) return;\n\n let lines = 0;\n try {\n const content = await readFile(fullPath, 'utf-8');\n lines = content.split('\\n').length;\n } catch {\n lines = 0;\n }\n\n results.push({\n path: fullPath,\n relativePath: relative(rootPath, fullPath),\n extension: ext,\n size: fileStat.size,\n lastModified: fileStat.mtime,\n lines,\n });\n })(),\n );\n }\n }\n }\n\n await Promise.all(promises);\n }\n\n await walk(rootPath, 0);\n return results;\n}\n\n// ===== FILE KIND DETECTION =====\n\nconst TYPE_PATTERNS = [/types?\\//i, /\\.d\\.ts$/, /interfaces?\\//i];\nconst TEST_PATTERNS = [/\\.test\\.[jt]sx?$/, /\\.spec\\.[jt]sx?$/, /\\/__tests__\\//, /\\/tests?\\//];\nconst CONFIG_PATTERNS = [/\\.config\\.[jt]s$/, /rc\\.[jt]s$/, /\\.env/, /tsconfig/, /package\\.json$/, /\\.yml$/, /\\.yaml$/, /\\.toml$/];\nconst ENTRY_PATTERNS = [/^index\\.[jt]sx?$/, /^main\\.[jt]sx?$/, /^app\\.[jt]sx?$/, /^server\\.[jt]sx?$/];\n\nexport function classifyFileKind(relativePath: string): FileKind {\n const filename = basename(relativePath);\n\n if (TYPE_PATTERNS.some((p) => p.test(relativePath))) return 'type';\n if (TEST_PATTERNS.some((p) => p.test(relativePath))) return 'test';\n if (CONFIG_PATTERNS.some((p) => p.test(relativePath) || p.test(filename))) return 'config';\n if (ENTRY_PATTERNS.some((p) => p.test(filename))) return 'entry';\n return 'source';\n}\n\n// ===== STACK DETECTION =====\n\nexport function detectStack(files: WalkEntry[]): string[] {\n const stack: string[] = [];\n const extensions = new Set(files.map((f) => f.extension));\n const paths = files.map((f) => f.relativePath.toLowerCase());\n\n if (extensions.has('ts') || extensions.has('tsx')) stack.push('TypeScript');\n else if (extensions.has('js') || extensions.has('jsx')) stack.push('JavaScript');\n if (extensions.has('py')) stack.push('Python');\n if (extensions.has('go')) stack.push('Go');\n if (extensions.has('rs')) stack.push('Rust');\n if (extensions.has('java')) stack.push('Java');\n if (extensions.has('kt')) stack.push('Kotlin');\n if (extensions.has('rb')) stack.push('Ruby');\n if (extensions.has('php')) stack.push('PHP');\n if (extensions.has('cs')) stack.push('C#');\n if (extensions.has('c') || extensions.has('cpp')) stack.push('C/C++');\n\n if (paths.some((p) => p.includes('next.config'))) stack.push('Next.js');\n if (paths.some((p) => p.includes('nuxt.config'))) stack.push('Nuxt');\n if (paths.some((p) => p.includes('angular.json'))) stack.push('Angular');\n\n return stack;\n}\n\n// ===== MAIN ANALYSIS PIPELINE =====\n\nexport async function analyzeProject(\n projectPath: string,\n config?: Partial<CTOConfig>,\n): Promise<ProjectAnalysis> {\n const absPath = resolve(projectPath);\n const projectName = basename(absPath);\n const mergedConfig = mergeConfig(DEFAULT_CONFIG, config);\n\n // 1. Walk project files\n const allExtensions = [\n ...mergedConfig.analysis.extensions.code,\n ...mergedConfig.analysis.extensions.config,\n ...mergedConfig.analysis.extensions.docs,\n ];\n\n const walkEntries = await walkProject(absPath, {\n ignoreDirs: mergedConfig.analysis.ignore.dirs,\n ignorePatterns: mergedConfig.analysis.ignore.patterns,\n extensions: allExtensions,\n maxDepth: mergedConfig.analysis.maxDepth,\n });\n\n // 2. Estimate tokens and build initial AnalyzedFile[]\n const tokenMethod = mergedConfig.tokens.method;\n const files: AnalyzedFile[] = [];\n\n for (const entry of walkEntries) {\n let tokens: number;\n if (tokenMethod === 'tiktoken') {\n try {\n const content = await readFile(entry.path, 'utf-8');\n tokens = estimateTokens(content, entry.size, 'tiktoken');\n } catch {\n tokens = countTokensChars4(entry.size);\n }\n } else {\n tokens = countTokensChars4(entry.size);\n }\n\n files.push({\n path: entry.path,\n relativePath: entry.relativePath,\n extension: entry.extension,\n size: entry.size,\n tokens,\n lines: entry.lines,\n lastModified: entry.lastModified,\n kind: classifyFileKind(entry.relativePath),\n\n // Graph data — populated by graph analysis\n imports: [],\n importedBy: [],\n isHub: false,\n complexity: 0,\n\n // Risk data — populated by risk analysis\n riskScore: 0,\n riskFactors: [],\n exclusionImpact: 'none',\n });\n }\n\n // 3. Build dependency graph (AST-based, best-effort)\n const graph = buildProjectGraph(absPath, files);\n\n // 4. Enrich files with graph data\n for (const file of files) {\n const nodeImports: string[] = [];\n const nodeImportedBy: string[] = [];\n\n for (const edge of graph.edges) {\n if (edge.from === file.relativePath) nodeImports.push(edge.to);\n if (edge.to === file.relativePath) nodeImportedBy.push(edge.from);\n }\n\n file.imports = nodeImports;\n file.importedBy = nodeImportedBy;\n file.isHub = graph.hubs.some((h) => h.relativePath === file.relativePath);\n }\n\n // 5. Score risk for all files\n const riskWeights = mergedConfig.risk.weights;\n scoreAllFiles(files, graph, riskWeights);\n\n // 6. Build risk profile\n const riskProfile = {\n distribution: {\n critical: files.filter((f) => f.riskScore >= 80).length,\n high: files.filter((f) => f.riskScore >= 60 && f.riskScore < 80).length,\n medium: files.filter((f) => f.riskScore >= 30 && f.riskScore < 60).length,\n low: files.filter((f) => f.riskScore < 30).length,\n },\n topRiskFiles: [...files].sort((a, b) => b.riskScore - a.riskScore).slice(0, 10),\n overallComplexity: files.length > 0\n ? files.reduce((s, f) => s + f.complexity, 0) / files.length\n : 0,\n };\n\n // 7. Build analysis hash for determinism\n const totalTokens = files.reduce((s, f) => s + f.tokens, 0);\n const hashInput = files\n .map((f) => `${f.relativePath}:${f.tokens}:${f.riskScore}`)\n .sort()\n .join('|');\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n const stack = detectStack(walkEntries);\n\n return {\n projectPath: absPath,\n projectName,\n analyzedAt: new Date(),\n hash,\n files,\n totalFiles: files.length,\n totalTokens,\n graph,\n riskProfile,\n stack,\n tokenMethod,\n };\n}\n\n// ===== CONFIG MERGE =====\n\nfunction mergeConfig(base: CTOConfig, overrides?: Partial<CTOConfig>): CTOConfig {\n if (!overrides) return base;\n\n return {\n ...base,\n ...overrides,\n analysis: {\n ...base.analysis,\n ...overrides.analysis,\n extensions: {\n ...base.analysis.extensions,\n ...overrides.analysis?.extensions,\n },\n ignore: {\n ...base.analysis.ignore,\n ...overrides.analysis?.ignore,\n },\n },\n risk: {\n ...base.risk,\n ...overrides.risk,\n weights: {\n ...base.risk.weights,\n ...overrides.risk?.weights,\n },\n },\n interaction: {\n ...base.interaction,\n ...overrides.interaction,\n },\n tokens: {\n ...base.tokens,\n ...overrides.tokens,\n },\n governance: {\n ...base.governance,\n ...overrides.governance,\n },\n };\n}\n","// ===== Layer 1: Context Intelligence Engine Types =====\n\n// ===== FILE ANALYSIS =====\n\nexport interface AnalyzedFile {\n path: string;\n relativePath: string;\n extension: string;\n size: number;\n tokens: number;\n lines: number;\n lastModified: Date;\n kind: FileKind;\n\n // Graph data (populated after graph analysis)\n imports: string[];\n importedBy: string[];\n isHub: boolean;\n complexity: number;\n\n // Risk data (populated after risk analysis)\n riskScore: number;\n riskFactors: RiskFactor[];\n exclusionImpact: ExclusionImpact;\n}\n\nexport type FileKind = 'source' | 'type' | 'test' | 'config' | 'entry' | 'asset';\nexport type ExclusionImpact = 'critical' | 'high' | 'medium' | 'low' | 'none';\n\n// ===== PROJECT ANALYSIS =====\n\nexport interface ProjectAnalysis {\n projectPath: string;\n projectName: string;\n analyzedAt: Date;\n hash: string;\n\n files: AnalyzedFile[];\n totalFiles: number;\n totalTokens: number;\n\n graph: ProjectGraph;\n riskProfile: RiskProfile;\n stack: string[];\n tokenMethod: 'chars4' | 'tiktoken';\n}\n\n// ===== DEPENDENCY GRAPH =====\n\nexport interface ProjectGraph {\n nodes: string[];\n edges: GraphEdge[];\n hubs: HubNode[];\n leaves: string[];\n orphans: string[];\n clusters: FileCluster[];\n}\n\nexport interface GraphEdge {\n from: string;\n to: string;\n type: 'import' | 'export' | 're-export';\n}\n\nexport interface HubNode {\n relativePath: string;\n dependents: number;\n dependencies: number;\n score: number;\n}\n\nexport interface FileCluster {\n id: string;\n name: string;\n files: string[];\n totalTokens: number;\n internalEdges: number;\n externalEdges: number;\n cohesion: number;\n}\n\n// ===== RISK MODEL =====\n\nexport interface RiskProfile {\n distribution: {\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n topRiskFiles: AnalyzedFile[];\n overallComplexity: number;\n}\n\nexport interface RiskFactor {\n type: RiskFactorType;\n score: number;\n weight: number;\n detail: string;\n}\n\nexport type RiskFactorType =\n | 'hub'\n | 'type-provider'\n | 'complexity'\n | 'recency'\n | 'config'\n | 'churn';\n\nexport interface RiskWeights {\n hub: number;\n typeProvider: number;\n complexity: number;\n recency: number;\n config: number;\n churn: number;\n}\n\nexport const DEFAULT_RISK_WEIGHTS: RiskWeights = {\n hub: 30,\n typeProvider: 25,\n complexity: 15,\n recency: 15,\n config: 10,\n churn: 5,\n};\n\n// ===== CONTEXT COVERAGE =====\n\nexport interface CoverageResult {\n score: number;\n relevantFiles: string[];\n includedRelevant: string[];\n missingRelevant: string[];\n missingCritical: string[];\n explanation: string;\n}\n\n// ===== CONTEXT SELECTION =====\n\nexport interface ContextSelection {\n files: SelectedFile[];\n totalTokens: number;\n budget: number;\n usedPercent: number;\n\n coverage: CoverageResult;\n riskScore: number;\n deterministic: boolean;\n hash: string;\n\n decisions: SelectionDecision[];\n}\n\nexport interface SelectedFile {\n relativePath: string;\n tokens: number;\n originalTokens: number;\n pruneLevel: PruneLevel;\n riskScore: number;\n reason: string;\n}\n\nexport type PruneLevel = 'full' | 'signatures' | 'skeleton' | 'excluded';\n\nexport interface SelectionDecision {\n file: string;\n action: 'include-full' | 'include-signatures' | 'include-skeleton' | 'exclude';\n reason: string;\n alternatives?: string;\n}\n\n// ===== BUDGET =====\n\nexport interface BudgetPlan {\n budget: number;\n used: number;\n remaining: number;\n fillPercent: number;\n files: BudgetEntry[];\n}\n\nexport interface BudgetEntry {\n relativePath: string;\n originalTokens: number;\n allocatedTokens: number;\n pruneLevel: PruneLevel;\n included: boolean;\n reason: string;\n}\n\n// ===== PRUNING =====\n\nexport interface PrunedContent {\n relativePath: string;\n originalTokens: number;\n prunedTokens: number;\n pruneLevel: PruneLevel;\n content: string;\n savingsPercent: number;\n}\n\n// ===== WALKER =====\n\nexport interface WalkEntry {\n path: string;\n relativePath: string;\n extension: string;\n size: number;\n lastModified: Date;\n lines: number;\n}\n\nexport interface WalkOptions {\n ignoreDirs: string[];\n ignorePatterns: string[];\n extensions: string[];\n maxDepth?: number;\n}\n","// ===== Configuration Types =====\n\nimport type { RiskWeights } from './engine.js';\n\nexport interface CTOConfig {\n version: string;\n\n analysis: {\n extensions: {\n code: string[];\n config: string[];\n docs: string[];\n };\n ignore: {\n dirs: string[];\n patterns: string[];\n };\n maxDepth: number;\n };\n\n risk: {\n weights: RiskWeights;\n };\n\n interaction: {\n defaultBudget: number;\n defaultModel: string;\n };\n\n tokens: {\n method: 'chars4' | 'tiktoken';\n };\n\n governance: {\n auditEnabled: boolean;\n secretDetection: boolean;\n retentionDays: number;\n };\n}\n\nexport const DEFAULT_CONFIG: CTOConfig = {\n version: '2.0',\n\n analysis: {\n extensions: {\n code: ['ts', 'tsx', 'js', 'jsx', 'py', 'go', 'rs', 'java', 'kt', 'rb', 'php', 'c', 'cpp', 'h', 'hpp', 'cs'],\n config: ['json', 'yml', 'yaml', 'toml'],\n docs: ['md', 'txt', 'rst'],\n },\n ignore: {\n dirs: ['node_modules', 'dist', 'build', '.git', 'coverage', '__pycache__', '.next', 'vendor', '.cto'],\n patterns: ['*.min.js', '*.map', '*.lock', '*.generated.*'],\n },\n maxDepth: 20,\n },\n\n risk: {\n weights: {\n hub: 30,\n typeProvider: 25,\n complexity: 15,\n recency: 15,\n config: 10,\n churn: 5,\n },\n },\n\n interaction: {\n defaultBudget: 50_000,\n defaultModel: 'claude-sonnet-4',\n },\n\n tokens: {\n method: 'chars4',\n },\n\n governance: {\n auditEnabled: true,\n secretDetection: true,\n retentionDays: 90,\n },\n};\n","import { encodingForModel } from 'js-tiktoken';\nimport { readFile, stat } from 'node:fs/promises';\n\nconst CHARS_PER_TOKEN = 4;\n\nlet encoder: ReturnType<typeof encodingForModel> | null = null;\n\nfunction getEncoder() {\n if (!encoder) {\n encoder = encodingForModel('claude-3-5-sonnet-20241022' as Parameters<typeof encodingForModel>[0]);\n }\n return encoder;\n}\n\nexport function countTokensTiktoken(text: string): number {\n try {\n const enc = getEncoder();\n const tokens = enc.encode(text);\n return tokens.length;\n } catch {\n return Math.ceil(text.length / CHARS_PER_TOKEN);\n }\n}\n\nexport function countTokensChars4(sizeInBytes: number): number {\n return Math.ceil(sizeInBytes / CHARS_PER_TOKEN);\n}\n\nexport function estimateTokens(\n content: string,\n sizeInBytes: number,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): number {\n if (method === 'tiktoken') {\n return countTokensTiktoken(content);\n }\n return countTokensChars4(sizeInBytes);\n}\n\nexport async function estimateFileTokens(\n filePath: string,\n method: 'chars4' | 'tiktoken' = 'chars4',\n): Promise<number> {\n if (method === 'chars4') {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n\n try {\n const content = await readFile(filePath, 'utf-8');\n return countTokensTiktoken(content);\n } catch {\n const s = await stat(filePath);\n return countTokensChars4(s.size);\n }\n}\n\nexport function freeEncoder(): void {\n encoder = null;\n}\n","import { Project, SyntaxKind, type SourceFile, type Node } from 'ts-morph';\nimport { resolve, relative, dirname, join, basename } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport type {\n AnalyzedFile,\n ProjectGraph,\n GraphEdge,\n HubNode,\n FileCluster,\n} from '../types/engine.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs', 'cts', 'cjs']);\n\n// ===== PROJECT CREATION =====\n\nexport function createProject(projectPath: string, filePaths: string[]): Project {\n const tsConfigPath = join(projectPath, 'tsconfig.json');\n const hasTsConfig = existsSync(tsConfigPath);\n\n const project = new Project({\n tsConfigFilePath: hasTsConfig ? tsConfigPath : undefined,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: hasTsConfig\n ? undefined\n : {\n allowJs: true,\n jsx: 4 as any, // JsxEmit.ReactJSX\n esModuleInterop: true,\n moduleResolution: 100 as any, // Bundler\n },\n });\n\n const tsFiles = filePaths.filter((f) => {\n const ext = f.split('.').pop()?.toLowerCase() ?? '';\n return TS_EXTENSIONS.has(ext);\n });\n\n for (const filePath of tsFiles) {\n try {\n project.addSourceFileAtPath(filePath);\n } catch {\n // skip files that can't be parsed\n }\n }\n\n return project;\n}\n\n// ===== DEPENDENCY GRAPH =====\n\nexport function buildProjectGraph(\n projectPath: string,\n files: AnalyzedFile[],\n): ProjectGraph {\n const absPath = resolve(projectPath);\n\n const tsFiles = files\n .filter((f) => TS_EXTENSIONS.has(f.extension))\n .map((f) => f.path);\n\n if (tsFiles.length === 0) {\n return emptyGraph(files);\n }\n\n let project: Project;\n try {\n project = createProject(projectPath, tsFiles);\n } catch {\n return emptyGraph(files);\n }\n\n const edges: GraphEdge[] = [];\n const nodeSet = new Set<string>();\n\n for (const sourceFile of project.getSourceFiles()) {\n const fromRel = relative(absPath, sourceFile.getFilePath());\n if (fromRel.startsWith('..') || fromRel.includes('node_modules')) continue;\n nodeSet.add(fromRel);\n\n // Imports\n for (const imp of sourceFile.getImportDeclarations()) {\n const moduleSpecifier = imp.getModuleSpecifierValue();\n const resolved = resolveImport(sourceFile, moduleSpecifier, absPath);\n if (resolved) {\n nodeSet.add(resolved);\n edges.push({ from: fromRel, to: resolved, type: 'import' });\n }\n }\n\n // Re-exports\n for (const exp of sourceFile.getExportDeclarations()) {\n const moduleSpecifier = exp.getModuleSpecifierValue();\n if (moduleSpecifier) {\n const resolved = resolveImport(sourceFile, moduleSpecifier, absPath);\n if (resolved) {\n nodeSet.add(resolved);\n edges.push({ from: fromRel, to: resolved, type: 're-export' });\n }\n }\n }\n }\n\n const nodes = Array.from(nodeSet);\n\n // Calculate import counts\n const importedByCount = new Map<string, number>();\n const importCount = new Map<string, number>();\n\n for (const edge of edges) {\n importedByCount.set(edge.to, (importedByCount.get(edge.to) ?? 0) + 1);\n importCount.set(edge.from, (importCount.get(edge.from) ?? 0) + 1);\n }\n\n // Identify hubs using normalized in-degree centrality (Freeman, 1978).\n // centrality = in-degree / (N - 1), then combined with out-degree for a\n // \"betweenness-lite\" score: score = (in-degree × 2 + out-degree) / (N - 1 + 1)\n // This normalizes by graph size so hub detection is consistent across projects.\n const N = Math.max(nodes.length, 1);\n const hubs: HubNode[] = nodes\n .map((node) => {\n const inDeg = importedByCount.get(node) ?? 0;\n const outDeg = importCount.get(node) ?? 0;\n // Normalized centrality score (0-100 scale)\n const centrality = N > 1 ? (inDeg / (N - 1)) * 100 : 0;\n const score = Math.round(centrality + outDeg * (100 / (2 * N)));\n return {\n relativePath: node,\n dependents: inDeg,\n dependencies: outDeg,\n score: Math.min(100, score),\n };\n })\n .filter((h) => h.dependents >= 3 || h.score >= 15)\n .sort((a, b) => b.score - a.score);\n\n // Identify leaves (files that import but are not imported by anyone)\n const leaves = nodes.filter(\n (node) => (importedByCount.get(node) ?? 0) === 0 && (importCount.get(node) ?? 0) > 0,\n );\n\n // Identify orphans (files with no connections at all)\n const connectedNodes = new Set<string>();\n for (const edge of edges) {\n connectedNodes.add(edge.from);\n connectedNodes.add(edge.to);\n }\n const allFileNodes = new Set(files.map((f) => f.relativePath));\n const orphans = Array.from(allFileNodes).filter((n) => !connectedNodes.has(n));\n\n // Detect clusters using Union-Find for true connected components,\n // then refine with directory grouping for naming.\n const clusters = detectClusters(nodes, edges, files);\n\n // Enrich files with complexity (AST-based)\n enrichComplexity(project, absPath, files);\n\n return { nodes, edges, hubs, leaves, orphans, clusters };\n}\n\n// ===== CLUSTER DETECTION (Union-Find + directory naming) =====\n\nclass UnionFind {\n parent: Map<string, string>;\n rank: Map<string, number>;\n\n constructor(nodes: string[]) {\n this.parent = new Map();\n this.rank = new Map();\n for (const n of nodes) {\n this.parent.set(n, n);\n this.rank.set(n, 0);\n }\n }\n\n find(x: string): string {\n const p = this.parent.get(x);\n if (p === undefined) return x;\n if (p !== x) {\n this.parent.set(x, this.find(p)); // path compression\n }\n return this.parent.get(x)!;\n }\n\n union(a: string, b: string): void {\n const ra = this.find(a);\n const rb = this.find(b);\n if (ra === rb) return;\n const rankA = this.rank.get(ra) ?? 0;\n const rankB = this.rank.get(rb) ?? 0;\n if (rankA < rankB) {\n this.parent.set(ra, rb);\n } else if (rankA > rankB) {\n this.parent.set(rb, ra);\n } else {\n this.parent.set(rb, ra);\n this.rank.set(ra, rankA + 1);\n }\n }\n}\n\nfunction detectClusters(\n nodes: string[],\n edges: GraphEdge[],\n files: AnalyzedFile[],\n): FileCluster[] {\n // 1. Build connected components using Union-Find (O(E × α(V)))\n const uf = new UnionFind(nodes);\n for (const edge of edges) {\n uf.union(edge.from, edge.to);\n }\n\n // 2. Group by component root\n const components = new Map<string, string[]>();\n for (const node of nodes) {\n const root = uf.find(node);\n if (!components.has(root)) components.set(root, []);\n components.get(root)!.push(node);\n }\n\n // 3. For each component, derive a human-readable name from common directory prefix\n const tokenMap = new Map(files.map((f) => [f.relativePath, f.tokens]));\n const clusters: FileCluster[] = [];\n\n for (const [, groupFiles] of components) {\n if (groupFiles.length < 2) continue;\n\n // Find common directory prefix for naming\n const name = commonPrefix(groupFiles);\n const fileSet = new Set(groupFiles);\n let internalEdges = 0;\n let externalEdges = 0;\n\n for (const edge of edges) {\n const fromIn = fileSet.has(edge.from);\n const toIn = fileSet.has(edge.to);\n if (fromIn && toIn) internalEdges++;\n else if (fromIn || toIn) externalEdges++;\n }\n\n const totalEdges = internalEdges + externalEdges;\n const cohesion = totalEdges > 0 ? internalEdges / totalEdges : 0;\n const totalTokens = groupFiles.reduce((s, f) => s + (tokenMap.get(f) ?? 0), 0);\n\n clusters.push({\n id: name.replace(/[^a-zA-Z0-9]/g, '-') || `cluster-${clusters.length}`,\n name: name || `cluster-${clusters.length}`,\n files: groupFiles,\n totalTokens,\n internalEdges,\n externalEdges,\n cohesion: Math.round(cohesion * 100) / 100,\n });\n }\n\n return clusters.sort((a, b) => b.files.length - a.files.length);\n}\n\nfunction commonPrefix(paths: string[]): string {\n if (paths.length === 0) return '';\n const parts = paths.map((p) => p.split('/'));\n const prefix: string[] = [];\n for (let i = 0; i < parts[0].length - 1; i++) {\n const segment = parts[0][i];\n if (parts.every((p) => p[i] === segment)) {\n prefix.push(segment);\n } else break;\n }\n return prefix.join('/') || parts[0][0];\n}\n\n// ===== COMPLEXITY ANALYSIS =====\n\nfunction enrichComplexity(\n project: Project,\n absPath: string,\n files: AnalyzedFile[],\n): void {\n const fileMap = new Map(files.map((f) => [f.relativePath, f]));\n\n for (const sourceFile of project.getSourceFiles()) {\n const relPath = relative(absPath, sourceFile.getFilePath());\n if (relPath.startsWith('..') || relPath.includes('node_modules')) continue;\n\n const file = fileMap.get(relPath);\n if (!file) continue;\n\n let totalComplexity = 0;\n\n // Function declarations\n for (const func of sourceFile.getFunctions()) {\n totalComplexity += calculateCyclomaticComplexity(func);\n }\n\n // Class methods\n for (const cls of sourceFile.getClasses()) {\n for (const method of cls.getMethods()) {\n totalComplexity += calculateCyclomaticComplexity(method);\n }\n }\n\n // Arrow functions assigned to variables\n for (const varDecl of sourceFile.getVariableDeclarations()) {\n const init = varDecl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n totalComplexity += calculateCyclomaticComplexity(init);\n }\n }\n\n file.complexity = Math.max(1, totalComplexity);\n }\n}\n\nfunction calculateCyclomaticComplexity(node: Node): number {\n let complexity = 1;\n\n node.forEachDescendant((descendant) => {\n switch (descendant.getKind()) {\n case SyntaxKind.IfStatement:\n case SyntaxKind.ConditionalExpression:\n case SyntaxKind.ForStatement:\n case SyntaxKind.ForInStatement:\n case SyntaxKind.ForOfStatement:\n case SyntaxKind.WhileStatement:\n case SyntaxKind.DoStatement:\n case SyntaxKind.CaseClause:\n case SyntaxKind.CatchClause:\n complexity++;\n break;\n case SyntaxKind.BinaryExpression: {\n // FIX: Check the operator token kind directly instead of getText()\n // to avoid double-counting nested logical expressions.\n // getText() on `a && (b || c)` returns the full text including\n // the inner `||`, which would match both operators from the outer node.\n const opToken = (descendant as any).getOperatorToken?.();\n if (opToken) {\n const kind = opToken.getKind();\n if (\n kind === SyntaxKind.AmpersandAmpersandToken ||\n kind === SyntaxKind.BarBarToken ||\n kind === SyntaxKind.QuestionQuestionToken\n ) {\n complexity++;\n }\n }\n break;\n }\n }\n });\n\n return complexity;\n}\n\n// ===== IMPORT RESOLUTION =====\n\nfunction resolveImport(\n sourceFile: SourceFile,\n moduleSpecifier: string,\n projectRoot: string,\n): string | null {\n if (!moduleSpecifier.startsWith('.')) return null;\n\n const sourceDir = dirname(sourceFile.getFilePath());\n const basePath = resolve(sourceDir, moduleSpecifier);\n\n const extensions = ['.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx', '/index.js', '/index.jsx'];\n\n for (const ext of extensions) {\n const candidate = basePath.endsWith(ext) ? basePath : basePath + ext;\n if (existsSync(candidate)) {\n const rel = relative(projectRoot, candidate);\n if (!rel.startsWith('..')) return rel;\n }\n }\n\n // .js → .ts resolution\n if (moduleSpecifier.endsWith('.js')) {\n const tsPath = basePath.replace(/\\.js$/, '.ts');\n if (existsSync(tsPath)) {\n const rel = relative(projectRoot, tsPath);\n if (!rel.startsWith('..')) return rel;\n }\n }\n\n return null;\n}\n\n// ===== HELPERS =====\n\nfunction emptyGraph(files: AnalyzedFile[]): ProjectGraph {\n return {\n nodes: files.map((f) => f.relativePath),\n edges: [],\n hubs: [],\n leaves: [],\n orphans: files.map((f) => f.relativePath),\n clusters: [],\n };\n}\n","import type {\n AnalyzedFile,\n RiskFactor,\n RiskFactorType,\n RiskWeights,\n ExclusionImpact,\n ProjectGraph,\n} from '../types/engine.js';\nimport { DEFAULT_RISK_WEIGHTS } from '../types/engine.js';\n\n// ===== RISK SCORING ENGINE =====\n//\n// Each file gets a riskScore ∈ [0, 100] representing the risk of EXCLUDING it\n// from AI context. Higher score = more dangerous to exclude.\n//\n// riskScore(file) = Σ(factor.score × factor.weight) / Σ(factor.weight)\n\nexport function scoreAllFiles(\n files: AnalyzedFile[],\n graph: ProjectGraph,\n weights: RiskWeights = DEFAULT_RISK_WEIGHTS,\n): void {\n // Pre-compute type provider map: files that export types used by others\n const typeProviderUsage = computeTypeProviderUsage(files, graph);\n\n for (const file of files) {\n const factors = computeRiskFactors(file, graph, typeProviderUsage, weights);\n file.riskFactors = factors;\n file.riskScore = computeWeightedScore(factors);\n file.exclusionImpact = scoreToImpact(file.riskScore);\n }\n}\n\nexport function scoreFile(\n file: AnalyzedFile,\n graph: ProjectGraph,\n weights: RiskWeights = DEFAULT_RISK_WEIGHTS,\n): number {\n const typeProviderUsage = computeTypeProviderUsage([file], graph);\n const factors = computeRiskFactors(file, graph, typeProviderUsage, weights);\n file.riskFactors = factors;\n file.riskScore = computeWeightedScore(factors);\n file.exclusionImpact = scoreToImpact(file.riskScore);\n return file.riskScore;\n}\n\n// ===== FACTOR COMPUTATION =====\n\nfunction computeRiskFactors(\n file: AnalyzedFile,\n graph: ProjectGraph,\n typeProviderUsage: Map<string, number>,\n weights: RiskWeights,\n): RiskFactor[] {\n const factors: RiskFactor[] = [];\n\n // 1. Hub factor — how many files depend on this file\n factors.push(computeHubFactor(file, weights.hub));\n\n // 2. Type provider factor — exports types used by other files\n factors.push(computeTypeProviderFactor(file, typeProviderUsage, weights.typeProvider));\n\n // 3. Complexity factor — cyclomatic complexity\n factors.push(computeComplexityFactor(file, weights.complexity));\n\n // 4. Recency factor — how recently modified\n factors.push(computeRecencyFactor(file, weights.recency));\n\n // 5. Config factor — is it a config or entry point\n factors.push(computeConfigFactor(file, weights.config));\n\n // 6. Churn factor — based on complexity as proxy (git integration deferred)\n factors.push(computeChurnFactor(file, weights.churn));\n\n return factors;\n}\n\nfunction computeHubFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const dependents = file.importedBy.length;\n\n // Logarithmic scaling: score = min(100, 100 × log₂(1 + dependents) / log₂(1 + K))\n // K=12 means 12 dependents → score 100. Based on information theory:\n // each doubling of dependents adds equal marginal risk.\n const K = 12;\n const score = dependents === 0\n ? 0\n : Math.min(100, Math.round(100 * Math.log2(1 + dependents) / Math.log2(1 + K)));\n\n const detail = dependents === 0\n ? 'No dependents'\n : `Hub: ${dependents} file(s) depend on this (score ${score}/100)`;\n\n return { type: 'hub', score, weight, detail };\n}\n\nfunction computeTypeProviderFactor(\n file: AnalyzedFile,\n usage: Map<string, number>,\n weight: number,\n): RiskFactor {\n const isTypeFile = file.kind === 'type';\n const consumers = usage.get(file.relativePath) ?? 0;\n\n let score: number;\n let detail: string;\n\n if (isTypeFile && consumers >= 4) {\n score = 100;\n detail = `Type provider: used by ${consumers} files (critical type source)`;\n } else if (isTypeFile && consumers >= 1) {\n score = 50;\n detail = `Type provider: used by ${consumers} files`;\n } else if (isTypeFile) {\n score = 30;\n detail = 'Type file (no detected consumers)';\n } else {\n score = 0;\n detail = 'Not a type provider';\n }\n\n return { type: 'type-provider', score, weight, detail };\n}\n\nfunction computeComplexityFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const c = file.complexity;\n\n // Logarithmic scaling: score = min(100, 100 × ln(1 + c) / ln(1 + K))\n // K=30 means complexity 30 → score 100. ln provides diminishing returns\n // for very complex files (consistent with McCabe 1976 thresholds).\n const K = 30;\n const score = Math.min(100, Math.round(100 * Math.log(1 + c) / Math.log(1 + K)));\n\n const detail = c >= 30\n ? `Very high complexity: ${c} (AI needs full context)`\n : c >= 10\n ? `High complexity: ${c}`\n : `Complexity: ${c}`;\n\n return { type: 'complexity', score, weight, detail };\n}\n\nfunction computeRecencyFactor(file: AnalyzedFile, weight: number): RiskFactor {\n const now = Date.now();\n const modified = new Date(file.lastModified).getTime();\n const daysAgo = (now - modified) / (1000 * 60 * 60 * 24);\n\n // Exponential decay with half-life of 7 days.\n // score = 100 × 2^(-daysAgo / halfLife)\n // This is the standard temporal decay model used in information retrieval\n // (Lv & Zhai, 2011 — \"Adaptive Relevance Feedback\").\n const HALF_LIFE = 7;\n const score = Math.round(100 * Math.pow(2, -daysAgo / HALF_LIFE));\n\n const detail = daysAgo <= 1\n ? 'Modified today'\n : `Modified ${Math.round(daysAgo)} days ago (decay score ${score})`;\n\n return { type: 'recency', score, weight, detail };\n}\n\nfunction computeConfigFactor(file: AnalyzedFile, weight: number): RiskFactor {\n let score: number;\n let detail: string;\n\n if (file.kind === 'entry') {\n score = 90;\n detail = 'Entry point — critical for understanding app structure';\n } else if (file.kind === 'config') {\n score = 80;\n detail = 'Configuration file — affects runtime behavior';\n } else {\n score = 0;\n detail = 'Regular source file';\n }\n\n return { type: 'config', score, weight, detail };\n}\n\nfunction computeChurnFactor(file: AnalyzedFile, weight: number): RiskFactor {\n // PROXY: Without git integration, approximate churn as geometric mean of\n // complexity signal and recency signal. This avoids double-weighting\n // (since complexity and recency have their own factors) by using the\n // *interaction* term only — high churn requires BOTH high complexity AND\n // recent modification simultaneously.\n //\n // TODO: Replace with real git churn (commit count in last 30 days) when\n // git integration is added. Proxy weight is intentionally low (5%).\n const complexitySignal = Math.min(file.complexity / 20, 1);\n const now = Date.now();\n const daysAgo = (now - new Date(file.lastModified).getTime()) / (1000 * 60 * 60 * 24);\n const recencySignal = Math.pow(2, -daysAgo / 7); // same half-life as recency factor\n\n // Geometric mean ensures both signals must be high for a high score\n const score = Math.round(Math.sqrt(complexitySignal * recencySignal) * 100);\n const detail = score >= 50\n ? 'Likely under active development (complex + recent)'\n : score >= 20\n ? 'Some recent activity'\n : 'Stable — low churn (proxy estimate)';\n\n return { type: 'churn', score, weight, detail };\n}\n\n// ===== HELPERS =====\n\nfunction computeWeightedScore(factors: RiskFactor[]): number {\n let totalWeightedScore = 0;\n let totalWeight = 0;\n\n for (const factor of factors) {\n totalWeightedScore += factor.score * factor.weight;\n totalWeight += factor.weight;\n }\n\n if (totalWeight === 0) return 0;\n return Math.round(totalWeightedScore / totalWeight);\n}\n\nfunction scoreToImpact(score: number): ExclusionImpact {\n if (score >= 80) return 'critical';\n if (score >= 60) return 'high';\n if (score >= 30) return 'medium';\n if (score > 0) return 'low';\n return 'none';\n}\n\nfunction computeTypeProviderUsage(\n files: AnalyzedFile[],\n graph: ProjectGraph,\n): Map<string, number> {\n const usage = new Map<string, number>();\n\n // Count how many files import each type file\n const typeFiles = new Set(\n files.filter((f) => f.kind === 'type').map((f) => f.relativePath),\n );\n\n for (const edge of graph.edges) {\n if (typeFiles.has(edge.to)) {\n usage.set(edge.to, (usage.get(edge.to) ?? 0) + 1);\n }\n }\n\n return usage;\n}\n","import { createHash } from 'node:crypto';\nimport { readdir, stat } from 'node:fs/promises';\nimport { join, extname, relative, resolve } from 'node:path';\nimport type { ProjectAnalysis } from '../types/engine.js';\nimport type { CTOConfig } from '../types/config.js';\nimport { DEFAULT_CONFIG } from '../types/config.js';\nimport { analyzeProject } from './analyzer.js';\n\n// ===== Analysis Cache =====\n//\n// Problem: analyzeProject() is expensive (file walk + AST parse + token count + graph build).\n// MCP tools and CLI commands call it on every invocation.\n//\n// Solution: Two-level cache with lightweight fingerprint invalidation.\n// 1. Compute a fast fingerprint: hash of (sorted file paths + mtimes)\n// Cost: O(n) readdir + stat, no file reads, no AST, no token counting\n// 2. If fingerprint matches cached entry → return cached analysis (instant)\n// 3. If fingerprint differs → full re-analysis, update cache\n//\n// TTL: Safety net. Even if fingerprint matches, expire after maxAgeMs.\n//\n// Reference: Content-addressable caching (Git object model principle)\n\ninterface CacheEntry {\n analysis: ProjectAnalysis;\n fingerprint: string;\n createdAt: number;\n hits: number;\n}\n\ninterface CacheOptions {\n maxAgeMs: number;\n maxEntries: number;\n enabled: boolean;\n}\n\nconst DEFAULT_CACHE_OPTIONS: CacheOptions = {\n maxAgeMs: 5 * 60 * 1000, // 5 minutes\n maxEntries: 10,\n enabled: true,\n};\n\n// In-memory LRU-ish cache\nconst cache = new Map<string, CacheEntry>();\nlet cacheOptions: CacheOptions = { ...DEFAULT_CACHE_OPTIONS };\n\n// ===== FINGERPRINT =====\n\n// Fast fingerprint: walk directory, collect paths + mtimes, hash them.\n// ~10-100x faster than full analysis (no file reads, no AST, no tokenization).\nasync function computeFingerprint(\n rootPath: string,\n config: CTOConfig = DEFAULT_CONFIG,\n): Promise<string> {\n const entries: string[] = [];\n const allExtensions = new Set([\n ...config.analysis.extensions.code,\n ...config.analysis.extensions.config,\n ...config.analysis.extensions.docs,\n ]);\n const ignoreDirSet = new Set(config.analysis.ignore.dirs);\n\n async function walk(dir: string, depth: number): Promise<void> {\n if (depth > config.analysis.maxDepth) return;\n\n let dirEntries;\n try {\n dirEntries = await readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n const promises: Promise<void>[] = [];\n\n for (const entry of dirEntries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (!ignoreDirSet.has(entry.name) && !entry.name.startsWith('.')) {\n promises.push(walk(fullPath, depth + 1));\n }\n } else if (entry.isFile()) {\n const ext = extname(entry.name).slice(1).toLowerCase();\n if (ext && allExtensions.has(ext)) {\n promises.push(\n (async () => {\n try {\n const s = await stat(fullPath);\n const rel = relative(rootPath, fullPath);\n // Include path + mtime + size for change detection\n entries.push(`${rel}:${s.mtimeMs.toFixed(0)}:${s.size}`);\n } catch {\n // skip inaccessible files\n }\n })(),\n );\n }\n }\n }\n\n await Promise.all(promises);\n }\n\n await walk(rootPath, 0);\n\n // Sort for determinism\n entries.sort();\n return createHash('sha256').update(entries.join('|')).digest('hex').substring(0, 16);\n}\n\n// ===== PUBLIC API =====\n\n/**\n * Get a project analysis, using cache when possible.\n *\n * Cache hit: ~1-5ms (fingerprint check only)\n * Cache miss: full analyzeProject() + cache update\n */\nexport async function getCachedAnalysis(\n projectPath: string,\n config?: Partial<CTOConfig>,\n): Promise<ProjectAnalysis> {\n const absPath = resolve(projectPath);\n\n if (!cacheOptions.enabled) {\n return analyzeProject(absPath, config);\n }\n\n const existing = cache.get(absPath);\n\n // Check TTL first (cheap)\n if (existing) {\n const age = Date.now() - existing.createdAt;\n if (age > cacheOptions.maxAgeMs) {\n cache.delete(absPath);\n }\n }\n\n // Compute fingerprint\n const mergedConfig = config\n ? { ...DEFAULT_CONFIG, ...config } as CTOConfig\n : DEFAULT_CONFIG;\n const fingerprint = await computeFingerprint(absPath, mergedConfig);\n\n // Cache hit: fingerprint matches\n const cached = cache.get(absPath);\n if (cached && cached.fingerprint === fingerprint) {\n cached.hits++;\n return cached.analysis;\n }\n\n // Cache miss: full analysis\n const analysis = await analyzeProject(absPath, config);\n\n // Evict oldest entry if at capacity\n if (cache.size >= cacheOptions.maxEntries) {\n const oldest = [...cache.entries()].sort(\n (a, b) => a[1].createdAt - b[1].createdAt,\n )[0];\n if (oldest) cache.delete(oldest[0]);\n }\n\n cache.set(absPath, {\n analysis,\n fingerprint,\n createdAt: Date.now(),\n hits: 0,\n });\n\n return analysis;\n}\n\n/**\n * Invalidate cache for a specific project (e.g., after a known file change).\n */\nexport function invalidateCache(projectPath?: string): void {\n if (projectPath) {\n cache.delete(resolve(projectPath));\n } else {\n cache.clear();\n }\n}\n\n/**\n * Get cache statistics for debugging/monitoring.\n */\nexport function getCacheStats(): {\n entries: number;\n totalHits: number;\n projects: { path: string; hits: number; ageMs: number }[];\n} {\n const now = Date.now();\n const projects = [...cache.entries()].map(([path, entry]) => ({\n path,\n hits: entry.hits,\n ageMs: now - entry.createdAt,\n }));\n\n return {\n entries: cache.size,\n totalHits: projects.reduce((s, p) => s + p.hits, 0),\n projects,\n };\n}\n\n/**\n * Configure cache behavior.\n */\nexport function configureCache(options: Partial<CacheOptions>): void {\n cacheOptions = { ...cacheOptions, ...options };\n if (!cacheOptions.enabled) {\n cache.clear();\n }\n}\n","import { createHash } from 'node:crypto';\nimport type {\n AnalyzedFile,\n ProjectAnalysis,\n ContextSelection,\n SelectedFile,\n SelectionDecision,\n PruneLevel,\n CoverageResult,\n} from '../types/engine.js';\nimport type { PolicySet, PolicyRule } from '../types/govern.js';\nimport { scanFileForSecrets } from '../govern/secrets.js';\nimport { pruneFile } from './pruner.js';\nimport { calculateCoverage } from './coverage.js';\nimport { getPruneLevelForRisk } from './budget.js';\nimport { buildAdjacencyList, bfsBidirectional, matchGlob } from './graph-utils.js';\n\n// ===== DETERMINISTIC CONTEXT SELECTION =====\n//\n// Given a task, analysis, budget, and policies → always returns the same file set.\n// No randomness, no heuristics that change over time.\n//\n// Algorithm:\n// 1. Identify target files from task description\n// 2. Expand via dependency graph (BFS, depth=2)\n// 3. Apply policies (include-always, exclude-always)\n// 4. Score & rank by riskScore\n// 5. Greedy allocation with cascading prune levels\n// 6. Validate policies\n// 7. Calculate coverage score\n// 8. Hash for reproducibility\n\nexport interface SelectionInput {\n task: string;\n analysis: ProjectAnalysis;\n budget: number;\n policies?: PolicySet;\n depth?: number;\n}\n\nexport async function selectContext(input: SelectionInput): Promise<ContextSelection> {\n const { task, analysis, budget, policies, depth = 2 } = input;\n const decisions: SelectionDecision[] = [];\n\n // 1. Identify target files from task description\n const targetPaths = identifyTargetFiles(task, analysis.files);\n if (targetPaths.length > 0) {\n decisions.push({\n file: targetPaths.join(', '),\n action: 'include-full',\n reason: `Target file(s) identified from task description`,\n });\n }\n\n // 2. Expand via dependency graph (O(V+E) using adjacency list)\n const adj = buildAdjacencyList(analysis.graph.edges);\n const expandedPaths = targetPaths.length > 0\n ? Array.from(bfsBidirectional(targetPaths, adj, depth))\n : [];\n const expansionCount = expandedPaths.length - targetPaths.length;\n if (expansionCount > 0) {\n decisions.push({\n file: `${expansionCount} dependencies`,\n action: 'include-full',\n reason: `Expanded ${targetPaths.length} target(s) to ${expandedPaths.length} files via dependency graph (depth ${depth})`,\n });\n }\n\n // 2b. Add type providers of expanded files as candidates.\n // This aligns with coverage.ts which counts type providers as \"relevant\".\n // Without this, coverage would flag missing critical type files that the\n // selector never considered as candidates — a consistency bug.\n const allFileMap = new Map(analysis.files.map((f) => [f.relativePath, f]));\n if (targetPaths.length > 0) {\n for (const path of expandedPaths) {\n const file = allFileMap.get(path);\n if (!file) continue;\n for (const imp of file.imports) {\n const impFile = allFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n expandedPaths.push(imp);\n }\n }\n }\n }\n\n // 3. Apply policies\n const { mustInclude, mustExclude } = applyPolicies(analysis.files, policies);\n\n // Merge: targets + expanded + must-include → candidate set\n const candidateSet = new Set([...expandedPaths, ...mustInclude]);\n\n // If no targets identified, use all files (filtered by exclude)\n if (targetPaths.length === 0) {\n for (const f of analysis.files) {\n candidateSet.add(f.relativePath);\n }\n }\n\n // Remove must-exclude\n for (const ex of mustExclude) {\n candidateSet.delete(ex);\n decisions.push({\n file: ex,\n action: 'exclude',\n reason: 'Excluded by policy',\n });\n }\n\n // 3b. secret-block: scan candidate files for secrets and exclude them\n const hasSecretBlock = policies?.rules.some(\n (r) => r.type === 'secret-block' && r.enabled,\n );\n if (hasSecretBlock) {\n for (const path of Array.from(candidateSet)) {\n const file = allFileMap.get(path);\n if (!file) continue;\n const findings = await scanFileForSecrets(\n file.path,\n analysis.projectPath,\n );\n if (findings.length > 0) {\n candidateSet.delete(path);\n decisions.push({\n file: path,\n action: 'exclude',\n reason: `Blocked: ${findings.length} secret(s) detected (${findings.map((f) => f.type).join(', ')})`,\n });\n }\n }\n }\n\n // 4. Sort candidates by riskScore descending\n const candidates = Array.from(candidateSet)\n .map((p) => allFileMap.get(p))\n .filter((f): f is AnalyzedFile => f !== undefined)\n .sort((a, b) => {\n // Targets always first\n const aIsTarget = targetPaths.includes(a.relativePath) ? 0 : 1;\n const bIsTarget = targetPaths.includes(b.relativePath) ? 0 : 1;\n if (aIsTarget !== bIsTarget) return aIsTarget - bIsTarget;\n\n // Then must-include\n const aIsMust = mustInclude.has(a.relativePath) ? 0 : 1;\n const bIsMust = mustInclude.has(b.relativePath) ? 0 : 1;\n if (aIsMust !== bIsMust) return aIsMust - bIsMust;\n\n // Then by riskScore\n return b.riskScore - a.riskScore;\n });\n\n // 5. Greedy allocation with cascading prune levels\n const selectedFiles: SelectedFile[] = [];\n let usedTokens = 0;\n\n for (const file of candidates) {\n const isTarget = targetPaths.includes(file.relativePath);\n const isMustInclude = mustInclude.has(file.relativePath);\n const defaultLevel = isTarget ? 'full' : getPruneLevelForRisk(file.riskScore);\n const levels = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levels) {\n if (level === 'excluded') break;\n\n let tokens: number;\n if (level === 'full') {\n tokens = file.tokens;\n } else {\n const pruned = await pruneFile(file, level);\n tokens = pruned.prunedTokens;\n }\n\n if (usedTokens + tokens <= budget) {\n usedTokens += tokens;\n selectedFiles.push({\n relativePath: file.relativePath,\n tokens,\n originalTokens: file.tokens,\n pruneLevel: level,\n riskScore: file.riskScore,\n reason: buildReason(file, level, isTarget, isMustInclude),\n });\n\n if (level !== defaultLevel) {\n decisions.push({\n file: file.relativePath,\n action: `include-${level}` as SelectionDecision['action'],\n reason: `Downgraded from ${defaultLevel} to ${level} due to budget constraint`,\n alternatives: `Would need ${file.tokens - tokens} more tokens for ${defaultLevel}`,\n });\n }\n\n included = true;\n break;\n }\n }\n\n if (!included) {\n decisions.push({\n file: file.relativePath,\n action: 'exclude',\n reason: `Budget exhausted (risk: ${file.riskScore}, needs ${file.tokens} tokens)`,\n });\n }\n }\n\n // 6. Calculate coverage\n const includedPaths = selectedFiles.map((f) => f.relativePath);\n const coverage = calculateCoverage(\n targetPaths,\n includedPaths,\n analysis.files,\n analysis.graph,\n depth,\n );\n\n // 7. Calculate overall risk of the selection (lower = better)\n const includedSet = new Set(includedPaths);\n const excludedFiles = analysis.files.filter(\n (f) => !includedSet.has(f.relativePath),\n );\n const excludedRisk = excludedFiles.length > 0\n ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length)\n : 0;\n\n // 8. Hash for determinism\n const hashInput = selectedFiles\n .map((f) => `${f.relativePath}:${f.pruneLevel}`)\n .sort()\n .join('|') + `|budget:${budget}`;\n const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 16);\n\n return {\n files: selectedFiles,\n totalTokens: usedTokens,\n budget,\n usedPercent: budget > 0 ? Math.round((usedTokens / budget) * 100 * 10) / 10 : 0,\n coverage,\n riskScore: excludedRisk,\n deterministic: true,\n hash,\n decisions,\n };\n}\n\n// ===== TARGET IDENTIFICATION =====\n\nfunction identifyTargetFiles(task: string, files: AnalyzedFile[]): string[] {\n const targets: string[] = [];\n\n // Extract file paths mentioned in the task\n // Matches patterns like: src/foo/bar.ts, ./utils.js, components/Header.tsx\n const pathPattern = /(?:^|\\s|[\"'`])([.\\w/-]+\\.[a-zA-Z]{1,4})(?:\\s|$|[\"'`]|,|:)/g;\n let match: RegExpExecArray | null;\n\n while ((match = pathPattern.exec(task)) !== null) {\n const candidate = match[1];\n // Find matching file\n const found = files.find(\n (f) => f.relativePath === candidate || f.relativePath.endsWith(candidate),\n );\n if (found && !targets.includes(found.relativePath)) {\n targets.push(found.relativePath);\n }\n }\n\n return targets;\n}\n\n// ===== POLICY APPLICATION =====\n\nfunction applyPolicies(\n files: AnalyzedFile[],\n policies?: PolicySet,\n): { mustInclude: Set<string>; mustExclude: Set<string> } {\n const mustInclude = new Set<string>();\n const mustExclude = new Set<string>();\n\n if (!policies) return { mustInclude, mustExclude };\n\n for (const rule of policies.rules) {\n if (!rule.enabled) continue;\n\n if (rule.type === 'include-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustInclude.add(file.relativePath);\n }\n }\n }\n\n if (rule.type === 'exclude-always' && rule.pattern) {\n for (const file of files) {\n if (matchGlob(file.relativePath, rule.pattern)) {\n mustExclude.add(file.relativePath);\n }\n }\n }\n\n // secret-block is handled separately in applyPoliciesAsync\n // because it requires file I/O (scanning file contents)\n }\n\n return { mustInclude, mustExclude };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n\nfunction buildReason(\n file: AnalyzedFile,\n level: PruneLevel,\n isTarget: boolean,\n isMustInclude: boolean,\n): string {\n if (isTarget) return 'Target file';\n if (isMustInclude) return 'Required by policy';\n\n const impact = file.exclusionImpact;\n const levelStr = level === 'full' ? 'full content' : level;\n\n if (impact === 'critical') return `Critical dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'high') return `High-risk dependency (risk ${file.riskScore}) — ${levelStr}`;\n if (impact === 'medium') return `Medium relevance (risk ${file.riskScore}) — ${levelStr}`;\n return `Low relevance (risk ${file.riskScore}) — ${levelStr}`;\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, relative } from 'node:path';\nimport type { SecretFinding, SecretType } from '../types/govern.js';\n\n// ===== SECRET DETECTION ENGINE =====\n\ninterface SecretPattern {\n type: SecretType;\n pattern: RegExp;\n severity: SecretFinding['severity'];\n description: string;\n}\n\nconst BUILTIN_PATTERNS: { type: SecretType; source: string; flags: string; severity: SecretFinding['severity']; description: string }[] = [\n // API Keys\n { type: 'api-key', source: '(?:api[_-]?key|apikey)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9_\\\\-]{20,})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'API Key' },\n { type: 'api-key', source: 'sk-[a-zA-Z0-9]{20,}', flags: 'g', severity: 'critical', description: 'OpenAI/Anthropic API Key' },\n { type: 'api-key', source: 'sk-ant-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'Anthropic API Key' },\n\n // AWS\n { type: 'aws-key', source: 'AKIA[0-9A-Z]{16}', flags: 'g', severity: 'critical', description: 'AWS Access Key ID' },\n { type: 'aws-key', source: '(?:aws_secret_access_key|aws_secret)\\\\s*[:=]\\\\s*[\\'\"]?([a-zA-Z0-9/+=]{40})[\\'\"]?', flags: 'gi', severity: 'critical', description: 'AWS Secret Key' },\n\n // Private Keys\n { type: 'private-key', source: '-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'Private Key' },\n { type: 'private-key', source: '-----BEGIN OPENSSH PRIVATE KEY-----', flags: 'g', severity: 'critical', description: 'SSH Private Key' },\n\n // Passwords\n { type: 'password', source: '(?:password|passwd|pwd)\\\\s*[:=]\\\\s*[\\'\"]([^\\'\"]{8,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Hardcoded Password' },\n { type: 'password', source: '(?:DB_PASSWORD|DATABASE_PASSWORD|MYSQL_PASSWORD|POSTGRES_PASSWORD)\\\\s*[:=]\\\\s*[\\'\"]?([^\\'\"{}\\\\s]{4,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database Password' },\n\n // Tokens\n { type: 'token', source: '(?:bearer|token|auth_token|access_token|refresh_token)\\\\s*[:=]\\\\s*[\\'\"]([a-zA-Z0-9_\\\\-.]{20,})[\\'\"](?!\\\\s*\\\\{)', flags: 'gi', severity: 'high', description: 'Auth Token' },\n { type: 'token', source: 'ghp_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub Personal Access Token' },\n { type: 'token', source: 'gho_[a-zA-Z0-9]{36}', flags: 'g', severity: 'critical', description: 'GitHub OAuth Token' },\n { type: 'token', source: 'glpat-[a-zA-Z0-9\\\\-]{20,}', flags: 'g', severity: 'critical', description: 'GitLab Personal Access Token' },\n { type: 'token', source: 'npm_[a-zA-Z0-9]{36}', flags: 'g', severity: 'high', description: 'npm Token' },\n\n // Connection strings\n { type: 'connection-string', source: '(?:mongodb(?:\\\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\\\/\\\\/[^\\\\s\\'\"]+:[^\\\\s\\'\"]+@[^\\\\s\\'\"]+', flags: 'gi', severity: 'critical', description: 'Database Connection String' },\n { type: 'connection-string', source: '(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{10,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Database URL' },\n\n // Environment variables with secrets\n { type: 'env-variable', source: '(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\\\s*[:=]\\\\s*[\\'\"]?([^\\\\s\\'\"]{8,})[\\'\"]?', flags: 'gi', severity: 'high', description: 'Secret Environment Variable' },\n\n // Stripe\n { type: 'api-key', source: 'sk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Live Secret Key' },\n { type: 'api-key', source: 'pk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'high', description: 'Stripe Live Publishable Key' },\n { type: 'api-key', source: 'rk_live_[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Stripe Restricted Key' },\n\n // Slack\n { type: 'token', source: 'xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack Bot Token' },\n { type: 'token', source: 'xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}', flags: 'g', severity: 'critical', description: 'Slack User Token' },\n { type: 'api-key', source: 'https://hooks\\\\.slack\\\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+', flags: 'g', severity: 'high', description: 'Slack Webhook URL' },\n\n // Google\n { type: 'api-key', source: 'AIza[0-9A-Za-z_-]{35}', flags: 'g', severity: 'high', description: 'Google API Key' },\n { type: 'token', source: 'ya29\\\\.[0-9A-Za-z_-]+', flags: 'g', severity: 'high', description: 'Google OAuth Token' },\n\n // Azure\n { type: 'api-key', source: '(?:AccountKey|SharedAccessKey)\\\\s*=\\\\s*[a-zA-Z0-9+/=]{40,}', flags: 'g', severity: 'critical', description: 'Azure Storage Key' },\n\n // Twilio\n { type: 'api-key', source: 'AC[a-f0-9]{32}', flags: 'g', severity: 'high', description: 'Twilio Account SID' },\n\n // SendGrid\n { type: 'api-key', source: 'SG\\\\.[a-zA-Z0-9_-]{22}\\\\.[a-zA-Z0-9_-]{43}', flags: 'g', severity: 'critical', description: 'SendGrid API Key' },\n\n // JWT\n { type: 'token', source: 'eyJ[a-zA-Z0-9_-]{10,}\\\\.eyJ[a-zA-Z0-9_-]{10,}\\\\.[a-zA-Z0-9_-]{10,}', flags: 'g', severity: 'high', description: 'JSON Web Token' },\n\n // PII\n { type: 'pii', source: '\\\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Z|a-z]{2,}\\\\b', flags: 'g', severity: 'medium', description: 'Email Address (PII)' },\n { type: 'pii', source: '\\\\b\\\\d{3}[-.]?\\\\d{2}[-.]?\\\\d{4}\\\\b', flags: 'g', severity: 'high', description: 'Possible SSN (PII)' },\n];\n\nfunction buildPatterns(customPatterns: string[] = []): SecretPattern[] {\n const patterns: SecretPattern[] = BUILTIN_PATTERNS.map((def) => ({\n type: def.type,\n pattern: new RegExp(def.source, def.flags),\n severity: def.severity,\n description: def.description,\n }));\n\n for (const custom of customPatterns) {\n try {\n patterns.push({\n type: 'custom',\n pattern: new RegExp(custom, 'gi'),\n severity: 'medium',\n description: `Custom pattern: ${custom}`,\n });\n } catch { /* skip invalid regex */ }\n }\n\n return patterns;\n}\n\nexport function scanContentForSecrets(\n content: string,\n filePath: string,\n customPatterns: string[] = [],\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n secretPattern.pattern.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = secretPattern.pattern.exec(line)) !== null) {\n const matchText = match[0];\n if (isTemplateOrPlaceholder(matchText)) continue;\n\n findings.push({\n type: secretPattern.type,\n file: filePath,\n line: i + 1,\n match: matchText,\n redacted: redactSecret(matchText),\n severity: secretPattern.severity,\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\nexport async function scanFileForSecrets(\n filePath: string,\n projectPath: string,\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(filePath));\n return scanContentForSecrets(content, relPath, customPatterns);\n } catch {\n return [];\n }\n}\n\nexport async function scanProjectForSecrets(\n projectPath: string,\n filePaths: string[],\n customPatterns: string[] = [],\n): Promise<SecretFinding[]> {\n const allFindings: SecretFinding[] = [];\n\n for (const fp of filePaths) {\n const findings = await scanFileForSecrets(fp, projectPath, customPatterns);\n allFindings.push(...findings);\n }\n\n return allFindings.sort((a, b) => {\n const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n}\n\nexport function sanitizeContent(content: string, customPatterns: string[] = []): string {\n let sanitized = content;\n const allPatterns = buildPatterns(customPatterns);\n\n for (const secretPattern of allPatterns) {\n sanitized = sanitized.replace(secretPattern.pattern, (match) => {\n if (isTemplateOrPlaceholder(match)) return match;\n return redactSecret(match);\n });\n }\n\n return sanitized;\n}\n\nfunction redactSecret(value: string): string {\n if (value.length <= 8) return '***REDACTED***';\n const prefix = value.substring(0, 4);\n const suffix = value.substring(value.length - 2);\n return `${prefix}${'*'.repeat(Math.min(value.length - 6, 20))}${suffix}`;\n}\n\nfunction isTemplateOrPlaceholder(value: string): boolean {\n const placeholders = [\n /\\$\\{.*\\}/, /\\{\\{.*\\}\\}/, /%[sd]/, /<[A-Z_]+>/, /YOUR_.*_HERE/i,\n /\\bCHANGE_ME\\b/i, /\\bPLACEHOLDER\\b/i, /\\bexample\\b/i, /\\bTODO\\b/i, /xxx+/i,\n /\\breplace.?me\\b/i, /\\bdummy\\b/i, /\\btest_?key\\b/i, /\\bsample\\b/i,\n ];\n return placeholders.some((p) => p.test(value));\n}\n\nfunction deduplicateFindings(findings: SecretFinding[]): SecretFinding[] {\n const seen = new Set<string>();\n return findings.filter((f) => {\n const key = `${f.file}:${f.line}:${f.type}:${f.match}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n// ===== ENTROPY ANALYSIS =====\n\nfunction shannonEntropy(str: string): number {\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) || 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n if (p > 0) entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\nconst HIGH_ENTROPY_RE = /['\"]([a-zA-Z0-9+/=_\\-]{30,})['\"]|=\\s*['\"]?([a-zA-Z0-9+/=_\\-]{30,})['\"]?/g;\n\nconst ENTROPY_SKIP = [\n /^[a-f0-9]{32,}$/i, // hex hashes\n /^[A-Z_]{30,}$/, // all-caps constants\n /^[a-z_]{30,}$/, // all-lowercase identifiers\n /^[a-zA-Z0-9+/]+=+$/, // base64 padding\n /^[a-z]+[A-Z][a-zA-Z]+$/, // camelCase identifiers\n /sha\\d+-/i, // integrity hashes (sha256-, sha512-)\n];\n\nexport function scanContentForHighEntropy(\n content: string,\n filePath: string,\n threshold: number = 5.0,\n): SecretFinding[] {\n const findings: SecretFinding[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.trim().startsWith('//') || line.trim().startsWith('#') || line.trim().startsWith('*')) continue;\n\n HIGH_ENTROPY_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = HIGH_ENTROPY_RE.exec(line)) !== null) {\n const value = match[1] || match[2];\n if (!value || value.length < 40) continue;\n if (isTemplateOrPlaceholder(value)) continue;\n if (ENTROPY_SKIP.some((p) => p.test(value))) continue;\n\n const entropy = shannonEntropy(value);\n if (entropy >= threshold) {\n findings.push({\n type: 'high-entropy',\n file: filePath,\n line: i + 1,\n match: value,\n redacted: redactSecret(value),\n severity: entropy >= 5.0 ? 'high' : 'medium',\n });\n }\n }\n }\n\n return deduplicateFindings(findings);\n}\n\n// ===== FULL PROJECT AUDIT =====\n\nexport interface AuditResult {\n findings: SecretFinding[];\n summary: {\n totalFiles: number;\n filesScanned: number;\n filesWithSecrets: number;\n totalFindings: number;\n bySeverity: { critical: number; high: number; medium: number; low: number };\n byType: Record<string, number>;\n };\n recommendations: string[];\n}\n\nexport async function auditProject(\n projectPath: string,\n filePaths: string[],\n options: { customPatterns?: string[]; entropyThreshold?: number; includePII?: boolean } = {},\n): Promise<AuditResult> {\n const { customPatterns = [], entropyThreshold = 4.5, includePII = true } = options;\n const allFindings: SecretFinding[] = [];\n const filesWithSecrets = new Set<string>();\n\n for (const fp of filePaths) {\n try {\n const content = await readFile(fp, 'utf-8');\n const relPath = relative(resolve(projectPath), resolve(fp));\n\n // Skip test files and declaration files for entropy (too many false positives)\n const isTestFile = /\\.(test|spec|mock)\\.[jt]sx?$/.test(relPath) || relPath.includes('__tests__');\n const isDtsFile = relPath.endsWith('.d.ts');\n\n // Pattern-based detection\n let findings = scanContentForSecrets(content, relPath, customPatterns);\n\n // Filter PII if not wanted\n if (!includePII) {\n findings = findings.filter((f) => f.type !== 'pii');\n }\n\n // Entropy-based detection (skip test/declaration files — too noisy)\n const entropyFindings = (isTestFile || isDtsFile)\n ? []\n : scanContentForHighEntropy(content, relPath, entropyThreshold);\n\n const combined = [...findings, ...entropyFindings];\n if (combined.length > 0) {\n filesWithSecrets.add(relPath);\n allFindings.push(...combined);\n }\n } catch {\n // skip unreadable files\n }\n }\n\n // Sort by severity\n allFindings.sort((a, b) => {\n const order = { critical: 0, high: 1, medium: 2, low: 3 };\n return order[a.severity] - order[b.severity];\n });\n\n // Build summary\n const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };\n const byType: Record<string, number> = {};\n for (const f of allFindings) {\n bySeverity[f.severity]++;\n byType[f.type] = (byType[f.type] || 0) + 1;\n }\n\n // Generate recommendations\n const recommendations: string[] = [];\n if (bySeverity.critical > 0) {\n recommendations.push('CRITICAL: Rotate all detected credentials immediately. They may already be compromised.');\n }\n if (byType['password'] > 0) {\n recommendations.push('Move passwords to environment variables or a secrets manager (AWS Secrets Manager, Vault, etc.).');\n }\n if (byType['api-key'] > 0 || byType['aws-key'] > 0) {\n recommendations.push('Use environment variables for API keys. Never commit them to source control.');\n }\n if (byType['connection-string'] > 0) {\n recommendations.push('Database connection strings should use environment variables, not hardcoded values.');\n }\n if (byType['private-key'] > 0) {\n recommendations.push('Private keys should NEVER be in source code. Use a key management service.');\n }\n if (byType['pii'] > 0) {\n recommendations.push('PII detected. Review for GDPR/CCPA compliance. Consider data anonymization.');\n }\n if (byType['high-entropy'] > 0) {\n recommendations.push('High-entropy strings detected that may be secrets. Review manually.');\n }\n if (allFindings.length > 0) {\n recommendations.push('Add a .gitignore entry for .env files if not already present.');\n recommendations.push('Run `npx cto-ai-cli --audit` regularly or add to CI pipeline.');\n }\n if (allFindings.length === 0) {\n recommendations.push('No secrets detected. Great job keeping your codebase clean!');\n }\n\n return {\n findings: allFindings,\n summary: {\n totalFiles: filePaths.length,\n filesScanned: filePaths.length,\n filesWithSecrets: filesWithSecrets.size,\n totalFindings: allFindings.length,\n bySeverity,\n byType,\n },\n recommendations,\n };\n}\n","import { Project, SyntaxKind, type SourceFile } from 'ts-morph';\nimport { readFile } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { AnalyzedFile, PruneLevel, PrunedContent } from '../types/engine.js';\nimport { countTokensChars4 } from './tokenizer.js';\n\nconst TS_EXTENSIONS = new Set(['ts', 'tsx', 'js', 'jsx', 'mts', 'mjs']);\n\n// ===== PUBLIC API =====\n\nexport async function pruneFile(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n if (level === 'excluded') {\n return emptyResult(file, 'excluded');\n }\n\n if (level === 'full') {\n return fullContent(file);\n }\n\n const ext = file.extension.toLowerCase();\n const isTS = TS_EXTENSIONS.has(ext);\n\n if (isTS) {\n return pruneTypeScript(file, level);\n }\n\n return pruneGeneric(file, level);\n}\n\nexport async function pruneFiles(\n files: AnalyzedFile[],\n levelFn: (file: AnalyzedFile) => PruneLevel,\n): Promise<PrunedContent[]> {\n const results: PrunedContent[] = [];\n\n for (const file of files) {\n const level = levelFn(file);\n const pruned = await pruneFile(file, level);\n results.push(pruned);\n }\n\n return results;\n}\n\n// ===== TYPESCRIPT AST-BASED PRUNING =====\n\nasync function pruneTypeScript(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n let project: Project;\n try {\n const tsConfigPath = findTsConfig(file.path);\n project = new Project({\n tsConfigFilePath: tsConfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: tsConfigPath\n ? undefined\n : { allowJs: true, esModuleInterop: true },\n });\n project.createSourceFile(file.path, content, { overwrite: true });\n } catch {\n // Fallback to generic pruning if AST fails\n return pruneGenericFromContent(file, content, level);\n }\n\n const sourceFile = project.getSourceFiles()[0];\n if (!sourceFile) {\n return pruneGenericFromContent(file, content, level);\n }\n\n const prunedContent = level === 'signatures'\n ? extractSignaturesAST(sourceFile)\n : extractSkeletonAST(sourceFile);\n\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== SIGNATURES: Keep imports, type defs, function signatures, class outlines =====\n\nfunction extractSignaturesAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases\n for (const ta of sf.getTypeAliases()) {\n addJSDoc(ta, parts);\n parts.push(ta.getText());\n }\n\n // Interfaces\n for (const iface of sf.getInterfaces()) {\n addJSDoc(iface, parts);\n parts.push(iface.getText());\n }\n\n // Enums\n for (const en of sf.getEnums()) {\n addJSDoc(en, parts);\n parts.push(en.getText());\n }\n\n // Function declarations — signature only\n for (const fn of sf.getFunctions()) {\n addJSDoc(fn, parts);\n const isExported = fn.isExported();\n const isAsync = fn.isAsync();\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n const returnType = fn.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n\n const prefix = isExported ? 'export ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(`${prefix}${asyncStr}function ${name}(${params})${returnStr} { /* ... */ }`);\n }\n\n // Variable declarations (const/let with arrow functions)\n for (const stmt of sf.getVariableStatements()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && (init.getKind() === SyntaxKind.ArrowFunction || init.getKind() === SyntaxKind.FunctionExpression)) {\n addJSDoc(stmt, parts);\n const isExported = stmt.isExported();\n const prefix = isExported ? 'export ' : '';\n const kind = stmt.getDeclarationKind();\n const name = decl.getName();\n const typeNode = decl.getTypeNode()?.getText();\n const typeStr = typeNode ? `: ${typeNode}` : '';\n parts.push(`${prefix}${kind} ${name}${typeStr} = /* ... */;`);\n } else {\n // Non-function variables: keep full declaration\n addJSDoc(stmt, parts);\n parts.push(stmt.getText());\n }\n }\n }\n\n // Classes — outline with method signatures\n for (const cls of sf.getClasses()) {\n addJSDoc(cls, parts);\n const isExported = cls.isExported();\n const prefix = isExported ? 'export ' : '';\n const name = cls.getName() ?? '<anonymous>';\n const ext = cls.getExtends()?.getText();\n const impl = cls.getImplements().map((i) => i.getText()).join(', ');\n let header = `${prefix}class ${name}`;\n if (ext) header += ` extends ${ext}`;\n if (impl) header += ` implements ${impl}`;\n header += ' {';\n parts.push(header);\n\n // Properties\n for (const prop of cls.getProperties()) {\n parts.push(` ${prop.getText()}`);\n }\n\n // Constructor\n const ctor = cls.getConstructors()[0];\n if (ctor) {\n const ctorParams = ctor.getParameters().map((p) => p.getText()).join(', ');\n parts.push(` constructor(${ctorParams}) { /* ... */ }`);\n }\n\n // Methods — signature only\n for (const method of cls.getMethods()) {\n const isStatic = method.isStatic();\n const isAsync = method.isAsync();\n const methodName = method.getName();\n const methodParams = method.getParameters().map((p) => p.getText()).join(', ');\n const returnType = method.getReturnTypeNode()?.getText();\n const returnStr = returnType ? `: ${returnType}` : '';\n const staticStr = isStatic ? 'static ' : '';\n const asyncStr = isAsync ? 'async ' : '';\n parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);\n }\n\n parts.push('}');\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n // Export assignments\n for (const exp of sf.getExportAssignments()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== SKELETON: Keep only declarations, no bodies at all =====\n\nfunction extractSkeletonAST(sf: SourceFile): string {\n const parts: string[] = [];\n\n // Imports\n for (const imp of sf.getImportDeclarations()) {\n parts.push(imp.getText());\n }\n\n if (parts.length > 0) parts.push('');\n\n // Type aliases — full\n for (const ta of sf.getTypeAliases()) {\n if (ta.isExported()) parts.push(ta.getText());\n }\n\n // Interfaces — name + extends only\n for (const iface of sf.getInterfaces()) {\n if (!iface.isExported()) continue;\n const ext = iface.getExtends().map((e) => e.getText());\n const extStr = ext.length > 0 ? ` extends ${ext.join(', ')}` : '';\n parts.push(`export interface ${iface.getName()}${extStr} { /* ${iface.getProperties().length} props */ }`);\n }\n\n // Enums — name only\n for (const en of sf.getEnums()) {\n if (!en.isExported()) continue;\n const members = en.getMembers().map((m) => m.getName());\n parts.push(`export enum ${en.getName()} { ${members.join(', ')} }`);\n }\n\n // Functions — name + params only\n for (const fn of sf.getFunctions()) {\n if (!fn.isExported()) continue;\n const name = fn.getName() ?? '<anonymous>';\n const params = fn.getParameters().map((p) => p.getText()).join(', ');\n parts.push(`export function ${name}(${params});`);\n }\n\n // Classes — name + method names only\n for (const cls of sf.getClasses()) {\n if (!cls.isExported()) continue;\n const methods = cls.getMethods().map((m) => m.getName());\n parts.push(`export class ${cls.getName()} { /* methods: ${methods.join(', ')} */ }`);\n }\n\n // Re-exports\n for (const exp of sf.getExportDeclarations()) {\n parts.push(exp.getText());\n }\n\n return parts.join('\\n');\n}\n\n// ===== GENERIC PRUNING (non-TS files) =====\n\nasync function pruneGeneric(\n file: AnalyzedFile,\n level: PruneLevel,\n): Promise<PrunedContent> {\n let content: string;\n try {\n content = await readFile(file.path, 'utf-8');\n } catch {\n return emptyResult(file, level);\n }\n\n return pruneGenericFromContent(file, content, level);\n}\n\nfunction pruneGenericFromContent(\n file: AnalyzedFile,\n content: string,\n level: PruneLevel,\n): PrunedContent {\n const lines = content.split('\\n');\n let result: string[];\n\n if (level === 'signatures') {\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t === '' ||\n t.startsWith('#') ||\n t.startsWith('//') ||\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('async def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n t.startsWith('const ') ||\n t.startsWith('let ') ||\n t.startsWith('var ') ||\n /^(pub |fn |struct |enum |impl |mod |use )/.test(t)\n );\n });\n } else {\n // skeleton: even more aggressive — only declarations\n result = lines.filter((line) => {\n const t = line.trim();\n return (\n t.startsWith('import ') ||\n t.startsWith('from ') ||\n t.startsWith('export ') ||\n t.startsWith('def ') ||\n t.startsWith('class ') ||\n t.startsWith('function ') ||\n /^(pub |fn |struct |enum |mod |use )/.test(t)\n );\n });\n }\n\n const prunedContent = result.join('\\n');\n const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, 'utf-8'));\n const savingsPercent = file.tokens > 0 ? ((file.tokens - prunedTokens) / file.tokens) * 100 : 0;\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens,\n pruneLevel: level,\n content: prunedContent,\n savingsPercent: Math.max(0, savingsPercent),\n };\n}\n\n// ===== HELPERS =====\n\nasync function fullContent(file: AnalyzedFile): Promise<PrunedContent> {\n let content = '';\n try {\n content = await readFile(file.path, 'utf-8');\n } catch { /* empty */ }\n\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: file.tokens,\n pruneLevel: 'full',\n content,\n savingsPercent: 0,\n };\n}\n\nfunction emptyResult(file: AnalyzedFile, level: PruneLevel): PrunedContent {\n return {\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n prunedTokens: 0,\n pruneLevel: level,\n content: '',\n savingsPercent: 100,\n };\n}\n\nfunction addJSDoc(node: { getJsDocs?: () => { getText(): string }[] }, parts: string[]): void {\n if (!node.getJsDocs) return;\n const docs = node.getJsDocs();\n if (docs.length > 0) {\n parts.push(docs[0].getText());\n }\n}\n\nfunction findTsConfig(filePath: string): string | undefined {\n let dir = filePath;\n for (let i = 0; i < 10; i++) {\n dir = join(dir, '..');\n const candidate = join(dir, 'tsconfig.json');\n if (existsSync(candidate)) return candidate;\n }\n return undefined;\n}\n","import type { GraphEdge } from '../types/engine.js';\n\n// ===== SHARED GRAPH UTILITIES =====\n\nexport interface AdjacencyList {\n forward: Map<string, string[]>; // file → files it imports\n reverse: Map<string, string[]>; // file → files that import it\n}\n\nexport function buildAdjacencyList(edges: GraphEdge[]): AdjacencyList {\n const forward = new Map<string, string[]>();\n const reverse = new Map<string, string[]>();\n\n for (const edge of edges) {\n if (!forward.has(edge.from)) forward.set(edge.from, []);\n forward.get(edge.from)!.push(edge.to);\n\n if (!reverse.has(edge.to)) reverse.set(edge.to, []);\n reverse.get(edge.to)!.push(edge.from);\n }\n\n return { forward, reverse };\n}\n\nexport function bfsBidirectional(\n seeds: string[],\n adj: AdjacencyList,\n depth: number,\n): Set<string> {\n const result = new Set(seeds);\n let frontier = [...seeds];\n const visited = new Set<string>();\n\n for (let d = 0; d < depth; d++) {\n const nextFrontier: string[] = [];\n\n for (const node of frontier) {\n if (visited.has(node)) continue;\n visited.add(node);\n\n // Forward neighbors (imports)\n const fwd = adj.forward.get(node);\n if (fwd) {\n for (const neighbor of fwd) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n\n // Reverse neighbors (imported by)\n const rev = adj.reverse.get(node);\n if (rev) {\n for (const neighbor of rev) {\n if (!visited.has(neighbor)) {\n result.add(neighbor);\n nextFrontier.push(neighbor);\n }\n }\n }\n }\n\n frontier = nextFrontier;\n }\n\n return result;\n}\n\nexport function matchGlob(path: string, pattern: string): boolean {\n const regexStr = pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*\\*/g, '§§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§§/g, '.*')\n .replace(/\\?/g, '.');\n\n try {\n return new RegExp(`^${regexStr}$`).test(path);\n } catch {\n return false;\n }\n}\n","import type {\n AnalyzedFile,\n ProjectGraph,\n CoverageResult,\n} from '../types/engine.js';\nimport { buildAdjacencyList, bfsBidirectional } from './graph-utils.js';\n\n// ===== CONTEXT COVERAGE SCORING =====\n//\n// Given a set of included files and a task's target files, calculate\n// how much of the \"relevant universe\" is covered.\n//\n// coverageScore = |includedRelevant| / |relevantFiles| × 100\n//\n// \"Relevant\" = target files + their dependencies (up to `depth` levels)\n// + type providers used by included files.\n\nexport function calculateCoverage(\n targetPaths: string[],\n includedPaths: string[],\n allFiles: AnalyzedFile[],\n graph: ProjectGraph,\n depth: number = 2,\n): CoverageResult {\n // 1. Find all relevant files: targets + their dependency cone\n // Uses adjacency list for O(V+E) BFS instead of scanning all edges per node.\n const adj = buildAdjacencyList(graph.edges);\n const relevantSet = targetPaths.length > 0\n ? bfsBidirectional(targetPaths, adj, depth)\n : new Set<string>();\n const includedSet = new Set(includedPaths);\n\n // Also add type providers used by included files\n const tempFileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n for (const path of includedPaths) {\n const file = tempFileMap.get(path);\n if (!file) continue;\n\n for (const imp of file.imports) {\n const impFile = tempFileMap.get(imp);\n if (impFile && impFile.kind === 'type') {\n relevantSet.add(imp);\n }\n }\n }\n\n // 2. Calculate coverage\n const relevantFiles = Array.from(relevantSet);\n const includedRelevant = relevantFiles.filter((f) => includedSet.has(f));\n const missingRelevant = relevantFiles.filter((f) => !includedSet.has(f));\n\n // Missing critical = missing files with critical/high exclusion impact\n const missingCritical = missingRelevant.filter((f) => {\n const file = tempFileMap.get(f);\n return file && (file.exclusionImpact === 'critical' || file.exclusionImpact === 'high');\n });\n\n // Risk-weighted coverage: missing a high-risk file hurts more than missing a low-risk one.\n // score = Σ(riskScore of included relevant) / Σ(riskScore of all relevant) × 100\n // Falls back to count-based if no risk data is available.\n const fileMap = new Map(allFiles.map((f) => [f.relativePath, f]));\n let totalRelevantRisk = 0;\n let includedRelevantRisk = 0;\n\n for (const f of relevantFiles) {\n const risk = fileMap.get(f)?.riskScore ?? 1;\n totalRelevantRisk += risk;\n if (includedSet.has(f)) {\n includedRelevantRisk += risk;\n }\n }\n\n const score = totalRelevantRisk > 0\n ? Math.round((includedRelevantRisk / totalRelevantRisk) * 100)\n : relevantFiles.length > 0\n ? Math.round((includedRelevant.length / relevantFiles.length) * 100)\n : 100;\n\n // Build explanation\n let explanation: string;\n if (score >= 90) {\n explanation = `Excellent coverage (${score}%): AI has nearly all relevant context.`;\n } else if (score >= 70) {\n explanation = `Good coverage (${score}%): Most relevant files included.`;\n if (missingCritical.length > 0) {\n explanation += ` Warning: ${missingCritical.length} critical file(s) missing.`;\n }\n } else if (score >= 50) {\n explanation = `Partial coverage (${score}%): Significant context is missing.`;\n if (missingCritical.length > 0) {\n explanation += ` ${missingCritical.length} critical file(s) not included — AI quality will degrade.`;\n }\n } else {\n explanation = `Low coverage (${score}%): Most relevant files are excluded. AI response quality will be poor.`;\n }\n\n return {\n score,\n relevantFiles,\n includedRelevant,\n missingRelevant,\n missingCritical,\n explanation,\n };\n}\n","import type { AnalyzedFile, BudgetPlan, BudgetEntry, PruneLevel } from '../types/engine.js';\nimport { pruneFile } from './pruner.js';\n\n// ===== BUDGET OPTIMIZER =====\n//\n// Greedy knapsack with cascading prune levels.\n// Files are sorted by riskScore (descending) — highest risk first.\n// Each file is tried at progressively smaller prune levels until it fits.\n\nexport function getPruneLevelForRisk(riskScore: number): PruneLevel {\n if (riskScore >= 80) return 'full'; // critical — include everything, no cascading\n if (riskScore >= 60) return 'full'; // high — try full first, can cascade\n if (riskScore >= 30) return 'signatures'; // medium — signatures by default\n return 'skeleton'; // low — skeleton\n}\n\nexport function isCriticalRisk(riskScore: number): boolean {\n return riskScore >= 80;\n}\n\nexport async function optimizeBudget(\n files: AnalyzedFile[],\n budget: number,\n): Promise<BudgetPlan> {\n const entries: BudgetEntry[] = [];\n let used = 0;\n\n // Sort by riskScore descending — most important files first\n const sorted = [...files].sort((a, b) => b.riskScore - a.riskScore);\n\n for (const file of sorted) {\n const defaultLevel = getPruneLevelForRisk(file.riskScore);\n\n // Try cascading prune levels: full → signatures → skeleton → excluded\n const levelsToTry = getCascadeLevels(defaultLevel);\n\n let included = false;\n\n for (const level of levelsToTry) {\n if (level === 'excluded') break;\n\n if (level === 'full') {\n if (used + file.tokens <= budget) {\n used += file.tokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: file.tokens,\n pruneLevel: 'full',\n included: true,\n reason: `Risk ${file.riskScore} — included in full`,\n });\n included = true;\n break;\n }\n } else {\n const pruned = await pruneFile(file, level);\n if (pruned.prunedTokens > 0 && used + pruned.prunedTokens <= budget) {\n used += pruned.prunedTokens;\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: pruned.prunedTokens,\n pruneLevel: level,\n included: true,\n reason: `Risk ${file.riskScore} — pruned to ${level} (budget constraint)`,\n });\n included = true;\n break;\n }\n }\n }\n\n if (!included) {\n entries.push({\n relativePath: file.relativePath,\n originalTokens: file.tokens,\n allocatedTokens: 0,\n pruneLevel: 'excluded',\n included: false,\n reason: `Risk ${file.riskScore} — excluded (budget exhausted, ${file.tokens} tokens)`,\n });\n }\n }\n\n return {\n budget,\n used,\n remaining: budget - used,\n fillPercent: budget > 0 ? Math.round((used / budget) * 100 * 10) / 10 : 0,\n files: entries,\n };\n}\n\n// ===== HELPERS =====\n\nfunction getCascadeLevels(startLevel: PruneLevel): PruneLevel[] {\n const all: PruneLevel[] = ['full', 'signatures', 'skeleton', 'excluded'];\n const startIdx = all.indexOf(startLevel);\n return all.slice(startIdx);\n}\n","import type { ProjectAnalysis, AnalyzedFile } from '../types/engine.js';\nimport type { ContextSelection } from '../types/engine.js';\nimport { selectContext } from './selector.js';\nimport { calculateCoverage } from './coverage.js';\nimport { buildAdjacencyList } from './graph-utils.js';\n\n// ===== Context Score™ =====\n//\n// A single 0-100 metric that quantifies how well a project's context\n// can be optimized for AI consumption.\n//\n// The score answers: \"If I send this project to an AI with CTO,\n// how much value am I getting per token?\"\n//\n// Dimensions:\n// 1. Efficiency (30%) — How much can we compress without losing value?\n// 2. Coverage (25%) — How much of the relevant universe do we capture?\n// 3. Risk Control (20%) — Are high-risk files properly prioritized?\n// 4. Structure (15%) — How well-organized is the codebase for AI consumption?\n// 5. Governance (10%) — Is the project set up for safe, auditable AI use?\n//\n// Grade scale:\n// A+ (95-100) | A (90-94) | A- (85-89)\n// B+ (80-84) | B (75-79) | B- (70-74)\n// C+ (65-69) | C (60-64) | C- (55-59)\n// D (40-54) | F (0-39)\n\nexport type Grade = 'A+' | 'A' | 'A-' | 'B+' | 'B' | 'B-' | 'C+' | 'C' | 'C-' | 'D' | 'F';\n\nexport interface ContextScore {\n overall: number;\n grade: Grade;\n\n dimensions: {\n efficiency: DimensionScore;\n coverage: DimensionScore;\n riskControl: DimensionScore;\n structure: DimensionScore;\n governance: DimensionScore;\n };\n\n insights: ScoreInsight[];\n\n comparison: {\n naiveTokens: number;\n optimizedTokens: number;\n savedTokens: number;\n savedPercent: number;\n monthlySavingsUSD: number;\n };\n\n meta: {\n projectName: string;\n totalFiles: number;\n totalTokens: number;\n analyzedAt: Date;\n };\n}\n\nexport interface DimensionScore {\n score: number;\n weight: number;\n weighted: number;\n detail: string;\n}\n\nexport interface ScoreInsight {\n type: 'strength' | 'weakness' | 'opportunity';\n title: string;\n detail: string;\n impact: 'high' | 'medium' | 'low';\n}\n\n// ===== MAIN API =====\n\n/**\n * Compute the Context Score™ for a project.\n *\n * @param analysis - Project analysis (from analyzeProject or getCachedAnalysis)\n * @param task - Representative task (default: \"general code review\")\n * @param budget - Token budget (default: 50000)\n */\nexport async function computeContextScore(\n analysis: ProjectAnalysis,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<ContextScore> {\n // Run context selection to get real numbers\n const selection = await selectContext({ task, analysis, budget });\n\n const insights: ScoreInsight[] = [];\n\n // 1. EFFICIENCY (30%) — compression ratio + pruning effectiveness\n const efficiency = scoreEfficiency(analysis, selection, insights);\n\n // 2. COVERAGE (25%) — how much of the relevant universe is captured\n const coverage = scoreCoverage(analysis, selection, insights);\n\n // 3. RISK CONTROL (20%) — high-risk file prioritization\n const riskControl = scoreRiskControl(analysis, selection, insights);\n\n // 4. STRUCTURE (15%) — codebase organization for AI\n const structure = scoreStructure(analysis, insights);\n\n // 5. GOVERNANCE (10%) — project setup for safe AI use\n const governance = scoreGovernance(analysis, insights);\n\n // Overall\n const overall = Math.round(\n efficiency.weighted + coverage.weighted + riskControl.weighted +\n structure.weighted + governance.weighted\n );\n\n const grade = scoreToGrade(overall);\n\n // Comparison: CTO vs naive (send everything)\n const naiveTokens = analysis.totalTokens;\n const optimizedTokens = selection.totalTokens;\n const savedTokens = naiveTokens - optimizedTokens;\n const savedPercent = naiveTokens > 0 ? Math.round((savedTokens / naiveTokens) * 100) : 0;\n\n // Monthly savings estimate: assume 40 interactions/day, 20 working days\n // Claude Sonnet: $3/M input tokens\n const interactionsPerMonth = 40 * 20;\n const costPerMToken = 3; // $/M tokens (Sonnet)\n const naiveMonthlyCost = (naiveTokens / 1_000_000) * costPerMToken * interactionsPerMonth;\n const optimizedMonthlyCost = (optimizedTokens / 1_000_000) * costPerMToken * interactionsPerMonth;\n const monthlySavingsUSD = Math.round((naiveMonthlyCost - optimizedMonthlyCost) * 100) / 100;\n\n return {\n overall,\n grade,\n dimensions: {\n efficiency,\n coverage,\n riskControl,\n structure,\n governance,\n },\n insights: insights.sort((a, b) => {\n const order = { high: 0, medium: 1, low: 2 };\n return order[a.impact] - order[b.impact];\n }),\n comparison: {\n naiveTokens,\n optimizedTokens,\n savedTokens,\n savedPercent,\n monthlySavingsUSD,\n },\n meta: {\n projectName: analysis.projectName,\n totalFiles: analysis.totalFiles,\n totalTokens: analysis.totalTokens,\n analyzedAt: analysis.analyzedAt,\n },\n };\n}\n\n// ===== DIMENSION SCORERS =====\n\nfunction scoreEfficiency(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 30;\n\n // Compression ratio: how much we reduced tokens\n const ratio = analysis.totalTokens > 0\n ? 1 - (selection.totalTokens / analysis.totalTokens)\n : 0;\n\n // File selectivity: what % of files were excluded\n const selectivity = analysis.totalFiles > 0\n ? 1 - (selection.files.length / analysis.totalFiles)\n : 0;\n\n // Pruning effectiveness: how many files got pruned (signatures/skeleton)\n const prunedFiles = selection.files.filter(\n (f) => f.pruneLevel === 'signatures' || f.pruneLevel === 'skeleton'\n ).length;\n const pruneRatio = selection.files.length > 0\n ? prunedFiles / selection.files.length\n : 0;\n\n // Weighted combination\n const raw = (ratio * 0.5 + selectivity * 0.3 + pruneRatio * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (ratio > 0.7) {\n insights.push({\n type: 'strength',\n title: 'Excellent compression',\n detail: `${Math.round(ratio * 100)}% token reduction while maintaining context quality`,\n impact: 'high',\n });\n }\n\n if (ratio < 0.3 && analysis.totalTokens > 20_000) {\n insights.push({\n type: 'weakness',\n title: 'Low compression opportunity',\n detail: 'Most files are needed. Consider splitting the project into smaller modules.',\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${Math.round(ratio * 100)}% compression, ${prunedFiles}/${selection.files.length} files pruned`,\n };\n}\n\nfunction scoreCoverage(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 25;\n\n const coverageScore = selection.coverage.score;\n const missingCritical = selection.coverage.missingCritical.length;\n\n // Penalize missing critical files heavily\n let penalty = 0;\n if (missingCritical > 0) {\n penalty = Math.min(30, missingCritical * 10);\n insights.push({\n type: 'weakness',\n title: `${missingCritical} critical file(s) missing from context`,\n detail: `Missing: ${selection.coverage.missingCritical.slice(0, 3).join(', ')}${missingCritical > 3 ? ` +${missingCritical - 3} more` : ''}`,\n impact: 'high',\n });\n }\n\n const score = Math.min(100, Math.max(0, Math.round(coverageScore - penalty)));\n const weighted = (score / 100) * weight;\n\n if (coverageScore >= 90 && missingCritical === 0) {\n insights.push({\n type: 'strength',\n title: 'Excellent context coverage',\n detail: `${coverageScore}% of the relevant universe captured with zero critical gaps`,\n impact: 'high',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${coverageScore}% coverage, ${missingCritical} critical gaps`,\n };\n}\n\nfunction scoreRiskControl(\n analysis: ProjectAnalysis,\n selection: ContextSelection,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 20;\n\n const dist = analysis.riskProfile.distribution;\n const totalFiles = analysis.totalFiles;\n\n // What % of critical+high risk files are included in the selection?\n const criticalFiles = analysis.files.filter((f) => f.riskScore >= 80);\n const highFiles = analysis.files.filter((f) => f.riskScore >= 60 && f.riskScore < 80);\n const selectedPaths = new Set(selection.files.map((f) => f.relativePath));\n\n const criticalIncluded = criticalFiles.filter((f) => selectedPaths.has(f.relativePath)).length;\n const highIncluded = highFiles.filter((f) => selectedPaths.has(f.relativePath)).length;\n\n const criticalCoverage = criticalFiles.length > 0\n ? criticalIncluded / criticalFiles.length\n : 1;\n const highCoverage = highFiles.length > 0\n ? highIncluded / highFiles.length\n : 1;\n\n // Risk distribution health: fewer critical files = healthier\n const criticalRatio = totalFiles > 0 ? dist.critical / totalFiles : 0;\n const healthScore = Math.max(0, 1 - criticalRatio * 5); // penalize heavy critical\n\n const raw = (criticalCoverage * 0.5 + highCoverage * 0.3 + healthScore * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (criticalCoverage === 1 && criticalFiles.length > 0) {\n insights.push({\n type: 'strength',\n title: 'All critical files included',\n detail: `${criticalFiles.length} critical-risk files are captured in context`,\n impact: 'high',\n });\n }\n\n if (criticalRatio > 0.2) {\n insights.push({\n type: 'opportunity',\n title: 'High concentration of critical files',\n detail: `${dist.critical} files (${Math.round(criticalRatio * 100)}%) are critical risk. Consider refactoring complex modules.`,\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${criticalIncluded}/${criticalFiles.length} critical + ${highIncluded}/${highFiles.length} high-risk files included`,\n };\n}\n\nfunction scoreStructure(\n analysis: ProjectAnalysis,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 15;\n\n const graph = analysis.graph;\n const totalFiles = analysis.totalFiles;\n\n // Cluster cohesion: well-organized projects have clear clusters\n const avgCohesion = graph.clusters.length > 0\n ? graph.clusters.reduce((s, c) => s + c.cohesion, 0) / graph.clusters.length\n : 0;\n\n // Orphan penalty: too many orphans = poor structure\n const orphanRatio = totalFiles > 0 ? graph.orphans.length / totalFiles : 0;\n\n // Hub concentration: too few hubs = flat, too many = coupled\n const hubRatio = totalFiles > 0 ? graph.hubs.length / totalFiles : 0;\n const hubHealth = hubRatio > 0.02 && hubRatio < 0.15 ? 1 : Math.max(0, 1 - Math.abs(hubRatio - 0.08) * 10);\n\n // Type coverage: having type files is good for AI\n const typeFiles = analysis.files.filter((f) => f.kind === 'type').length;\n const typeRatio = totalFiles > 0 ? typeFiles / totalFiles : 0;\n const typeScore = Math.min(1, typeRatio * 10); // reward up to 10% types\n\n const raw = (avgCohesion * 0.3 + (1 - orphanRatio) * 0.3 + hubHealth * 0.2 + typeScore * 0.2) * 100;\n const score = Math.min(100, Math.max(0, Math.round(raw)));\n const weighted = (score / 100) * weight;\n\n if (orphanRatio > 0.5) {\n insights.push({\n type: 'weakness',\n title: 'Many orphan files',\n detail: `${graph.orphans.length} files (${Math.round(orphanRatio * 100)}%) have no imports/exports. AI gets less context from the dependency graph.`,\n impact: 'medium',\n });\n }\n\n if (graph.clusters.length > 0 && avgCohesion > 0.7) {\n insights.push({\n type: 'strength',\n title: 'Well-organized module structure',\n detail: `${graph.clusters.length} cohesive clusters (avg cohesion: ${(avgCohesion * 100).toFixed(0)}%). CTO can efficiently select relevant modules.`,\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `${graph.clusters.length} clusters, ${graph.orphans.length} orphans, ${graph.hubs.length} hubs`,\n };\n}\n\nfunction scoreGovernance(\n analysis: ProjectAnalysis,\n insights: ScoreInsight[],\n): DimensionScore {\n const weight = 10;\n\n // Check for config/policy/type organization\n const hasTypes = analysis.files.some((f) => f.kind === 'type');\n const hasConfig = analysis.files.some((f) => f.kind === 'config');\n const hasTests = analysis.files.some((f) => f.kind === 'test');\n\n let score = 50; // baseline\n\n if (hasTypes) { score += 15; }\n if (hasConfig) { score += 10; }\n if (hasTests) { score += 15; }\n\n // Stack detection: known stacks get better AI support\n if (analysis.stack.length > 0) { score += 10; }\n\n score = Math.min(100, score);\n const weighted = (score / 100) * weight;\n\n if (!hasTests) {\n insights.push({\n type: 'opportunity',\n title: 'No test files detected',\n detail: 'Adding tests helps CTO understand code intent and provides better context boundaries.',\n impact: 'low',\n });\n }\n\n if (!hasTypes) {\n insights.push({\n type: 'opportunity',\n title: 'No type definition files',\n detail: 'Type files dramatically improve AI code generation accuracy. Consider adding interfaces/types.',\n impact: 'medium',\n });\n }\n\n return {\n score,\n weight,\n weighted,\n detail: `types:${hasTypes ? '✓' : '✗'} tests:${hasTests ? '✓' : '✗'} config:${hasConfig ? '✓' : '✗'} stack:${analysis.stack.join(',') || 'unknown'}`,\n };\n}\n\n// ===== HELPERS =====\n\nfunction scoreToGrade(score: number): Grade {\n if (score >= 95) return 'A+';\n if (score >= 90) return 'A';\n if (score >= 85) return 'A-';\n if (score >= 80) return 'B+';\n if (score >= 75) return 'B';\n if (score >= 70) return 'B-';\n if (score >= 65) return 'C+';\n if (score >= 60) return 'C';\n if (score >= 55) return 'C-';\n if (score >= 40) return 'D';\n return 'F';\n}\n\n// ===== RENDERING =====\n\n/**\n * Render the Context Score as a beautiful terminal-friendly string.\n */\nexport function renderContextScore(score: ContextScore): string {\n const lines: string[] = [];\n\n const gradeColor = score.grade.startsWith('A') ? '🟢'\n : score.grade.startsWith('B') ? '🔵'\n : score.grade.startsWith('C') ? '🟡'\n : '🔴';\n\n lines.push('');\n lines.push(` ╔══════════════════════════════════════════════════╗`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${gradeColor} Context Score™ ${padCenter(score.overall.toString(), 3)} / 100 Grade: ${padCenter(score.grade, 2)} ║`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${score.meta.projectName.padEnd(20).substring(0, 20)} ║`);\n lines.push(` ║ ${score.meta.totalFiles} files · ${Math.round(score.meta.totalTokens / 1000)}K tokens ║`);\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n\n // Dimensions\n const dims = [\n { label: 'Efficiency', dim: score.dimensions.efficiency },\n { label: 'Coverage', dim: score.dimensions.coverage },\n { label: 'Risk Control', dim: score.dimensions.riskControl },\n { label: 'Structure', dim: score.dimensions.structure },\n { label: 'Governance', dim: score.dimensions.governance },\n ];\n\n lines.push(` ║ ║`);\n\n for (const { label, dim } of dims) {\n const bar = renderBar(dim.score, 20);\n const pct = dim.score.toString().padStart(3);\n lines.push(` ║ ${label.padEnd(14)} ${bar} ${pct}% (×${dim.weight}%) ║`);\n }\n\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n\n // Comparison\n lines.push(` ║ ║`);\n lines.push(` ║ 💰 vs. Sending Everything: ║`);\n lines.push(` ║ Tokens saved: ${formatNumber(score.comparison.savedTokens).padEnd(12)} (${score.comparison.savedPercent}%) ║`);\n lines.push(` ║ Monthly savings: $${score.comparison.monthlySavingsUSD.toFixed(2).padEnd(10)} ║`);\n lines.push(` ║ (${score.comparison.optimizedTokens.toLocaleString()} vs ${score.comparison.naiveTokens.toLocaleString()} tokens) ║`);\n lines.push(` ║ ║`);\n\n // Insights\n if (score.insights.length > 0) {\n lines.push(` ╠══════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n\n const top = score.insights.slice(0, 5);\n for (const insight of top) {\n const icon = insight.type === 'strength' ? '✅'\n : insight.type === 'weakness' ? '⚠️'\n : '💡';\n const title = insight.title.substring(0, 44).padEnd(44);\n lines.push(` ║ ${icon} ${title} ║`);\n }\n\n lines.push(` ║ ║`);\n }\n\n lines.push(` ╚══════════════════════════════════════════════════╝`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction renderBar(pct: number, width: number): string {\n const filled = Math.round((pct / 100) * width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\nfunction padCenter(str: string, width: number): string {\n const pad = Math.max(0, width - str.length);\n const left = Math.floor(pad / 2);\n return ' '.repeat(left) + str + ' '.repeat(pad - left);\n}\n\nfunction formatNumber(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n","import type { ProjectAnalysis } from '../types/engine.js';\nimport { selectContext } from './selector.js';\n\n// ===== Benchmark Engine =====\n//\n// Proves CTO works with real numbers. Compares three strategies:\n// 1. CTO (risk-aware, deterministic selection)\n// 2. Naive (send everything — what most people do)\n// 3. Random (randomly pick files up to budget — baseline)\n//\n// Measures: tokens, coverage, cost, time\n\nexport interface BenchmarkResult {\n project: string;\n totalFiles: number;\n totalTokens: number;\n budget: number;\n task: string;\n\n strategies: {\n cto: StrategyResult;\n naive: StrategyResult;\n random: StrategyResult;\n };\n\n winner: 'cto' | 'naive' | 'random';\n ctoAdvantage: {\n vsNaiveTokensSaved: number;\n vsNaiveTokensSavedPercent: number;\n vsRandomCoverageGain: number;\n vsNaiveCostSavedMonthlyUSD: number;\n };\n}\n\nexport interface StrategyResult {\n filesSelected: number;\n tokensUsed: number;\n coverageScore: number;\n criticalFilesCovered: number;\n criticalFilesTotal: number;\n highRiskCovered: number;\n highRiskTotal: number;\n costPerInteractionUSD: number;\n timeMs: number;\n}\n\n/**\n * Run a full benchmark comparing CTO vs naive vs random selection.\n */\nexport async function runBenchmark(\n analysis: ProjectAnalysis,\n task: string = 'general code review and refactoring',\n budget: number = 50_000,\n): Promise<BenchmarkResult> {\n const criticalFiles = analysis.files.filter((f) => f.riskScore >= 80);\n const highRiskFiles = analysis.files.filter((f) => f.riskScore >= 60 && f.riskScore < 80);\n\n // ===== STRATEGY 1: CTO (risk-aware selection) =====\n const ctoStart = performance.now();\n const ctoSelection = await selectContext({ task, analysis, budget });\n const ctoTime = performance.now() - ctoStart;\n\n const ctoSelectedPaths = new Set(ctoSelection.files.map((f) => f.relativePath));\n const ctoCritical = criticalFiles.filter((f) => ctoSelectedPaths.has(f.relativePath)).length;\n const ctoHigh = highRiskFiles.filter((f) => ctoSelectedPaths.has(f.relativePath)).length;\n\n const cto: StrategyResult = {\n filesSelected: ctoSelection.files.length,\n tokensUsed: ctoSelection.totalTokens,\n coverageScore: ctoSelection.coverage.score,\n criticalFilesCovered: ctoCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: ctoHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: (ctoSelection.totalTokens / 1_000_000) * 3, // Sonnet pricing\n timeMs: Math.round(ctoTime),\n };\n\n // ===== STRATEGY 2: NAIVE (send everything up to budget) =====\n const naiveStart = performance.now();\n const naiveFiles = [...analysis.files].sort((a, b) => a.relativePath.localeCompare(b.relativePath));\n let naiveTokens = 0;\n let naiveCount = 0;\n const naiveSelectedPaths = new Set<string>();\n\n for (const f of naiveFiles) {\n if (naiveTokens + f.tokens <= budget) {\n naiveTokens += f.tokens;\n naiveCount++;\n naiveSelectedPaths.add(f.relativePath);\n }\n }\n // If budget exceeds total, include everything\n if (naiveTokens === 0 && analysis.totalTokens <= budget) {\n naiveTokens = analysis.totalTokens;\n naiveCount = analysis.totalFiles;\n for (const f of analysis.files) naiveSelectedPaths.add(f.relativePath);\n }\n const naiveTime = performance.now() - naiveStart;\n\n const naiveCritical = criticalFiles.filter((f) => naiveSelectedPaths.has(f.relativePath)).length;\n const naiveHigh = highRiskFiles.filter((f) => naiveSelectedPaths.has(f.relativePath)).length;\n\n // Naive coverage: just count how many of the relevant files are included\n const naiveCoverage = analysis.totalFiles > 0\n ? Math.round((naiveCount / analysis.totalFiles) * 100)\n : 0;\n\n const naive: StrategyResult = {\n filesSelected: naiveCount,\n tokensUsed: naiveTokens > 0 ? naiveTokens : analysis.totalTokens,\n coverageScore: naiveTokens > 0 ? naiveCoverage : 100,\n criticalFilesCovered: naiveCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: naiveHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: ((naiveTokens > 0 ? naiveTokens : analysis.totalTokens) / 1_000_000) * 3,\n timeMs: Math.round(naiveTime),\n };\n\n // ===== STRATEGY 3: RANDOM (randomly pick files up to budget) =====\n const randomStart = performance.now();\n const shuffled = [...analysis.files].sort(() => Math.random() - 0.5);\n let randomTokens = 0;\n let randomCount = 0;\n const randomSelectedPaths = new Set<string>();\n\n for (const f of shuffled) {\n if (randomTokens + f.tokens <= budget) {\n randomTokens += f.tokens;\n randomCount++;\n randomSelectedPaths.add(f.relativePath);\n }\n }\n const randomTime = performance.now() - randomStart;\n\n const randomCritical = criticalFiles.filter((f) => randomSelectedPaths.has(f.relativePath)).length;\n const randomHigh = highRiskFiles.filter((f) => randomSelectedPaths.has(f.relativePath)).length;\n const randomCoverage = analysis.totalFiles > 0\n ? Math.round((randomCount / analysis.totalFiles) * 100)\n : 0;\n\n const random: StrategyResult = {\n filesSelected: randomCount,\n tokensUsed: randomTokens,\n coverageScore: randomCoverage,\n criticalFilesCovered: randomCritical,\n criticalFilesTotal: criticalFiles.length,\n highRiskCovered: randomHigh,\n highRiskTotal: highRiskFiles.length,\n costPerInteractionUSD: (randomTokens / 1_000_000) * 3,\n timeMs: Math.round(randomTime),\n };\n\n // ===== DETERMINE WINNER =====\n // Score: coverage * 0.5 + criticalCoverage * 0.3 + tokenEfficiency * 0.2\n const ctoScore = computeStrategyScore(cto, budget);\n const naiveScore = computeStrategyScore(naive, budget);\n const randomScore = computeStrategyScore(random, budget);\n\n const winner = ctoScore >= naiveScore && ctoScore >= randomScore ? 'cto'\n : naiveScore >= randomScore ? 'naive' : 'random';\n\n // Monthly savings: 40 interactions/day × 20 days\n const interactionsPerMonth = 800;\n const vsNaiveCostSaved = (naive.costPerInteractionUSD - cto.costPerInteractionUSD) * interactionsPerMonth;\n\n return {\n project: analysis.projectName,\n totalFiles: analysis.totalFiles,\n totalTokens: analysis.totalTokens,\n budget,\n task,\n strategies: { cto, naive, random },\n winner,\n ctoAdvantage: {\n vsNaiveTokensSaved: naive.tokensUsed - cto.tokensUsed,\n vsNaiveTokensSavedPercent: naive.tokensUsed > 0\n ? Math.round(((naive.tokensUsed - cto.tokensUsed) / naive.tokensUsed) * 100)\n : 0,\n vsRandomCoverageGain: cto.coverageScore - random.coverageScore,\n vsNaiveCostSavedMonthlyUSD: Math.round(vsNaiveCostSaved * 100) / 100,\n },\n };\n}\n\nfunction computeStrategyScore(strategy: StrategyResult, budget: number): number {\n const coverageWeight = strategy.coverageScore / 100;\n const criticalWeight = strategy.criticalFilesTotal > 0\n ? strategy.criticalFilesCovered / strategy.criticalFilesTotal\n : 1;\n const efficiency = budget > 0 ? 1 - (strategy.tokensUsed / budget) : 0;\n\n return coverageWeight * 0.5 + criticalWeight * 0.3 + Math.max(0, efficiency) * 0.2;\n}\n\n/**\n * Render benchmark results as a formatted comparison table.\n */\nexport function renderBenchmark(result: BenchmarkResult): string {\n const lines: string[] = [];\n const { cto, naive, random } = result.strategies;\n\n lines.push('');\n lines.push(` ╔══════════════════════════════════════════════════════════════╗`);\n lines.push(` ║ ⚡ CTO Benchmark — ${result.project.substring(0, 20).padEnd(20)} ║`);\n lines.push(` ╠══════════════════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n lines.push(` ║ ${pad('Metric', 20)} ${pad('CTO', 12)} ${pad('Naive', 12)} ${pad('Random', 12)} ║`);\n lines.push(` ║ ${'─'.repeat(56)} ║`);\n lines.push(` ║ ${pad('Files selected', 20)} ${pad(cto.filesSelected.toString(), 12)} ${pad(naive.filesSelected.toString(), 12)} ${pad(random.filesSelected.toString(), 12)} ║`);\n lines.push(` ║ ${pad('Tokens used', 20)} ${pad(fmt(cto.tokensUsed), 12)} ${pad(fmt(naive.tokensUsed), 12)} ${pad(fmt(random.tokensUsed), 12)} ║`);\n lines.push(` ║ ${pad('Coverage', 20)} ${pad(cto.coverageScore + '%', 12)} ${pad(naive.coverageScore + '%', 12)} ${pad(random.coverageScore + '%', 12)} ║`);\n lines.push(` ║ ${pad('Critical files', 20)} ${pad(`${cto.criticalFilesCovered}/${cto.criticalFilesTotal}`, 12)} ${pad(`${naive.criticalFilesCovered}/${naive.criticalFilesTotal}`, 12)} ${pad(`${random.criticalFilesCovered}/${random.criticalFilesTotal}`, 12)} ║`);\n lines.push(` ║ ${pad('High-risk files', 20)} ${pad(`${cto.highRiskCovered}/${cto.highRiskTotal}`, 12)} ${pad(`${naive.highRiskCovered}/${naive.highRiskTotal}`, 12)} ${pad(`${random.highRiskCovered}/${random.highRiskTotal}`, 12)} ║`);\n lines.push(` ║ ${pad('Cost/interaction', 20)} ${pad('$' + cto.costPerInteractionUSD.toFixed(4), 12)} ${pad('$' + naive.costPerInteractionUSD.toFixed(4), 12)} ${pad('$' + random.costPerInteractionUSD.toFixed(4), 12)} ║`);\n lines.push(` ║ ${pad('Time', 20)} ${pad(cto.timeMs + 'ms', 12)} ${pad(naive.timeMs + 'ms', 12)} ${pad(random.timeMs + 'ms', 12)} ║`);\n lines.push(` ║ ║`);\n lines.push(` ╠══════════════════════════════════════════════════════════════╣`);\n lines.push(` ║ ║`);\n lines.push(` ║ 🏆 Winner: ${result.winner.toUpperCase().padEnd(48)} ║`);\n\n if (result.ctoAdvantage.vsNaiveTokensSavedPercent > 0) {\n lines.push(` ║ 📉 ${result.ctoAdvantage.vsNaiveTokensSavedPercent}% fewer tokens than naive ║`);\n }\n if (result.ctoAdvantage.vsRandomCoverageGain > 0) {\n lines.push(` ║ 📈 +${result.ctoAdvantage.vsRandomCoverageGain}% better coverage than random ║`);\n }\n if (result.ctoAdvantage.vsNaiveCostSavedMonthlyUSD > 0) {\n lines.push(` ║ 💰 $${result.ctoAdvantage.vsNaiveCostSavedMonthlyUSD.toFixed(2)}/mo saved vs naive (800 interactions) ║`);\n }\n\n lines.push(` ║ ║`);\n lines.push(` ╚══════════════════════════════════════════════════════════════╝`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction pad(s: string, w: number): string { return s.padEnd(w).substring(0, w); }\nfunction fmt(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return n.toString();\n}\n"],"mappings":"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","names":["resolve","join","readFile","readFile","stat","join","relative","resolve","basename","join","stat","readFile","relative","basename","resolve","createHash","readdir","stat","join","extname","relative","resolve","readdir","join","extname","stat","relative","createHash","resolve","createHash","readFile","resolve","relative","Project","SyntaxKind","readFile","existsSync","join","TS_EXTENSIONS","readFile","Project","SyntaxKind","join","existsSync","createHash","readFile","join","resolve"]}
|