@aiready/context-analyzer 0.21.5 ā 0.21.7
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/.aiready/aiready-report-20260314-222254.json +39216 -0
- package/.aiready/aiready-report-20260314-223947.json +3413 -0
- package/.aiready/aiready-report-20260314-224112.json +3413 -0
- package/.aiready/aiready-report-20260314-224302.json +2973 -0
- package/.aiready/aiready-report-20260314-224939.json +3092 -0
- package/.aiready/aiready-report-20260314-225154.json +3092 -0
- package/.turbo/turbo-build.log +26 -24
- package/.turbo/turbo-lint.log +5 -6
- package/.turbo/turbo-test.log +41 -119
- package/dist/__tests__/analyzer.test.js +55 -14
- package/dist/__tests__/analyzer.test.js.map +1 -1
- package/dist/__tests__/cluster-detector.test.d.ts +2 -0
- package/dist/__tests__/cluster-detector.test.d.ts.map +1 -0
- package/dist/__tests__/cluster-detector.test.js +121 -0
- package/dist/__tests__/cluster-detector.test.js.map +1 -0
- package/dist/__tests__/contract.test.d.ts +2 -0
- package/dist/__tests__/contract.test.d.ts.map +1 -0
- package/dist/__tests__/contract.test.js +59 -0
- package/dist/__tests__/contract.test.js.map +1 -0
- package/dist/__tests__/enhanced-cohesion.test.js +12 -2
- package/dist/__tests__/enhanced-cohesion.test.js.map +1 -1
- package/dist/__tests__/file-classification.test.d.ts +2 -0
- package/dist/__tests__/file-classification.test.d.ts.map +1 -0
- package/dist/__tests__/file-classification.test.js +749 -0
- package/dist/__tests__/file-classification.test.js.map +1 -0
- package/dist/__tests__/fragmentation-advanced.test.js +2 -8
- package/dist/__tests__/fragmentation-advanced.test.js.map +1 -1
- package/dist/__tests__/fragmentation-coupling.test.js +2 -2
- package/dist/__tests__/fragmentation-coupling.test.js.map +1 -1
- package/dist/__tests__/fragmentation-log.test.js +3 -7
- package/dist/__tests__/fragmentation-log.test.js.map +1 -1
- package/dist/__tests__/provider.test.d.ts +2 -0
- package/dist/__tests__/provider.test.d.ts.map +1 -0
- package/dist/__tests__/provider.test.js +72 -0
- package/dist/__tests__/provider.test.js.map +1 -0
- package/dist/__tests__/remediation.test.d.ts +2 -0
- package/dist/__tests__/remediation.test.d.ts.map +1 -0
- package/dist/__tests__/remediation.test.js +61 -0
- package/dist/__tests__/remediation.test.js.map +1 -0
- package/dist/__tests__/scoring.test.js +196 -16
- package/dist/__tests__/scoring.test.js.map +1 -1
- package/dist/__tests__/structural-cohesion.test.js +8 -2
- package/dist/__tests__/structural-cohesion.test.js.map +1 -1
- package/dist/analyzer.d.ts +31 -94
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +260 -678
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzers/python-context.d.ts.map +1 -1
- package/dist/analyzers/python-context.js +10 -8
- package/dist/analyzers/python-context.js.map +1 -1
- package/dist/ast-utils.d.ts +16 -0
- package/dist/ast-utils.d.ts.map +1 -0
- package/dist/ast-utils.js +81 -0
- package/dist/ast-utils.js.map +1 -0
- package/dist/chunk-2HE27YEV.mjs +1739 -0
- package/dist/chunk-64U3PNO3.mjs +94 -0
- package/dist/chunk-CDIVYADN.mjs +2110 -0
- package/dist/chunk-D25B5LZR.mjs +1739 -0
- package/dist/chunk-D3SIHB2V.mjs +2118 -0
- package/dist/chunk-FNPSK3CG.mjs +1760 -0
- package/dist/chunk-GXTGOLZT.mjs +92 -0
- package/dist/chunk-KDUUZQBK.mjs +1692 -0
- package/dist/chunk-KWIS5FQP.mjs +1739 -0
- package/dist/chunk-LERPI33Y.mjs +2060 -0
- package/dist/chunk-MZP3G7TF.mjs +2118 -0
- package/dist/chunk-NOHK5DLU.mjs +2173 -0
- package/dist/chunk-ORLC5Y4J.mjs +1787 -0
- package/dist/chunk-OTCQL7DY.mjs +2045 -0
- package/dist/chunk-RRB2C34Q.mjs +1738 -0
- package/dist/chunk-SFK6XTJE.mjs +2110 -0
- package/dist/chunk-U5R2FTCR.mjs +1803 -0
- package/dist/chunk-UU4HZ7ZT.mjs +1849 -0
- package/dist/chunk-WKOZOHOU.mjs +2060 -0
- package/dist/chunk-XIXAWCMS.mjs +1760 -0
- package/dist/chunk-XTAXUNQN.mjs +1742 -0
- package/dist/classifier.d.ts +114 -0
- package/dist/classifier.d.ts.map +1 -0
- package/dist/classifier.js +439 -0
- package/dist/classifier.js.map +1 -0
- package/dist/cli.js +681 -1170
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +63 -533
- package/dist/cluster-detector.d.ts +8 -0
- package/dist/cluster-detector.d.ts.map +1 -0
- package/dist/cluster-detector.js +70 -0
- package/dist/cluster-detector.js.map +1 -0
- package/dist/defaults.d.ts +7 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/defaults.js +54 -0
- package/dist/defaults.js.map +1 -0
- package/dist/graph-builder.d.ts +33 -0
- package/dist/graph-builder.d.ts.map +1 -0
- package/dist/graph-builder.js +225 -0
- package/dist/graph-builder.js.map +1 -0
- package/dist/index.d.mts +93 -106
- package/dist/index.d.ts +93 -106
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +932 -745
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +262 -28
- package/dist/metrics.d.ts +34 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +170 -0
- package/dist/metrics.js.map +1 -0
- package/dist/provider.d.ts +6 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +48 -0
- package/dist/provider.js.map +1 -0
- package/dist/python-context-3GZKN3LR.mjs +162 -0
- package/dist/python-context-O2EN3M6Z.mjs +162 -0
- package/dist/remediation.d.ts +25 -0
- package/dist/remediation.d.ts.map +1 -0
- package/dist/remediation.js +98 -0
- package/dist/remediation.js.map +1 -0
- package/dist/scoring.d.ts +3 -7
- package/dist/scoring.d.ts.map +1 -1
- package/dist/scoring.js +57 -48
- package/dist/scoring.js.map +1 -1
- package/dist/semantic-analysis.d.ts +12 -23
- package/dist/semantic-analysis.d.ts.map +1 -1
- package/dist/semantic-analysis.js +172 -110
- package/dist/semantic-analysis.js.map +1 -1
- package/dist/summary.d.ts +6 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +92 -0
- package/dist/summary.js.map +1 -0
- package/dist/types.d.ts +9 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/output-formatter.d.ts +14 -0
- package/dist/utils/output-formatter.d.ts.map +1 -0
- package/dist/utils/output-formatter.js +338 -0
- package/dist/utils/output-formatter.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +1 -1
- package/src/__tests__/auto-detection.test.ts +1 -1
- package/src/__tests__/contract.test.ts +1 -1
- package/src/__tests__/enhanced-cohesion.test.ts +1 -1
- package/src/__tests__/file-classification.test.ts +1 -1
- package/src/__tests__/fragmentation-advanced.test.ts +1 -1
- package/src/__tests__/fragmentation-coupling.test.ts +1 -1
- package/src/__tests__/fragmentation-log.test.ts +1 -1
- package/src/__tests__/provider.test.ts +1 -1
- package/src/__tests__/structural-cohesion.test.ts +1 -1
- package/src/analyzer.ts +112 -317
- package/src/analyzers/python-context.ts +7 -76
- package/src/ast-utils.ts +2 -2
- package/src/classifier.ts +13 -328
- package/src/cli-action.ts +110 -0
- package/src/cli.ts +3 -701
- package/src/cluster-detector.ts +28 -1
- package/src/defaults.ts +3 -0
- package/src/graph-builder.ts +10 -91
- package/src/heuristics.ts +216 -0
- package/src/index.ts +6 -0
- package/src/issue-analyzer.ts +158 -0
- package/src/metrics.ts +9 -0
- package/src/scoring.ts +3 -5
- package/src/semantic-analysis.ts +8 -14
- package/src/summary.ts +62 -106
- package/src/types.ts +52 -20
- package/src/utils/dependency-graph-utils.ts +126 -0
- package/src/utils/output-formatter.ts +411 -0
- package/src/utils/string-utils.ts +21 -0
package/src/classifier.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { DependencyNode, FileClassification } from './types';
|
|
2
|
+
import {
|
|
3
|
+
isBarrelExport,
|
|
4
|
+
isTypeDefinition,
|
|
5
|
+
isNextJsPage,
|
|
6
|
+
isLambdaHandler,
|
|
7
|
+
isServiceFile,
|
|
8
|
+
isEmailTemplate,
|
|
9
|
+
isParserFile,
|
|
10
|
+
isSessionFile,
|
|
11
|
+
isUtilityModule,
|
|
12
|
+
isConfigFile,
|
|
13
|
+
} from './heuristics';
|
|
2
14
|
|
|
3
15
|
/**
|
|
4
16
|
* Constants for file classifications to avoid magic strings
|
|
@@ -99,334 +111,7 @@ export function classifyFile(
|
|
|
99
111
|
return Classification.UNKNOWN;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
|
-
|
|
103
|
-
* Detect if a file is a barrel export (index.ts)
|
|
104
|
-
*
|
|
105
|
-
* @param node The dependency node to check
|
|
106
|
-
* @returns True if the file appears to be a barrel export
|
|
107
|
-
*/
|
|
108
|
-
export function isBarrelExport(node: DependencyNode): boolean {
|
|
109
|
-
const { file, exports } = node;
|
|
110
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
111
|
-
|
|
112
|
-
// Barrel files are typically named index.ts or index.js
|
|
113
|
-
const isIndexFile = fileName === 'index.ts' || fileName === 'index.js';
|
|
114
|
-
|
|
115
|
-
// Small file with many exports is likely a barrel
|
|
116
|
-
const isSmallAndManyExports =
|
|
117
|
-
node.tokenCost < 1000 && (exports || []).length > 5;
|
|
118
|
-
|
|
119
|
-
// RE-EXPORT HEURISTIC for non-index files
|
|
120
|
-
const isReexportPattern =
|
|
121
|
-
(exports || []).length >= 5 &&
|
|
122
|
-
(exports || []).every(
|
|
123
|
-
(e) =>
|
|
124
|
-
e.type === 'const' ||
|
|
125
|
-
e.type === 'function' ||
|
|
126
|
-
e.type === 'type' ||
|
|
127
|
-
e.type === 'interface'
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
return !!isIndexFile || !!isSmallAndManyExports || !!isReexportPattern;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Detect if a file is primarily type definitions
|
|
135
|
-
*
|
|
136
|
-
* @param node The dependency node to check
|
|
137
|
-
* @returns True if the file appears to be primarily types
|
|
138
|
-
*/
|
|
139
|
-
export function isTypeDefinition(node: DependencyNode): boolean {
|
|
140
|
-
const { file } = node;
|
|
141
|
-
|
|
142
|
-
// Check file extension
|
|
143
|
-
if (file.endsWith('.d.ts')) return true;
|
|
144
|
-
|
|
145
|
-
// Check if all exports are types or interfaces
|
|
146
|
-
const nodeExports = node.exports || [];
|
|
147
|
-
const hasExports = nodeExports.length > 0;
|
|
148
|
-
const areAllTypes =
|
|
149
|
-
hasExports &&
|
|
150
|
-
nodeExports.every((e) => e.type === 'type' || e.type === 'interface');
|
|
151
|
-
const allTypes: boolean = !!areAllTypes;
|
|
152
|
-
|
|
153
|
-
// Check if path includes 'types' or 'interfaces'
|
|
154
|
-
const isTypePath =
|
|
155
|
-
file.toLowerCase().includes('/types/') ||
|
|
156
|
-
file.toLowerCase().includes('/interfaces/') ||
|
|
157
|
-
file.toLowerCase().includes('/models/');
|
|
158
|
-
|
|
159
|
-
return allTypes || (isTypePath && hasExports);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Detect if a file is a utility module
|
|
164
|
-
*
|
|
165
|
-
* @param node The dependency node to check
|
|
166
|
-
* @returns True if the file appears to be a utility module
|
|
167
|
-
*/
|
|
168
|
-
export function isUtilityModule(node: DependencyNode): boolean {
|
|
169
|
-
const { file } = node;
|
|
170
|
-
|
|
171
|
-
// Check if path includes 'utils', 'helpers', etc.
|
|
172
|
-
const isUtilPath =
|
|
173
|
-
file.toLowerCase().includes('/utils/') ||
|
|
174
|
-
file.toLowerCase().includes('/helpers/') ||
|
|
175
|
-
file.toLowerCase().includes('/util/') ||
|
|
176
|
-
file.toLowerCase().includes('/helper/');
|
|
177
|
-
|
|
178
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
179
|
-
const isUtilName =
|
|
180
|
-
fileName?.includes('utils.') ||
|
|
181
|
-
fileName?.includes('helpers.') ||
|
|
182
|
-
fileName?.includes('util.') ||
|
|
183
|
-
fileName?.includes('helper.');
|
|
184
|
-
|
|
185
|
-
return !!isUtilPath || !!isUtilName;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Detect if a file is a Lambda/API handler
|
|
190
|
-
*
|
|
191
|
-
* @param node The dependency node to check
|
|
192
|
-
* @returns True if the file appears to be a Lambda handler
|
|
193
|
-
*/
|
|
194
|
-
export function isLambdaHandler(node: DependencyNode): boolean {
|
|
195
|
-
const { file, exports } = node;
|
|
196
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
197
|
-
|
|
198
|
-
const handlerPatterns = [
|
|
199
|
-
'handler',
|
|
200
|
-
'.handler.',
|
|
201
|
-
'-handler.',
|
|
202
|
-
'lambda',
|
|
203
|
-
'.lambda.',
|
|
204
|
-
'-lambda.',
|
|
205
|
-
];
|
|
206
|
-
const isHandlerName = handlerPatterns.some((pattern) =>
|
|
207
|
-
fileName?.includes(pattern)
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const isHandlerPath =
|
|
211
|
-
file.toLowerCase().includes('/handlers/') ||
|
|
212
|
-
file.toLowerCase().includes('/lambdas/') ||
|
|
213
|
-
file.toLowerCase().includes('/lambda/') ||
|
|
214
|
-
file.toLowerCase().includes('/functions/');
|
|
215
|
-
|
|
216
|
-
const hasHandlerExport = (exports || []).some(
|
|
217
|
-
(e) =>
|
|
218
|
-
e.name.toLowerCase() === 'handler' ||
|
|
219
|
-
e.name.toLowerCase() === 'main' ||
|
|
220
|
-
e.name.toLowerCase() === 'lambdahandler' ||
|
|
221
|
-
e.name.toLowerCase().endsWith('handler')
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
return !!isHandlerName || !!isHandlerPath || !!hasHandlerExport;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Detect if a file is a service file
|
|
229
|
-
*
|
|
230
|
-
* @param node The dependency node to check
|
|
231
|
-
* @returns True if the file appears to be a service file
|
|
232
|
-
*/
|
|
233
|
-
export function isServiceFile(node: DependencyNode): boolean {
|
|
234
|
-
const { file, exports } = node;
|
|
235
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
236
|
-
|
|
237
|
-
const servicePatterns = ['service', '.service.', '-service.', '_service.'];
|
|
238
|
-
const isServiceName = servicePatterns.some((pattern) =>
|
|
239
|
-
fileName?.includes(pattern)
|
|
240
|
-
);
|
|
241
|
-
const isServicePath = file.toLowerCase().includes('/services/');
|
|
242
|
-
const hasServiceNamedExport = (exports || []).some(
|
|
243
|
-
(e) =>
|
|
244
|
-
e.name.toLowerCase().includes('service') ||
|
|
245
|
-
e.name.toLowerCase().endsWith('service')
|
|
246
|
-
);
|
|
247
|
-
const hasClassExport = (exports || []).some((e) => e.type === 'class');
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
!!isServiceName ||
|
|
251
|
-
!!isServicePath ||
|
|
252
|
-
(!!hasServiceNamedExport && !!hasClassExport)
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Detect if a file is an email template/layout
|
|
258
|
-
*
|
|
259
|
-
* @param node The dependency node to check
|
|
260
|
-
* @returns True if the file appears to be an email template
|
|
261
|
-
*/
|
|
262
|
-
export function isEmailTemplate(node: DependencyNode): boolean {
|
|
263
|
-
const { file, exports } = node;
|
|
264
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
265
|
-
|
|
266
|
-
const emailTemplatePatterns = [
|
|
267
|
-
'-email-',
|
|
268
|
-
'.email.',
|
|
269
|
-
'_email_',
|
|
270
|
-
'-template',
|
|
271
|
-
'.template.',
|
|
272
|
-
'_template',
|
|
273
|
-
'-mail.',
|
|
274
|
-
'.mail.',
|
|
275
|
-
];
|
|
276
|
-
const isEmailTemplateName = emailTemplatePatterns.some((pattern) =>
|
|
277
|
-
fileName?.includes(pattern)
|
|
278
|
-
);
|
|
279
|
-
const isEmailPath =
|
|
280
|
-
file.toLowerCase().includes('/emails/') ||
|
|
281
|
-
file.toLowerCase().includes('/mail/') ||
|
|
282
|
-
file.toLowerCase().includes('/notifications/');
|
|
283
|
-
|
|
284
|
-
const hasTemplateFunction = (exports || []).some(
|
|
285
|
-
(e) =>
|
|
286
|
-
e.type === 'function' &&
|
|
287
|
-
(e.name.toLowerCase().startsWith('render') ||
|
|
288
|
-
e.name.toLowerCase().startsWith('generate') ||
|
|
289
|
-
(e.name.toLowerCase().includes('template') &&
|
|
290
|
-
e.name.toLowerCase().includes('email')))
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
return !!isEmailPath || !!isEmailTemplateName || !!hasTemplateFunction;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Detect if a file is a parser/transformer
|
|
298
|
-
*
|
|
299
|
-
* @param node The dependency node to check
|
|
300
|
-
* @returns True if the file appears to be a parser
|
|
301
|
-
*/
|
|
302
|
-
export function isParserFile(node: DependencyNode): boolean {
|
|
303
|
-
const { file, exports } = node;
|
|
304
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
305
|
-
|
|
306
|
-
const parserPatterns = [
|
|
307
|
-
'parser',
|
|
308
|
-
'.parser.',
|
|
309
|
-
'-parser.',
|
|
310
|
-
'_parser.',
|
|
311
|
-
'transform',
|
|
312
|
-
'.transform.',
|
|
313
|
-
'converter',
|
|
314
|
-
'mapper',
|
|
315
|
-
'serializer',
|
|
316
|
-
];
|
|
317
|
-
const isParserName = parserPatterns.some((pattern) =>
|
|
318
|
-
fileName?.includes(pattern)
|
|
319
|
-
);
|
|
320
|
-
const isParserPath =
|
|
321
|
-
file.toLowerCase().includes('/parsers/') ||
|
|
322
|
-
file.toLowerCase().includes('/transformers/');
|
|
323
|
-
|
|
324
|
-
const hasParseFunction = (exports || []).some(
|
|
325
|
-
(e) =>
|
|
326
|
-
e.type === 'function' &&
|
|
327
|
-
(e.name.toLowerCase().startsWith('parse') ||
|
|
328
|
-
e.name.toLowerCase().startsWith('transform') ||
|
|
329
|
-
e.name.toLowerCase().startsWith('extract'))
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
return !!isParserName || !!isParserPath || !!hasParseFunction;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Detect if a file is a session/state management file
|
|
337
|
-
*
|
|
338
|
-
* @param node The dependency node to check
|
|
339
|
-
* @returns True if the file appears to be a session/state file
|
|
340
|
-
*/
|
|
341
|
-
export function isSessionFile(node: DependencyNode): boolean {
|
|
342
|
-
const { file, exports } = node;
|
|
343
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
344
|
-
|
|
345
|
-
const sessionPatterns = ['session', 'state', 'context', 'store'];
|
|
346
|
-
const isSessionName = sessionPatterns.some((pattern) =>
|
|
347
|
-
fileName?.includes(pattern)
|
|
348
|
-
);
|
|
349
|
-
const isSessionPath =
|
|
350
|
-
file.toLowerCase().includes('/sessions/') ||
|
|
351
|
-
file.toLowerCase().includes('/state/');
|
|
352
|
-
|
|
353
|
-
const hasSessionExport = (exports || []).some(
|
|
354
|
-
(e) =>
|
|
355
|
-
e.name.toLowerCase().includes('session') ||
|
|
356
|
-
e.name.toLowerCase().includes('state') ||
|
|
357
|
-
e.name.toLowerCase().includes('store')
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
return !!isSessionName || !!isSessionPath || !!hasSessionExport;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Detect if a file is a configuration or schema file
|
|
365
|
-
*
|
|
366
|
-
* @param node The dependency node to check
|
|
367
|
-
* @returns True if the file appears to be a config file
|
|
368
|
-
*/
|
|
369
|
-
export function isConfigFile(node: DependencyNode): boolean {
|
|
370
|
-
const { file, exports } = node;
|
|
371
|
-
const lowerPath = file.toLowerCase();
|
|
372
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
373
|
-
|
|
374
|
-
const configPatterns = [
|
|
375
|
-
'.config.',
|
|
376
|
-
'tsconfig',
|
|
377
|
-
'jest.config',
|
|
378
|
-
'package.json',
|
|
379
|
-
'aiready.json',
|
|
380
|
-
'next.config',
|
|
381
|
-
'sst.config',
|
|
382
|
-
];
|
|
383
|
-
const isConfigName = configPatterns.some((p) => fileName?.includes(p));
|
|
384
|
-
const isConfigPath =
|
|
385
|
-
lowerPath.includes('/config/') ||
|
|
386
|
-
lowerPath.includes('/settings/') ||
|
|
387
|
-
lowerPath.includes('/schemas/');
|
|
388
|
-
|
|
389
|
-
const hasSchemaExports = (exports || []).some(
|
|
390
|
-
(e) =>
|
|
391
|
-
e.name.toLowerCase().includes('schema') ||
|
|
392
|
-
e.name.toLowerCase().includes('config') ||
|
|
393
|
-
e.name.toLowerCase().includes('setting')
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
return !!isConfigName || !!isConfigPath || !!hasSchemaExports;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Detect if a file is a Next.js App Router page
|
|
401
|
-
*
|
|
402
|
-
* @param node The dependency node to check
|
|
403
|
-
* @returns True if the file appears to be a Next.js page
|
|
404
|
-
*/
|
|
405
|
-
export function isNextJsPage(node: DependencyNode): boolean {
|
|
406
|
-
const { file, exports } = node;
|
|
407
|
-
const lowerPath = file.toLowerCase();
|
|
408
|
-
const fileName = file.split('/').pop()?.toLowerCase();
|
|
409
|
-
|
|
410
|
-
const isInAppDir =
|
|
411
|
-
lowerPath.includes('/app/') || lowerPath.startsWith('app/');
|
|
412
|
-
const isPageFile = fileName === 'page.tsx' || fileName === 'page.ts';
|
|
413
|
-
|
|
414
|
-
if (!isInAppDir || !isPageFile) return false;
|
|
415
|
-
|
|
416
|
-
const hasDefaultExport = (exports || []).some((e) => e.type === 'default');
|
|
417
|
-
const nextJsExports = [
|
|
418
|
-
'metadata',
|
|
419
|
-
'generatemetadata',
|
|
420
|
-
'faqjsonld',
|
|
421
|
-
'jsonld',
|
|
422
|
-
'icon',
|
|
423
|
-
];
|
|
424
|
-
const hasNextJsExports = (exports || []).some((e) =>
|
|
425
|
-
nextJsExports.includes(e.name.toLowerCase())
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
return !!hasDefaultExport || !!hasNextJsExports;
|
|
429
|
-
}
|
|
114
|
+
// [Split Point] Logic below this point handled by heuristics.ts
|
|
430
115
|
|
|
431
116
|
/**
|
|
432
117
|
* Adjust cohesion score based on file classification
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadMergedConfig,
|
|
3
|
+
handleJSONOutput,
|
|
4
|
+
handleCLIError,
|
|
5
|
+
getElapsedTime,
|
|
6
|
+
resolveOutputPath,
|
|
7
|
+
} from '@aiready/core';
|
|
8
|
+
import { analyzeContext } from './analyzer';
|
|
9
|
+
import { generateSummary } from './summary';
|
|
10
|
+
import {
|
|
11
|
+
displayConsoleReport,
|
|
12
|
+
generateHTMLReport,
|
|
13
|
+
runInteractiveSetup,
|
|
14
|
+
} from './utils/output-formatter';
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { writeFileSync } from 'fs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Orchestrates the context analysis CLI command.
|
|
20
|
+
* Merges configuration, invokes the analyzer, and formats the output (Console/JSON/HTML).
|
|
21
|
+
*
|
|
22
|
+
* @param directory - Root directory to analyze
|
|
23
|
+
* @param options - CLI options including focus area, max depth, and output format
|
|
24
|
+
*/
|
|
25
|
+
export async function contextActionHandler(directory: string, options: any) {
|
|
26
|
+
console.log(chalk.blue('š Analyzing context window costs...\n'));
|
|
27
|
+
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Define defaults
|
|
32
|
+
const defaults = {
|
|
33
|
+
maxDepth: 5,
|
|
34
|
+
maxContextBudget: 10000,
|
|
35
|
+
minCohesion: 0.6,
|
|
36
|
+
maxFragmentation: 0.5,
|
|
37
|
+
focus: 'all',
|
|
38
|
+
includeNodeModules: false,
|
|
39
|
+
include: undefined,
|
|
40
|
+
exclude: undefined,
|
|
41
|
+
maxResults: 10,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Load and merge config with CLI options
|
|
45
|
+
let finalOptions = (await loadMergedConfig(directory, defaults, {
|
|
46
|
+
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
|
|
47
|
+
maxContextBudget: options.maxContext
|
|
48
|
+
? parseInt(options.maxContext)
|
|
49
|
+
: undefined,
|
|
50
|
+
minCohesion: options.minCohesion
|
|
51
|
+
? parseFloat(options.minCohesion)
|
|
52
|
+
: undefined,
|
|
53
|
+
maxFragmentation: options.maxFragmentation
|
|
54
|
+
? parseFloat(options.maxFragmentation)
|
|
55
|
+
: undefined,
|
|
56
|
+
focus:
|
|
57
|
+
(options.focus as 'fragmentation' | 'cohesion' | 'depth' | 'all') ||
|
|
58
|
+
undefined,
|
|
59
|
+
includeNodeModules: options.includeNodeModules,
|
|
60
|
+
include: options.include?.split(','),
|
|
61
|
+
exclude: options.exclude?.split(','),
|
|
62
|
+
maxResults: options.maxResults ? parseInt(options.maxResults) : undefined,
|
|
63
|
+
})) as any;
|
|
64
|
+
|
|
65
|
+
// Interactive setup if requested
|
|
66
|
+
if (options.interactive) {
|
|
67
|
+
finalOptions = await runInteractiveSetup(directory, finalOptions);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Run analysis
|
|
71
|
+
const results = await analyzeContext(finalOptions);
|
|
72
|
+
const summary = generateSummary(results, finalOptions);
|
|
73
|
+
|
|
74
|
+
const duration = getElapsedTime(startTime);
|
|
75
|
+
|
|
76
|
+
// Handle output
|
|
77
|
+
if (options.output === 'json') {
|
|
78
|
+
handleJSONOutput(
|
|
79
|
+
{
|
|
80
|
+
summary: {
|
|
81
|
+
...summary,
|
|
82
|
+
executionTime: duration,
|
|
83
|
+
config: {
|
|
84
|
+
scan: { tools: ['context'] },
|
|
85
|
+
tools: { context: finalOptions },
|
|
86
|
+
},
|
|
87
|
+
toolConfigs: { context: finalOptions },
|
|
88
|
+
},
|
|
89
|
+
context: { results },
|
|
90
|
+
},
|
|
91
|
+
options.outputFile
|
|
92
|
+
);
|
|
93
|
+
} else if (options.output === 'html') {
|
|
94
|
+
const html = generateHTMLReport(summary, results);
|
|
95
|
+
const outputPath = resolveOutputPath(
|
|
96
|
+
directory,
|
|
97
|
+
options.outputFile,
|
|
98
|
+
'context-report.html'
|
|
99
|
+
);
|
|
100
|
+
writeFileSync(outputPath, html, 'utf-8');
|
|
101
|
+
console.log(chalk.green(`\nā
HTML report saved to: ${outputPath}`));
|
|
102
|
+
} else {
|
|
103
|
+
// Default: Console
|
|
104
|
+
displayConsoleReport(summary, results, finalOptions.maxResults);
|
|
105
|
+
console.log(chalk.dim(`\n⨠Analysis completed in ${duration}ms\n`));
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
handleCLIError(error, 'context-analyzer');
|
|
109
|
+
}
|
|
110
|
+
}
|