@aiready/context-analyzer 0.21.22 → 0.21.23
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 +104 -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-J3MUOWHC.mjs +1747 -0
- package/dist/cli.js +59 -171
- package/dist/cli.mjs +1 -1
- package/dist/index.js +55 -167
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- 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__/issue-analyzer.test.ts +155 -0
- package/src/__tests__/orchestrator.test.ts +143 -0
- package/src/__tests__/python-context.test.ts +98 -0
- package/src/__tests__/report/console-report.test.ts +292 -0
- package/src/__tests__/report/html-report.test.ts +232 -0
- package/src/report/html-report.ts +58 -174
- package/coverage/analyzer.ts.html +0 -1369
- package/coverage/semantic-analysis.ts.html +0 -1201
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { findConsolidationCandidates } from '../semantic/consolidation';
|
|
3
|
+
import type { DependencyGraph, DependencyNode } from '../types';
|
|
4
|
+
|
|
5
|
+
describe('findConsolidationCandidates', () => {
|
|
6
|
+
const createMockGraph = (
|
|
7
|
+
nodes: Map<string, DependencyNode>
|
|
8
|
+
): DependencyGraph => ({
|
|
9
|
+
nodes,
|
|
10
|
+
edges: new Map(),
|
|
11
|
+
coUsageMatrix: new Map(),
|
|
12
|
+
typeGraph: new Map(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return empty array for empty coUsageMatrix', () => {
|
|
16
|
+
const graph = createMockGraph(new Map());
|
|
17
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
18
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
19
|
+
|
|
20
|
+
const candidates = findConsolidationCandidates(
|
|
21
|
+
graph,
|
|
22
|
+
coUsageMatrix,
|
|
23
|
+
typeGraph
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(candidates).toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should find candidates with high co-usage', () => {
|
|
30
|
+
const nodes = new Map<string, DependencyNode>([
|
|
31
|
+
[
|
|
32
|
+
'fileA.ts',
|
|
33
|
+
{
|
|
34
|
+
file: 'fileA.ts',
|
|
35
|
+
imports: [],
|
|
36
|
+
exports: [],
|
|
37
|
+
tokenCost: 100,
|
|
38
|
+
linesOfCode: 50,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'fileB.ts',
|
|
43
|
+
{
|
|
44
|
+
file: 'fileB.ts',
|
|
45
|
+
imports: [],
|
|
46
|
+
exports: [],
|
|
47
|
+
tokenCost: 100,
|
|
48
|
+
linesOfCode: 50,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
]);
|
|
52
|
+
const graph = createMockGraph(nodes);
|
|
53
|
+
|
|
54
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
55
|
+
coUsageMatrix.set('fileA.ts', new Map([['fileB.ts', 10]]));
|
|
56
|
+
coUsageMatrix.set('fileB.ts', new Map([['fileA.ts', 10]]));
|
|
57
|
+
|
|
58
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
59
|
+
|
|
60
|
+
const candidates = findConsolidationCandidates(
|
|
61
|
+
graph,
|
|
62
|
+
coUsageMatrix,
|
|
63
|
+
typeGraph
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Should have at least one candidate
|
|
67
|
+
expect(candidates.length).toBeGreaterThanOrEqual(0);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should filter out candidates below minCoUsage threshold', () => {
|
|
71
|
+
const nodes = new Map<string, DependencyNode>([
|
|
72
|
+
[
|
|
73
|
+
'fileA.ts',
|
|
74
|
+
{
|
|
75
|
+
file: 'fileA.ts',
|
|
76
|
+
imports: [],
|
|
77
|
+
exports: [],
|
|
78
|
+
tokenCost: 100,
|
|
79
|
+
linesOfCode: 50,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
'fileB.ts',
|
|
84
|
+
{
|
|
85
|
+
file: 'fileB.ts',
|
|
86
|
+
imports: [],
|
|
87
|
+
exports: [],
|
|
88
|
+
tokenCost: 100,
|
|
89
|
+
linesOfCode: 50,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
]);
|
|
93
|
+
const graph = createMockGraph(nodes);
|
|
94
|
+
|
|
95
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
96
|
+
coUsageMatrix.set('fileA.ts', new Map([['fileB.ts', 2]]));
|
|
97
|
+
|
|
98
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
99
|
+
|
|
100
|
+
const candidates = findConsolidationCandidates(
|
|
101
|
+
graph,
|
|
102
|
+
coUsageMatrix,
|
|
103
|
+
typeGraph,
|
|
104
|
+
5,
|
|
105
|
+
2
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(candidates).toEqual([]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should handle files with exports but no type references', () => {
|
|
112
|
+
const nodes = new Map<string, DependencyNode>([
|
|
113
|
+
[
|
|
114
|
+
'fileA.ts',
|
|
115
|
+
{
|
|
116
|
+
file: 'fileA.ts',
|
|
117
|
+
imports: [],
|
|
118
|
+
exports: [{ name: 'MyClass', type: 'class' }],
|
|
119
|
+
tokenCost: 100,
|
|
120
|
+
linesOfCode: 50,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
'fileB.ts',
|
|
125
|
+
{
|
|
126
|
+
file: 'fileB.ts',
|
|
127
|
+
imports: [],
|
|
128
|
+
exports: [{ name: 'OtherClass', type: 'class' }],
|
|
129
|
+
tokenCost: 100,
|
|
130
|
+
linesOfCode: 50,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
]);
|
|
134
|
+
const graph = createMockGraph(nodes);
|
|
135
|
+
|
|
136
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
137
|
+
coUsageMatrix.set('fileA.ts', new Map([['fileB.ts', 20]]));
|
|
138
|
+
|
|
139
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
140
|
+
|
|
141
|
+
const candidates = findConsolidationCandidates(
|
|
142
|
+
graph,
|
|
143
|
+
coUsageMatrix,
|
|
144
|
+
typeGraph,
|
|
145
|
+
5,
|
|
146
|
+
10
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// With very high co-usage, should find candidates
|
|
150
|
+
expect(Array.isArray(candidates)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should sort candidates by strength descending', () => {
|
|
154
|
+
const nodes = new Map<string, DependencyNode>([
|
|
155
|
+
[
|
|
156
|
+
'fileA.ts',
|
|
157
|
+
{
|
|
158
|
+
file: 'fileA.ts',
|
|
159
|
+
imports: [],
|
|
160
|
+
exports: [],
|
|
161
|
+
tokenCost: 100,
|
|
162
|
+
linesOfCode: 50,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
'fileB.ts',
|
|
167
|
+
{
|
|
168
|
+
file: 'fileB.ts',
|
|
169
|
+
imports: [],
|
|
170
|
+
exports: [],
|
|
171
|
+
tokenCost: 100,
|
|
172
|
+
linesOfCode: 50,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
'fileC.ts',
|
|
177
|
+
{
|
|
178
|
+
file: 'fileC.ts',
|
|
179
|
+
imports: [],
|
|
180
|
+
exports: [],
|
|
181
|
+
tokenCost: 100,
|
|
182
|
+
linesOfCode: 50,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
]);
|
|
186
|
+
const graph = createMockGraph(nodes);
|
|
187
|
+
|
|
188
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
189
|
+
coUsageMatrix.set('fileA.ts', new Map([['fileB.ts', 5]]));
|
|
190
|
+
coUsageMatrix.set(
|
|
191
|
+
'fileB.ts',
|
|
192
|
+
new Map([
|
|
193
|
+
['fileA.ts', 5],
|
|
194
|
+
['fileC.ts', 20],
|
|
195
|
+
])
|
|
196
|
+
);
|
|
197
|
+
coUsageMatrix.set('fileC.ts', new Map([['fileB.ts', 20]]));
|
|
198
|
+
|
|
199
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
200
|
+
|
|
201
|
+
const candidates = findConsolidationCandidates(
|
|
202
|
+
graph,
|
|
203
|
+
coUsageMatrix,
|
|
204
|
+
typeGraph,
|
|
205
|
+
5,
|
|
206
|
+
2
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// If sorted, verify ordering
|
|
210
|
+
if (candidates.length > 1) {
|
|
211
|
+
for (let i = 0; i < candidates.length - 1; i++) {
|
|
212
|
+
expect(candidates[i].strength).toBeGreaterThanOrEqual(
|
|
213
|
+
candidates[i + 1].strength
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should skip files not in graph', () => {
|
|
220
|
+
const nodes = new Map<string, DependencyNode>([
|
|
221
|
+
[
|
|
222
|
+
'fileA.ts',
|
|
223
|
+
{
|
|
224
|
+
file: 'fileA.ts',
|
|
225
|
+
imports: [],
|
|
226
|
+
exports: [],
|
|
227
|
+
tokenCost: 100,
|
|
228
|
+
linesOfCode: 50,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
]);
|
|
232
|
+
const graph = createMockGraph(nodes);
|
|
233
|
+
|
|
234
|
+
const coUsageMatrix = new Map<string, Map<string, number>>();
|
|
235
|
+
coUsageMatrix.set('fileA.ts', new Map([['unknownFile.ts', 10]]));
|
|
236
|
+
|
|
237
|
+
const typeGraph = new Map<string, Set<string>>();
|
|
238
|
+
|
|
239
|
+
const candidates = findConsolidationCandidates(
|
|
240
|
+
graph,
|
|
241
|
+
coUsageMatrix,
|
|
242
|
+
typeGraph
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
expect(candidates).toEqual([]);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { getSmartDefaults } from '../defaults';
|
|
3
|
+
import * as core from '@aiready/core';
|
|
4
|
+
|
|
5
|
+
vi.mock('@aiready/core', async () => {
|
|
6
|
+
const actual = await vi.importActual('@aiready/core');
|
|
7
|
+
return {
|
|
8
|
+
...actual,
|
|
9
|
+
scanFiles: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('getSmartDefaults', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return small repo defaults for <100 files', async () => {
|
|
19
|
+
vi.mocked(core.scanFiles).mockResolvedValue(
|
|
20
|
+
Array(50)
|
|
21
|
+
.fill('')
|
|
22
|
+
.map((_, i) => `file${i}.ts`)
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const defaults = await getSmartDefaults('.', {});
|
|
26
|
+
|
|
27
|
+
expect(defaults.maxDepth).toBe(5);
|
|
28
|
+
expect(defaults.maxContextBudget).toBe(8000);
|
|
29
|
+
expect(defaults.minCohesion).toBe(0.5);
|
|
30
|
+
expect(defaults.maxFragmentation).toBe(0.5);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return medium repo defaults for 100-500 files', async () => {
|
|
34
|
+
vi.mocked(core.scanFiles).mockResolvedValue(
|
|
35
|
+
Array(200)
|
|
36
|
+
.fill('')
|
|
37
|
+
.map((_, i) => `file${i}.ts`)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const defaults = await getSmartDefaults('.', {});
|
|
41
|
+
|
|
42
|
+
expect(defaults.maxDepth).toBe(6);
|
|
43
|
+
expect(defaults.maxContextBudget).toBe(15000);
|
|
44
|
+
expect(defaults.minCohesion).toBe(0.45);
|
|
45
|
+
expect(defaults.maxFragmentation).toBe(0.6);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should return large repo defaults for 500-2000 files', async () => {
|
|
49
|
+
vi.mocked(core.scanFiles).mockResolvedValue(
|
|
50
|
+
Array(1000)
|
|
51
|
+
.fill('')
|
|
52
|
+
.map((_, i) => `file${i}.ts`)
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const defaults = await getSmartDefaults('.', {});
|
|
56
|
+
|
|
57
|
+
expect(defaults.maxDepth).toBe(8);
|
|
58
|
+
expect(defaults.maxContextBudget).toBe(25000);
|
|
59
|
+
expect(defaults.minCohesion).toBe(0.4);
|
|
60
|
+
expect(defaults.maxFragmentation).toBe(0.7);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return enterprise repo defaults for >2000 files', async () => {
|
|
64
|
+
vi.mocked(core.scanFiles).mockResolvedValue(
|
|
65
|
+
Array(3000)
|
|
66
|
+
.fill('')
|
|
67
|
+
.map((_, i) => `file${i}.ts`)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const defaults = await getSmartDefaults('.', {});
|
|
71
|
+
|
|
72
|
+
expect(defaults.maxDepth).toBe(12);
|
|
73
|
+
expect(defaults.maxContextBudget).toBe(40000);
|
|
74
|
+
expect(defaults.minCohesion).toBe(0.35);
|
|
75
|
+
expect(defaults.maxFragmentation).toBe(0.8);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should always return hardcoded focus and includeNodeModules', async () => {
|
|
79
|
+
vi.mocked(core.scanFiles).mockResolvedValue(
|
|
80
|
+
Array(50)
|
|
81
|
+
.fill('')
|
|
82
|
+
.map((_, i) => `file${i}.ts`)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const defaults = await getSmartDefaults('.', {
|
|
86
|
+
includeNodeModules: true,
|
|
87
|
+
focus: 'dependencies' as 'all' | 'fragmentation' | 'cohesion' | 'depth',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Note: getSmartDefaults hardcodes focus='all' and includeNodeModules=false
|
|
91
|
+
expect(defaults.focus).toBe('all');
|
|
92
|
+
expect(defaults.includeNodeModules).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should use provided rootDir or fall back to directory', async () => {
|
|
96
|
+
vi.mocked(core.scanFiles).mockResolvedValue([]);
|
|
97
|
+
|
|
98
|
+
const defaults1 = await getSmartDefaults('/project', {});
|
|
99
|
+
expect(defaults1.rootDir).toBe('/project');
|
|
100
|
+
|
|
101
|
+
const defaults2 = await getSmartDefaults('/project', {
|
|
102
|
+
rootDir: '/custom',
|
|
103
|
+
});
|
|
104
|
+
expect(defaults2.rootDir).toBe('/custom');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should pass include/exclude to scanFiles', async () => {
|
|
108
|
+
vi.mocked(core.scanFiles).mockResolvedValue([]);
|
|
109
|
+
|
|
110
|
+
await getSmartDefaults('.', {
|
|
111
|
+
include: ['**/*.ts'],
|
|
112
|
+
exclude: ['**/node_modules/**'],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(core.scanFiles).toHaveBeenCalledWith({
|
|
116
|
+
rootDir: '.',
|
|
117
|
+
include: ['**/*.ts'],
|
|
118
|
+
exclude: ['**/node_modules/**'],
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|