@aiready/context-analyzer 0.21.22 → 0.21.24
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/.turbo/turbo-build.log +26 -25
- package/.turbo/turbo-lint.log +5 -5
- package/.turbo/turbo-test.log +91 -41
- package/coverage/clover.xml +2615 -1242
- package/coverage/coverage-final.json +30 -13
- package/coverage/dist/chunk-64U3PNO3.mjs.html +367 -0
- package/coverage/dist/chunk-J3MUOWHC.mjs.html +5326 -0
- package/coverage/dist/index.html +146 -0
- package/coverage/{classifier.ts.html → dist/index.mjs.html} +537 -912
- package/coverage/index.html +84 -189
- package/coverage/src/analyzer.ts.html +88 -0
- package/coverage/src/analyzers/index.html +116 -0
- package/coverage/src/analyzers/python-context.ts.html +910 -0
- package/coverage/{ast-utils.ts.html → src/ast-utils.ts.html} +84 -54
- package/coverage/src/classifier.ts.html +892 -0
- package/coverage/src/classify/classification-patterns.ts.html +307 -0
- package/coverage/src/classify/file-classifiers.ts.html +973 -0
- package/coverage/src/classify/index.html +131 -0
- package/coverage/{cluster-detector.ts.html → src/cluster-detector.ts.html} +154 -91
- package/coverage/{defaults.ts.html → src/defaults.ts.html} +74 -65
- package/coverage/{graph-builder.ts.html → src/graph-builder.ts.html} +268 -229
- package/coverage/src/index.html +341 -0
- package/coverage/{index.ts.html → src/index.ts.html} +70 -13
- package/coverage/{scoring.ts.html → src/issue-analyzer.ts.html} +201 -261
- package/coverage/src/mapper.ts.html +439 -0
- package/coverage/{metrics.ts.html → src/metrics.ts.html} +201 -132
- package/coverage/src/orchestrator.ts.html +493 -0
- package/coverage/{provider.ts.html → src/provider.ts.html} +21 -21
- package/coverage/{remediation.ts.html → src/remediation.ts.html} +112 -52
- package/coverage/src/report/console-report.ts.html +415 -0
- package/coverage/src/report/html-report.ts.html +361 -0
- package/coverage/src/report/index.html +146 -0
- package/coverage/src/report/interactive-setup.ts.html +373 -0
- package/coverage/src/scoring.ts.html +895 -0
- package/coverage/src/semantic/co-usage.ts.html +340 -0
- package/coverage/src/semantic/consolidation.ts.html +223 -0
- package/coverage/src/semantic/domain-inference.ts.html +859 -0
- package/coverage/src/semantic/index.html +161 -0
- package/coverage/src/semantic/type-graph.ts.html +163 -0
- package/coverage/{summary.ts.html → src/summary.ts.html} +155 -275
- package/coverage/{types.ts.html → src/types.ts.html} +133 -31
- package/coverage/src/utils/dependency-graph-utils.ts.html +463 -0
- package/coverage/src/utils/index.html +131 -0
- package/coverage/src/utils/string-utils.ts.html +148 -0
- package/dist/chunk-AMPK6SWS.mjs +1754 -0
- package/dist/chunk-BHCRDEE4.mjs +1745 -0
- package/dist/chunk-IKRP7ECY.mjs +1754 -0
- package/dist/chunk-J3MUOWHC.mjs +1747 -0
- package/dist/chunk-TWWPY7FD.mjs +1754 -0
- package/dist/chunk-Z5WY6A4P.mjs +1754 -0
- package/dist/cli.js +77 -185
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +73 -181
- package/dist/index.mjs +1 -1
- package/dist/python-context-BWDC4E5Z.mjs +162 -0
- package/package.json +3 -3
- package/src/__tests__/analyzer.test.ts +14 -14
- package/src/__tests__/auto-detection.test.ts +16 -16
- package/src/__tests__/consolidation.test.ts +247 -0
- package/src/__tests__/defaults.test.ts +121 -0
- package/src/__tests__/domain-inference.test.ts +420 -0
- package/src/__tests__/fragmentation-coupling.test.ts +4 -4
- package/src/__tests__/issue-analyzer.test.ts +155 -0
- package/src/__tests__/orchestrator.test.ts +143 -0
- package/src/__tests__/python-context.test.ts +100 -0
- package/src/__tests__/report/console-report.test.ts +292 -0
- package/src/__tests__/report/html-report.test.ts +243 -0
- package/src/__tests__/scoring.test.ts +17 -11
- package/src/analyzers/python-context.ts +2 -2
- package/src/ast-utils.ts +3 -3
- package/src/graph-builder.ts +4 -4
- package/src/mapper.ts +6 -0
- package/src/orchestrator.ts +1 -1
- package/src/report/html-report.ts +73 -181
- package/coverage/analyzer.ts.html +0 -1369
- package/coverage/semantic-analysis.ts.html +0 -1201
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { generateHTMLReport } from '../../report/html-report';
|
|
3
|
+
import type { ContextSummary, ContextAnalysisResult } from '../../types';
|
|
4
|
+
|
|
5
|
+
// Mock @aiready/core
|
|
6
|
+
vi.mock('@aiready/core', () => ({
|
|
7
|
+
generateReportHead: (title: string) => `<head><title>${title}</title></head>`,
|
|
8
|
+
generateStatCards: (stats: any[]) =>
|
|
9
|
+
`<div class="stats">${stats.map((s) => s.label).join(', ')}</div>`,
|
|
10
|
+
generateTable: (options: { headers: string[]; rows: string[][] }) =>
|
|
11
|
+
`<table><thead><tr>${options.headers.map((h) => `<th>${h}</th>`).join('')}</thead><tbody>${options.rows.map((r) => `<tr>${r.map((c) => `<td>${c}</td>`).join('')}</tr>`).join('')}</tbody></table>`,
|
|
12
|
+
generateReportFooter: (options: any) =>
|
|
13
|
+
`<footer>${options.title} - ${options.packageUrl}</footer>`,
|
|
14
|
+
generateReportHero: (title: string, subtitle?: string) =>
|
|
15
|
+
`<div class="hero"><h1>${title}</h1><p>${subtitle}</p></div>`,
|
|
16
|
+
generateIssueSummary: (
|
|
17
|
+
critical: number,
|
|
18
|
+
major: number,
|
|
19
|
+
minor: number,
|
|
20
|
+
savings?: number
|
|
21
|
+
) =>
|
|
22
|
+
`<div class="issue-summary">Issues: Critical:${critical}, Major:${major}, Minor:${minor}. Savings: ${savings}</div>`,
|
|
23
|
+
wrapInCard: (content: string, title?: string) =>
|
|
24
|
+
`<div class="card"><h2>${title}</h2>${content}</div>`,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
describe('generateHTMLReport', () => {
|
|
28
|
+
const createMockSummary = (
|
|
29
|
+
overrides: Partial<ContextSummary> = {}
|
|
30
|
+
): ContextSummary => ({
|
|
31
|
+
totalFiles: 10,
|
|
32
|
+
totalTokens: 50000,
|
|
33
|
+
avgContextBudget: 5000,
|
|
34
|
+
maxContextBudget: 10000,
|
|
35
|
+
avgImportDepth: 3,
|
|
36
|
+
maxImportDepth: 7,
|
|
37
|
+
deepFiles: [],
|
|
38
|
+
avgFragmentation: 0.3,
|
|
39
|
+
fragmentedModules: [],
|
|
40
|
+
avgCohesion: 0.7,
|
|
41
|
+
lowCohesionFiles: [],
|
|
42
|
+
criticalIssues: 2,
|
|
43
|
+
majorIssues: 5,
|
|
44
|
+
minorIssues: 3,
|
|
45
|
+
totalPotentialSavings: 10000,
|
|
46
|
+
topExpensiveFiles: [],
|
|
47
|
+
config: {},
|
|
48
|
+
...overrides,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const createMockResults = (): ContextAnalysisResult[] => [
|
|
52
|
+
{
|
|
53
|
+
file: 'src/services/user-service.ts',
|
|
54
|
+
tokenCost: 5000,
|
|
55
|
+
linesOfCode: 500,
|
|
56
|
+
importDepth: 5,
|
|
57
|
+
dependencyCount: 10,
|
|
58
|
+
dependencyList: [],
|
|
59
|
+
circularDeps: [],
|
|
60
|
+
cohesionScore: 0.8,
|
|
61
|
+
domains: ['user'],
|
|
62
|
+
exportCount: 5,
|
|
63
|
+
contextBudget: 15000,
|
|
64
|
+
fragmentationScore: 0.3,
|
|
65
|
+
relatedFiles: [],
|
|
66
|
+
fileClassification: 'service-file',
|
|
67
|
+
severity: 'critical',
|
|
68
|
+
issues: ['High context budget'],
|
|
69
|
+
recommendations: ['Split into smaller modules'],
|
|
70
|
+
potentialSavings: 5000,
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
it('should generate HTML report with all sections', () => {
|
|
75
|
+
const summary = createMockSummary({
|
|
76
|
+
criticalIssues: 2,
|
|
77
|
+
majorIssues: 1,
|
|
78
|
+
minorIssues: 1,
|
|
79
|
+
});
|
|
80
|
+
const results = createMockResults();
|
|
81
|
+
|
|
82
|
+
const html = generateHTMLReport(summary, results);
|
|
83
|
+
|
|
84
|
+
expect(html).toContain('AIReady Context Analysis Report');
|
|
85
|
+
expect(html).toContain('head');
|
|
86
|
+
expect(html).toContain('body');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should include stats cards', () => {
|
|
90
|
+
const summary = createMockSummary();
|
|
91
|
+
const results: ContextAnalysisResult[] = [];
|
|
92
|
+
|
|
93
|
+
const html = generateHTMLReport(summary, results);
|
|
94
|
+
|
|
95
|
+
expect(html).toContain('Files Analyzed');
|
|
96
|
+
expect(html).toContain('Total Tokens');
|
|
97
|
+
expect(html).toContain('Avg Context Budget');
|
|
98
|
+
expect(html).toContain('Total Issues');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should include issues summary when issues exist', () => {
|
|
102
|
+
const summary = createMockSummary({
|
|
103
|
+
criticalIssues: 2,
|
|
104
|
+
majorIssues: 3,
|
|
105
|
+
minorIssues: 1,
|
|
106
|
+
totalPotentialSavings: 5000,
|
|
107
|
+
});
|
|
108
|
+
const results: ContextAnalysisResult[] = [];
|
|
109
|
+
|
|
110
|
+
const html = generateHTMLReport(summary, results);
|
|
111
|
+
|
|
112
|
+
expect(html).toContain('Issues:');
|
|
113
|
+
expect(html).toContain('Critical:');
|
|
114
|
+
expect(html).toContain('Major:');
|
|
115
|
+
expect(html).toContain('Minor:');
|
|
116
|
+
expect(html).toContain('Savings');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should not include issues section when no issues', () => {
|
|
120
|
+
const summary = createMockSummary({
|
|
121
|
+
criticalIssues: 0,
|
|
122
|
+
majorIssues: 0,
|
|
123
|
+
minorIssues: 0,
|
|
124
|
+
});
|
|
125
|
+
const results: ContextAnalysisResult[] = [];
|
|
126
|
+
|
|
127
|
+
const html = generateHTMLReport(summary, results);
|
|
128
|
+
|
|
129
|
+
expect(html).not.toContain('Issues Summary');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should include fragmented modules when present', () => {
|
|
133
|
+
const summary = createMockSummary({
|
|
134
|
+
fragmentedModules: [
|
|
135
|
+
{
|
|
136
|
+
domain: 'src/features',
|
|
137
|
+
files: ['a.ts', 'b.ts', 'c.ts'],
|
|
138
|
+
totalTokens: 3000,
|
|
139
|
+
fragmentationScore: 0.75,
|
|
140
|
+
avgCohesion: 0.4,
|
|
141
|
+
suggestedStructure: {
|
|
142
|
+
targetFiles: 2,
|
|
143
|
+
consolidationPlan: ['Consolidate into fewer modules'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
const results: ContextAnalysisResult[] = [];
|
|
149
|
+
|
|
150
|
+
const html = generateHTMLReport(summary, results);
|
|
151
|
+
|
|
152
|
+
expect(html).toContain('Fragmented Modules');
|
|
153
|
+
expect(html).toContain('src/features');
|
|
154
|
+
expect(html).toContain('Domain');
|
|
155
|
+
expect(html).toContain('Files');
|
|
156
|
+
expect(html).toContain('Fragmentation');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should not include fragmented modules when none exist', () => {
|
|
160
|
+
const summary = createMockSummary({
|
|
161
|
+
fragmentedModules: [],
|
|
162
|
+
});
|
|
163
|
+
const results: ContextAnalysisResult[] = [];
|
|
164
|
+
|
|
165
|
+
const html = generateHTMLReport(summary, results);
|
|
166
|
+
|
|
167
|
+
expect(html).not.toContain('Fragmented Modules');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should include expensive files when present', () => {
|
|
171
|
+
const summary = createMockSummary({
|
|
172
|
+
topExpensiveFiles: [
|
|
173
|
+
{
|
|
174
|
+
file: 'src/services/user-service.ts',
|
|
175
|
+
contextBudget: 15000,
|
|
176
|
+
severity: 'critical',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
file: 'src/services/order-service.ts',
|
|
180
|
+
contextBudget: 12000,
|
|
181
|
+
severity: 'major',
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
const results: ContextAnalysisResult[] = [];
|
|
186
|
+
|
|
187
|
+
const html = generateHTMLReport(summary, results);
|
|
188
|
+
|
|
189
|
+
expect(html).toContain('Most Expensive Files');
|
|
190
|
+
expect(html).toContain('src/services/user-service.ts');
|
|
191
|
+
expect(html).toContain('Context Budget');
|
|
192
|
+
expect(html).toContain('Severity');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should not include expensive files when none exist', () => {
|
|
196
|
+
const summary = createMockSummary({
|
|
197
|
+
topExpensiveFiles: [],
|
|
198
|
+
});
|
|
199
|
+
const results: ContextAnalysisResult[] = [];
|
|
200
|
+
|
|
201
|
+
const html = generateHTMLReport(summary, results);
|
|
202
|
+
|
|
203
|
+
expect(html).not.toContain('Most Expensive Files');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should include footer with package info', () => {
|
|
207
|
+
const summary = createMockSummary();
|
|
208
|
+
const results: ContextAnalysisResult[] = [];
|
|
209
|
+
|
|
210
|
+
const html = generateHTMLReport(summary, results);
|
|
211
|
+
|
|
212
|
+
expect(html).toContain('<footer>');
|
|
213
|
+
expect(html).toContain('context-analyzer');
|
|
214
|
+
expect(html).toContain('github.com');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should include token values in stats', () => {
|
|
218
|
+
const summary = createMockSummary({
|
|
219
|
+
totalTokens: 50000,
|
|
220
|
+
avgContextBudget: 5000,
|
|
221
|
+
});
|
|
222
|
+
const results: ContextAnalysisResult[] = [];
|
|
223
|
+
|
|
224
|
+
const html = generateHTMLReport(summary, results);
|
|
225
|
+
|
|
226
|
+
// Should include stats section
|
|
227
|
+
expect(html).toContain('Total Tokens');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should show issues when present', () => {
|
|
231
|
+
const summary = createMockSummary({
|
|
232
|
+
criticalIssues: 1,
|
|
233
|
+
majorIssues: 0,
|
|
234
|
+
minorIssues: 0,
|
|
235
|
+
});
|
|
236
|
+
const results: ContextAnalysisResult[] = [];
|
|
237
|
+
|
|
238
|
+
const html = generateHTMLReport(summary, results);
|
|
239
|
+
|
|
240
|
+
// Should show issues section
|
|
241
|
+
expect(html).toContain('Issues:');
|
|
242
|
+
});
|
|
243
|
+
});
|
|
@@ -38,7 +38,7 @@ describe('Context Scoring', () => {
|
|
|
38
38
|
const result = calculateContextScore(summary);
|
|
39
39
|
|
|
40
40
|
expect(result.score).toBeLessThan(70);
|
|
41
|
-
expect(result.factors.some((f) => f.name === 'Context Budget')).toBe(
|
|
41
|
+
expect(result.factors.some((f: any) => f.name === 'Context Budget')).toBe(
|
|
42
42
|
true
|
|
43
43
|
);
|
|
44
44
|
expect(result.recommendations.length).toBeGreaterThan(0);
|
|
@@ -60,9 +60,13 @@ describe('Context Scoring', () => {
|
|
|
60
60
|
|
|
61
61
|
// With depth=12: depthScore=80, rawScore=100*0.35+80*0.25+100*0.25=80, no bonus (frag=0.2 not <0.2)
|
|
62
62
|
expect(result.score).toBe(80);
|
|
63
|
-
expect(result.factors.some((f) => f.name === 'Import Depth')).toBe(
|
|
63
|
+
expect(result.factors.some((f: any) => f.name === 'Import Depth')).toBe(
|
|
64
|
+
true
|
|
65
|
+
);
|
|
64
66
|
expect(
|
|
65
|
-
result.recommendations.some((r) =>
|
|
67
|
+
result.recommendations.some((r: any) =>
|
|
68
|
+
r.action.includes('import chains')
|
|
69
|
+
)
|
|
66
70
|
).toBe(true);
|
|
67
71
|
});
|
|
68
72
|
|
|
@@ -86,7 +90,9 @@ describe('Context Scoring', () => {
|
|
|
86
90
|
// Actually frag=0.7 >= 0.2, so no bonus
|
|
87
91
|
// rawScore = 80, no penalties = 80
|
|
88
92
|
expect(result.score).toBeLessThan(85); // Adjusted for new calculation
|
|
89
|
-
expect(result.factors.some((f) => f.name === 'Fragmentation')).toBe(
|
|
93
|
+
expect(result.factors.some((f: any) => f.name === 'Fragmentation')).toBe(
|
|
94
|
+
true
|
|
95
|
+
);
|
|
90
96
|
});
|
|
91
97
|
|
|
92
98
|
it('should apply critical issue penalties', () => {
|
|
@@ -108,9 +114,9 @@ describe('Context Scoring', () => {
|
|
|
108
114
|
// criticalPenalty = min(20, 5*3) = min(20,15) = 15
|
|
109
115
|
// finalScore = 85 - 15 = 70
|
|
110
116
|
expect(result.score).toBe(70);
|
|
111
|
-
expect(
|
|
112
|
-
|
|
113
|
-
);
|
|
117
|
+
expect(
|
|
118
|
+
result.factors.some((f: any) => f.name === 'Critical Issues')
|
|
119
|
+
).toBe(true);
|
|
114
120
|
});
|
|
115
121
|
|
|
116
122
|
it('should handle extreme max budget penalty', () => {
|
|
@@ -128,10 +134,10 @@ describe('Context Scoring', () => {
|
|
|
128
134
|
const result = calculateContextScore(summary);
|
|
129
135
|
|
|
130
136
|
expect(
|
|
131
|
-
result.factors.some((f) => f.name === 'Extreme File Detected')
|
|
137
|
+
result.factors.some((f: any) => f.name === 'Extreme File Detected')
|
|
132
138
|
).toBe(true);
|
|
133
139
|
expect(
|
|
134
|
-
result.recommendations.some((r) =>
|
|
140
|
+
result.recommendations.some((r: any) =>
|
|
135
141
|
r.action.includes('Split large file')
|
|
136
142
|
)
|
|
137
143
|
).toBe(true);
|
|
@@ -214,7 +220,7 @@ describe('Context Scoring', () => {
|
|
|
214
220
|
// finalScore = 90
|
|
215
221
|
expect(result.score).toBe(90);
|
|
216
222
|
expect(
|
|
217
|
-
result.factors.some((f) => f.name === 'Well-Organized Codebase')
|
|
223
|
+
result.factors.some((f: any) => f.name === 'Well-Organized Codebase')
|
|
218
224
|
).toBe(true);
|
|
219
225
|
});
|
|
220
226
|
|
|
@@ -235,7 +241,7 @@ describe('Context Scoring', () => {
|
|
|
235
241
|
// No bonus because fragmentation >= 0.2
|
|
236
242
|
expect(result.score).toBe(85); // 100*0.35 + 100*0.25 + 100*0.25 = 85
|
|
237
243
|
expect(
|
|
238
|
-
result.factors.some((f) => f.name === 'Well-Organized Codebase')
|
|
244
|
+
result.factors.some((f: any) => f.name === 'Well-Organized Codebase')
|
|
239
245
|
).toBe(false);
|
|
240
246
|
});
|
|
241
247
|
|
|
@@ -51,7 +51,7 @@ export async function analyzePythonContext(
|
|
|
51
51
|
rootDir: string
|
|
52
52
|
): Promise<PythonContextMetrics[]> {
|
|
53
53
|
const results: PythonContextMetrics[] = [];
|
|
54
|
-
const parser = getParser('dummy.py');
|
|
54
|
+
const parser = await getParser('dummy.py');
|
|
55
55
|
|
|
56
56
|
if (!parser) {
|
|
57
57
|
console.warn('Python parser not available');
|
|
@@ -135,7 +135,7 @@ async function buildPythonDependencyGraph(
|
|
|
135
135
|
rootDir: string
|
|
136
136
|
): Promise<Map<string, Set<string>>> {
|
|
137
137
|
const graph = new Map<string, Set<string>>();
|
|
138
|
-
const parser = getParser('dummy.py');
|
|
138
|
+
const parser = await getParser('dummy.py');
|
|
139
139
|
|
|
140
140
|
if (!parser) return graph;
|
|
141
141
|
|
package/src/ast-utils.ts
CHANGED
|
@@ -12,14 +12,14 @@ import { inferDomain, extractExports } from './semantic/domain-inference';
|
|
|
12
12
|
* @returns Array of high-fidelity export metadata.
|
|
13
13
|
* @lastUpdated 2026-03-18
|
|
14
14
|
*/
|
|
15
|
-
export function extractExportsWithAST(
|
|
15
|
+
export async function extractExportsWithAST(
|
|
16
16
|
content: string,
|
|
17
17
|
filePath: string,
|
|
18
18
|
domainOptions?: { domainKeywords?: string[] },
|
|
19
19
|
fileImports?: string[]
|
|
20
|
-
): ExportInfo[] {
|
|
20
|
+
): Promise<ExportInfo[]> {
|
|
21
21
|
try {
|
|
22
|
-
const { exports: astExports } = parseFileExports(content, filePath);
|
|
22
|
+
const { exports: astExports } = await parseFileExports(content, filePath);
|
|
23
23
|
|
|
24
24
|
if (astExports.length === 0 && !isTestFile(filePath)) {
|
|
25
25
|
// If AST fails to find anything, we still use regex as a last resort
|
package/src/graph-builder.ts
CHANGED
|
@@ -128,10 +128,10 @@ export function extractDomainKeywordsFromPaths(files: FileContent[]): string[] {
|
|
|
128
128
|
* @param options - Optional configuration for domain detection.
|
|
129
129
|
* @returns Complete dependency graph with nodes, edges, and semantic matrices.
|
|
130
130
|
*/
|
|
131
|
-
export function buildDependencyGraph(
|
|
131
|
+
export async function buildDependencyGraph(
|
|
132
132
|
files: FileContent[],
|
|
133
133
|
options?: { domainKeywords?: string[] }
|
|
134
|
-
): DependencyGraph {
|
|
134
|
+
): Promise<DependencyGraph> {
|
|
135
135
|
const nodes = new Map<string, DependencyNode>();
|
|
136
136
|
const edges = new Map<string, Set<string>>();
|
|
137
137
|
|
|
@@ -142,7 +142,7 @@ export function buildDependencyGraph(
|
|
|
142
142
|
|
|
143
143
|
for (const { file, content } of files) {
|
|
144
144
|
// 1. Get high-fidelity AST-based imports & exports
|
|
145
|
-
const { imports: astImports } = parseFileExports(content, file);
|
|
145
|
+
const { imports: astImports } = await parseFileExports(content, file);
|
|
146
146
|
|
|
147
147
|
// 2. Resolve imports to absolute paths in the graph
|
|
148
148
|
const resolvedImports = astImports
|
|
@@ -152,7 +152,7 @@ export function buildDependencyGraph(
|
|
|
152
152
|
const importSources = astImports.map((i) => i.source);
|
|
153
153
|
|
|
154
154
|
// 3. Wrap with platform-specific metadata (v0.11+)
|
|
155
|
-
const exports = extractExportsWithAST(
|
|
155
|
+
const exports = await extractExportsWithAST(
|
|
156
156
|
content,
|
|
157
157
|
file,
|
|
158
158
|
{ domainKeywords: autoDetectedKeywords },
|
package/src/mapper.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface MappingOptions {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Maps a single dependency node to a comprehensive ContextAnalysisResult.
|
|
30
|
+
*
|
|
31
|
+
* @param node - The dependency node to map
|
|
32
|
+
* @param graph - The full dependency graph
|
|
33
|
+
* @param clusters - All identified module clusters
|
|
34
|
+
* @param allCircularDeps - All identified circular dependencies
|
|
35
|
+
* @param options - Mapping options for detailed analysis
|
|
30
36
|
*/
|
|
31
37
|
export function mapNodeToResult(
|
|
32
38
|
node: DependencyNode,
|
package/src/orchestrator.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { analyzeContext } from '../analyzer';
|
|
2
2
|
import { generateSummary } from '../summary';
|
|
3
|
+
import {
|
|
4
|
+
generateReportHead,
|
|
5
|
+
generateReportHero,
|
|
6
|
+
generateStatCards,
|
|
7
|
+
generateIssueSummary,
|
|
8
|
+
generateTable,
|
|
9
|
+
generateReportFooter,
|
|
10
|
+
wrapInCard,
|
|
11
|
+
} from '@aiready/core';
|
|
3
12
|
|
|
4
13
|
/**
|
|
5
14
|
* Generate HTML report
|
|
@@ -14,195 +23,78 @@ export function generateHTMLReport(
|
|
|
14
23
|
// 'results' may be used in templates later; reference to avoid lint warnings
|
|
15
24
|
void results;
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.header {
|
|
35
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
36
|
-
color: white;
|
|
37
|
-
padding: 30px;
|
|
38
|
-
border-radius: 8px;
|
|
39
|
-
margin-bottom: 30px;
|
|
40
|
-
}
|
|
41
|
-
.summary {
|
|
42
|
-
display: grid;
|
|
43
|
-
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
44
|
-
gap: 20px;
|
|
45
|
-
margin-bottom: 30px;
|
|
46
|
-
}
|
|
47
|
-
.card {
|
|
48
|
-
background: white;
|
|
49
|
-
padding: 20px;
|
|
50
|
-
border-radius: 8px;
|
|
51
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
52
|
-
}
|
|
53
|
-
.metric {
|
|
54
|
-
font-size: 2em;
|
|
55
|
-
font-weight: bold;
|
|
56
|
-
color: #667eea;
|
|
57
|
-
}
|
|
58
|
-
.label {
|
|
59
|
-
color: #666;
|
|
60
|
-
font-size: 0.9em;
|
|
61
|
-
margin-top: 5px;
|
|
62
|
-
}
|
|
63
|
-
.issue-critical { color: #e74c3c; }
|
|
64
|
-
.issue-major { color: #f39c12; }
|
|
65
|
-
.issue-minor { color: #3498db; }
|
|
66
|
-
table {
|
|
67
|
-
width: 100%;
|
|
68
|
-
border-collapse: collapse;
|
|
69
|
-
background: white;
|
|
70
|
-
border-radius: 8px;
|
|
71
|
-
overflow: hidden;
|
|
72
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
73
|
-
}
|
|
74
|
-
th, td {
|
|
75
|
-
padding: 12px;
|
|
76
|
-
text-align: left;
|
|
77
|
-
border-bottom: 1px solid #eee;
|
|
78
|
-
}
|
|
79
|
-
th {
|
|
80
|
-
background-color: #667eea;
|
|
81
|
-
color: white;
|
|
82
|
-
font-weight: 600;
|
|
83
|
-
}
|
|
84
|
-
tr:hover { background-color: #f8f9fa; }
|
|
85
|
-
.footer {
|
|
86
|
-
text-align: center;
|
|
87
|
-
margin-top: 40px;
|
|
88
|
-
padding: 20px;
|
|
89
|
-
color: #666;
|
|
90
|
-
font-size: 0.9em;
|
|
91
|
-
}
|
|
92
|
-
</style>
|
|
93
|
-
</head>
|
|
94
|
-
<body>
|
|
95
|
-
<div class="header">
|
|
96
|
-
<h1>🔍 AIReady Context Analysis Report</h1>
|
|
97
|
-
<p>Generated on ${new Date().toLocaleString()}</p>
|
|
98
|
-
</div>
|
|
26
|
+
const head = generateReportHead('AIReady Context Analysis Report');
|
|
27
|
+
|
|
28
|
+
const stats = generateStatCards([
|
|
29
|
+
{ value: summary.totalFiles, label: 'Files Analyzed' },
|
|
30
|
+
{ value: summary.totalTokens.toLocaleString(), label: 'Total Tokens' },
|
|
31
|
+
{ value: summary.avgContextBudget.toFixed(0), label: 'Avg Context Budget' },
|
|
32
|
+
{
|
|
33
|
+
value: totalIssues,
|
|
34
|
+
label: 'Total Issues',
|
|
35
|
+
color: totalIssues > 0 ? '#f39c12' : undefined,
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const hero = generateReportHero(
|
|
40
|
+
'🔍 AIReady Context Analysis Report',
|
|
41
|
+
`Generated on ${new Date().toLocaleString()}`
|
|
42
|
+
);
|
|
99
43
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
<div class="metric">${summary.totalFiles}</div>
|
|
103
|
-
<div class="label">Files Analyzed</div>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="card">
|
|
106
|
-
<div class="metric">${summary.totalTokens.toLocaleString()}</div>
|
|
107
|
-
<div class="label">Total Tokens</div>
|
|
108
|
-
</div>
|
|
109
|
-
<div class="card">
|
|
110
|
-
<div class="metric">${summary.avgContextBudget.toFixed(0)}</div>
|
|
111
|
-
<div class="label">Avg Context Budget</div>
|
|
112
|
-
</div>
|
|
113
|
-
<div class="card">
|
|
114
|
-
<div class="metric ${totalIssues > 0 ? 'issue-major' : ''}">${totalIssues}</div>
|
|
115
|
-
<div class="label">Total Issues</div>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
44
|
+
let body = `${hero}
|
|
45
|
+
${stats}`;
|
|
118
46
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<span class="issue-major">🟡 Major: ${summary.majorIssues}</span>
|
|
127
|
-
<span class="issue-minor">🔵 Minor: ${summary.minorIssues}</span>
|
|
128
|
-
</p>
|
|
129
|
-
<p><strong>Potential Savings:</strong> ${summary.totalPotentialSavings.toLocaleString()} tokens</p>
|
|
130
|
-
</div>
|
|
131
|
-
`
|
|
132
|
-
: ''
|
|
47
|
+
if (totalIssues > 0) {
|
|
48
|
+
body += generateIssueSummary(
|
|
49
|
+
summary.criticalIssues,
|
|
50
|
+
summary.majorIssues,
|
|
51
|
+
summary.minorIssues,
|
|
52
|
+
summary.totalPotentialSavings
|
|
53
|
+
);
|
|
133
54
|
}
|
|
134
55
|
|
|
135
|
-
|
|
136
|
-
summary.fragmentedModules.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<tbody>
|
|
150
|
-
${summary.fragmentedModules
|
|
151
|
-
.map(
|
|
152
|
-
(m) => `
|
|
153
|
-
<tr>
|
|
154
|
-
<td>${m.domain}</td>
|
|
155
|
-
<td>${m.files.length}</td>
|
|
156
|
-
<td>${(m.fragmentationScore * 100).toFixed(0)}%</td>
|
|
157
|
-
<td>${m.totalTokens.toLocaleString()}</td>
|
|
158
|
-
</tr>
|
|
159
|
-
`
|
|
160
|
-
)
|
|
161
|
-
.join('')}
|
|
162
|
-
</tbody>
|
|
163
|
-
</table>
|
|
164
|
-
</div>
|
|
165
|
-
`
|
|
166
|
-
: ''
|
|
56
|
+
if (summary.fragmentedModules.length > 0) {
|
|
57
|
+
const fragmentedRows = summary.fragmentedModules.map((m) => [
|
|
58
|
+
m.domain,
|
|
59
|
+
String(m.files.length),
|
|
60
|
+
`${(m.fragmentationScore * 100).toFixed(0)}%`,
|
|
61
|
+
m.totalTokens.toLocaleString(),
|
|
62
|
+
]);
|
|
63
|
+
body += wrapInCard(
|
|
64
|
+
generateTable({
|
|
65
|
+
headers: ['Domain', 'Files', 'Fragmentation', 'Token Cost'],
|
|
66
|
+
rows: fragmentedRows,
|
|
67
|
+
}),
|
|
68
|
+
'🧩 Fragmented Modules'
|
|
69
|
+
);
|
|
167
70
|
}
|
|
168
71
|
|
|
169
|
-
|
|
170
|
-
summary.topExpensiveFiles.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
<tbody>
|
|
183
|
-
${summary.topExpensiveFiles
|
|
184
|
-
.map(
|
|
185
|
-
(f) => `
|
|
186
|
-
<tr>
|
|
187
|
-
<td>${f.file}</td>
|
|
188
|
-
<td>${f.contextBudget.toLocaleString()} tokens</td>
|
|
189
|
-
<td class="issue-${f.severity}">${f.severity.toUpperCase()}</td>
|
|
190
|
-
</tr>
|
|
191
|
-
`
|
|
192
|
-
)
|
|
193
|
-
.join('')}
|
|
194
|
-
</tbody>
|
|
195
|
-
</table>
|
|
196
|
-
</div>
|
|
197
|
-
`
|
|
198
|
-
: ''
|
|
72
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
73
|
+
const expensiveRows = summary.topExpensiveFiles.map((f) => [
|
|
74
|
+
f.file,
|
|
75
|
+
`${f.contextBudget.toLocaleString()} tokens`,
|
|
76
|
+
`<span class="issue-${f.severity}">${f.severity.toUpperCase()}</span>`,
|
|
77
|
+
]);
|
|
78
|
+
body += wrapInCard(
|
|
79
|
+
generateTable({
|
|
80
|
+
headers: ['File', 'Context Budget', 'Severity'],
|
|
81
|
+
rows: expensiveRows,
|
|
82
|
+
}),
|
|
83
|
+
'💸 Most Expensive Files'
|
|
84
|
+
);
|
|
199
85
|
}
|
|
200
86
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
87
|
+
const footer = generateReportFooter({
|
|
88
|
+
title: 'Context Analysis Report',
|
|
89
|
+
packageName: 'context-analyzer',
|
|
90
|
+
packageUrl: 'https://github.com/caopengau/aiready-context-analyzer',
|
|
91
|
+
bugUrl: 'https://github.com/caopengau/aiready-context-analyzer/issues',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return `${head}
|
|
95
|
+
<body>
|
|
96
|
+
${body}
|
|
97
|
+
${footer}
|
|
206
98
|
</body>
|
|
207
99
|
</html>`;
|
|
208
100
|
}
|