Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import type {
ContextAnalysisResult,
ContextSummary,
ModuleCluster,
} from './types';
import { GLOBAL_SCAN_OPTIONS } from '@aiready/core';
import { calculatePathEntropy } from './metrics';
/**
* Generate summary of context analysis results
*
* @param results - Array of individual file analysis results.
* @param options - Optional scan configuration for context extraction.
* @returns A consolidated summary of the entire context scan.
*/
export function generateSummary(
results: ContextAnalysisResult[],
options: any = {}
): ContextSummary {
const config = options
? Object.fromEntries(
Object.entries(options).filter(
([key]) => !GLOBAL_SCAN_OPTIONS.includes(key) || key === 'rootDir'
)
)
: {};
const totalFiles = results.length;
const totalTokens = results.reduce((sum, r) => sum + r.tokenCost, 0);
const avgContextBudget =
totalFiles > 0
? results.reduce((sum, r) => sum + r.contextBudget, 0) / totalFiles
: 0;
// Find deep files
const deepFiles = results
.filter((r) => r.importDepth > 5)
.map((r) => ({ file: r.file, depth: r.importDepth }));
const maxImportDepth = Math.max(0, ...results.map((r) => r.importDepth));
// Find fragmented modules (clusters)
const moduleMap = new Map<string, ContextAnalysisResult[]>();
results.forEach((r) => {
const parts = r.file.split('/');
// Try to identify domain/module (e.g., packages/core, src/utils)
let domain = 'root';
Iif (parts.length > 2) {
domain = parts.slice(0, 2).join('/');
}
Eif (!moduleMap.has(domain)) moduleMap.set(domain, []);
moduleMap.get(domain)!.push(r);
});
const fragmentedModules: ModuleCluster[] = [];
moduleMap.forEach((files, domain) => {
const clusterTokens = files.reduce((sum, f) => sum + f.tokenCost, 0);
const filePaths = files.map((f) => f.file);
const avgEntropy = calculatePathEntropy(filePaths);
// A module is fragmented if it has many files with high directory distance
// and relatively low cohesion
const fragmentationScore = Math.min(1, avgEntropy * (files.length / 10));
Iif (fragmentationScore > 0.4) {
fragmentedModules.push({
domain,
files: filePaths,
fragmentationScore,
totalTokens: clusterTokens,
avgCohesion:
files.reduce((sum, f) => sum + f.cohesionScore, 0) / files.length,
suggestedStructure: {
targetFiles: Math.ceil(files.length / 2),
consolidationPlan: [
`Consolidate ${files.length} files in ${domain} into fewer modules`,
],
},
});
}
});
fragmentedModules.sort((a, b) => b.fragmentationScore - a.fragmentationScore);
const avgFragmentation =
fragmentedModules.length > 0
? fragmentedModules.reduce((sum, m) => sum + m.fragmentationScore, 0) /
fragmentedModules.length
: 0;
// Cohesion
const avgCohesion =
results.reduce((sum, r) => sum + r.cohesionScore, 0) / (totalFiles || 1);
const lowCohesionFiles = results
.filter((r) => r.cohesionScore < 0.4)
.map((r) => ({ file: r.file, score: r.cohesionScore }));
// Issues
const criticalIssues = results.filter(
(r) => r.severity === 'critical'
).length;
const majorIssues = results.filter((r) => r.severity === 'major').length;
const minorIssues = results.filter((r) => r.severity === 'minor').length;
const totalPotentialSavings = results.reduce(
(sum, r) => sum + (r.potentialSavings || 0),
0
);
const topExpensiveFiles = [...results]
.sort((a, b) => b.contextBudget - a.contextBudget)
.slice(0, 10)
.map((r) => ({
file: r.file,
contextBudget: r.contextBudget,
severity: r.severity,
}));
return {
totalFiles,
totalTokens,
avgContextBudget,
maxContextBudget: Math.max(0, ...results.map((r) => r.contextBudget)),
avgImportDepth:
results.reduce((sum, r) => sum + r.importDepth, 0) / (totalFiles || 1),
maxImportDepth,
deepFiles,
avgFragmentation,
fragmentedModules,
avgCohesion,
lowCohesionFiles,
criticalIssues,
majorIssues,
minorIssues,
totalPotentialSavings,
topExpensiveFiles,
config,
};
}
|