@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
|
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import { buildDependencyGraph } from '../index';
|
|
3
3
|
|
|
4
4
|
describe('Auto-detection from folder structure', () => {
|
|
5
|
-
it('should auto-detect domain keywords from folder paths', () => {
|
|
5
|
+
it('should auto-detect domain keywords from folder paths', async () => {
|
|
6
6
|
const files = [
|
|
7
7
|
{
|
|
8
8
|
file: 'src/payments/process.ts',
|
|
@@ -14,7 +14,7 @@ describe('Auto-detection from folder structure', () => {
|
|
|
14
14
|
},
|
|
15
15
|
];
|
|
16
16
|
|
|
17
|
-
const graph = buildDependencyGraph(files);
|
|
17
|
+
const graph = await buildDependencyGraph(files);
|
|
18
18
|
const paymentsNode = graph.nodes.get('src/payments/process.ts');
|
|
19
19
|
const ordersNode = graph.nodes.get('src/orders/create.ts');
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ describe('Auto-detection from folder structure', () => {
|
|
|
25
25
|
expect(ordersNode?.exports[0].inferredDomain).toBe('order');
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
it('should detect domains from nested folders', () => {
|
|
28
|
+
it('should detect domains from nested folders', async () => {
|
|
29
29
|
const files = [
|
|
30
30
|
{
|
|
31
31
|
file: 'src/api/invoices/handler.ts',
|
|
@@ -33,14 +33,14 @@ describe('Auto-detection from folder structure', () => {
|
|
|
33
33
|
},
|
|
34
34
|
];
|
|
35
35
|
|
|
36
|
-
const graph = buildDependencyGraph(files);
|
|
36
|
+
const graph = await buildDependencyGraph(files);
|
|
37
37
|
const node = graph.nodes.get('src/api/invoices/handler.ts');
|
|
38
38
|
|
|
39
39
|
// Should detect 'invoice' from path (invoices folder)
|
|
40
40
|
expect(node?.exports[0].inferredDomain).toBe('invoice');
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
it('should skip common infrastructure folders', () => {
|
|
43
|
+
it('should skip common infrastructure folders', async () => {
|
|
44
44
|
const files = [
|
|
45
45
|
{
|
|
46
46
|
file: 'src/utils/helpers/format.ts',
|
|
@@ -48,14 +48,14 @@ describe('Auto-detection from folder structure', () => {
|
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
50
|
|
|
51
|
-
const graph = buildDependencyGraph(files);
|
|
51
|
+
const graph = await buildDependencyGraph(files);
|
|
52
52
|
const node = graph.nodes.get('src/utils/helpers/format.ts');
|
|
53
53
|
|
|
54
54
|
// 'utils' and 'helpers' should be skipped, no domain detected
|
|
55
55
|
expect(node?.exports[0].inferredDomain).toBe('unknown');
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
it('should merge auto-detected with custom keywords', () => {
|
|
58
|
+
it('should merge auto-detected with custom keywords', async () => {
|
|
59
59
|
const files = [
|
|
60
60
|
{
|
|
61
61
|
file: 'src/receipts/scan.ts',
|
|
@@ -63,7 +63,7 @@ describe('Auto-detection from folder structure', () => {
|
|
|
63
63
|
},
|
|
64
64
|
];
|
|
65
65
|
|
|
66
|
-
const graph = buildDependencyGraph(files, {
|
|
66
|
+
const graph = await buildDependencyGraph(files, {
|
|
67
67
|
domainKeywords: ['receipt'], // Custom keyword
|
|
68
68
|
});
|
|
69
69
|
const node = graph.nodes.get('src/receipts/scan.ts');
|
|
@@ -74,7 +74,7 @@ describe('Auto-detection from folder structure', () => {
|
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
describe('Import-path domain inference', () => {
|
|
77
|
-
it('should infer domain from import paths', () => {
|
|
77
|
+
it('should infer domain from import paths', async () => {
|
|
78
78
|
const files = [
|
|
79
79
|
{
|
|
80
80
|
file: 'src/lib/session.ts',
|
|
@@ -89,14 +89,14 @@ describe('Import-path domain inference', () => {
|
|
|
89
89
|
},
|
|
90
90
|
];
|
|
91
91
|
|
|
92
|
-
const graph = buildDependencyGraph(files);
|
|
92
|
+
const graph = await buildDependencyGraph(files);
|
|
93
93
|
const sessionNode = graph.nodes.get('src/lib/session.ts');
|
|
94
94
|
|
|
95
95
|
// session.ts imports from '../payments/...' so should infer 'payment' domain
|
|
96
96
|
expect(sessionNode?.exports[0].inferredDomain).toBe('payment');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
it('should infer domain from absolute import paths', () => {
|
|
99
|
+
it('should infer domain from absolute import paths', async () => {
|
|
100
100
|
const files = [
|
|
101
101
|
{
|
|
102
102
|
file: 'src/components/nav-links.ts',
|
|
@@ -111,14 +111,14 @@ describe('Import-path domain inference', () => {
|
|
|
111
111
|
},
|
|
112
112
|
];
|
|
113
113
|
|
|
114
|
-
const graph = buildDependencyGraph(files);
|
|
114
|
+
const graph = await buildDependencyGraph(files);
|
|
115
115
|
const navNode = graph.nodes.get('src/components/nav-links.ts');
|
|
116
116
|
|
|
117
117
|
// nav-links.ts imports from '@/orders/...' so should infer 'order' domain
|
|
118
118
|
expect(navNode?.exports[0].inferredDomain).toBe('order');
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
it('should use identifier name first before import-path fallback', () => {
|
|
121
|
+
it('should use identifier name first before import-path fallback', async () => {
|
|
122
122
|
const files = [
|
|
123
123
|
{
|
|
124
124
|
file: 'src/lib/handler.ts',
|
|
@@ -129,14 +129,14 @@ describe('Import-path domain inference', () => {
|
|
|
129
129
|
},
|
|
130
130
|
];
|
|
131
131
|
|
|
132
|
-
const graph = buildDependencyGraph(files);
|
|
132
|
+
const graph = await buildDependencyGraph(files);
|
|
133
133
|
const node = graph.nodes.get('src/lib/handler.ts');
|
|
134
134
|
|
|
135
135
|
// processInvoice should match 'invoice' from identifier, not 'payment' from imports
|
|
136
136
|
expect(node?.exports[0].inferredDomain).toBe('invoice');
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
it('should fall back to import-path when identifier is generic', () => {
|
|
139
|
+
it('should fall back to import-path when identifier is generic', async () => {
|
|
140
140
|
const files = [
|
|
141
141
|
{
|
|
142
142
|
file: 'src/lib/dynamodb.ts',
|
|
@@ -147,7 +147,7 @@ describe('Import-path domain inference', () => {
|
|
|
147
147
|
},
|
|
148
148
|
];
|
|
149
149
|
|
|
150
|
-
const graph = buildDependencyGraph(files);
|
|
150
|
+
const graph = await buildDependencyGraph(files);
|
|
151
151
|
const node = graph.nodes.get('src/lib/dynamodb.ts');
|
|
152
152
|
|
|
153
153
|
// 'connect' is generic, should infer 'customer' from import path
|
|
@@ -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
|
+
});
|