@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.
Files changed (163) hide show
  1. package/.aiready/aiready-report-20260314-222254.json +39216 -0
  2. package/.aiready/aiready-report-20260314-223947.json +3413 -0
  3. package/.aiready/aiready-report-20260314-224112.json +3413 -0
  4. package/.aiready/aiready-report-20260314-224302.json +2973 -0
  5. package/.aiready/aiready-report-20260314-224939.json +3092 -0
  6. package/.aiready/aiready-report-20260314-225154.json +3092 -0
  7. package/.turbo/turbo-build.log +26 -24
  8. package/.turbo/turbo-lint.log +5 -6
  9. package/.turbo/turbo-test.log +41 -119
  10. package/dist/__tests__/analyzer.test.js +55 -14
  11. package/dist/__tests__/analyzer.test.js.map +1 -1
  12. package/dist/__tests__/cluster-detector.test.d.ts +2 -0
  13. package/dist/__tests__/cluster-detector.test.d.ts.map +1 -0
  14. package/dist/__tests__/cluster-detector.test.js +121 -0
  15. package/dist/__tests__/cluster-detector.test.js.map +1 -0
  16. package/dist/__tests__/contract.test.d.ts +2 -0
  17. package/dist/__tests__/contract.test.d.ts.map +1 -0
  18. package/dist/__tests__/contract.test.js +59 -0
  19. package/dist/__tests__/contract.test.js.map +1 -0
  20. package/dist/__tests__/enhanced-cohesion.test.js +12 -2
  21. package/dist/__tests__/enhanced-cohesion.test.js.map +1 -1
  22. package/dist/__tests__/file-classification.test.d.ts +2 -0
  23. package/dist/__tests__/file-classification.test.d.ts.map +1 -0
  24. package/dist/__tests__/file-classification.test.js +749 -0
  25. package/dist/__tests__/file-classification.test.js.map +1 -0
  26. package/dist/__tests__/fragmentation-advanced.test.js +2 -8
  27. package/dist/__tests__/fragmentation-advanced.test.js.map +1 -1
  28. package/dist/__tests__/fragmentation-coupling.test.js +2 -2
  29. package/dist/__tests__/fragmentation-coupling.test.js.map +1 -1
  30. package/dist/__tests__/fragmentation-log.test.js +3 -7
  31. package/dist/__tests__/fragmentation-log.test.js.map +1 -1
  32. package/dist/__tests__/provider.test.d.ts +2 -0
  33. package/dist/__tests__/provider.test.d.ts.map +1 -0
  34. package/dist/__tests__/provider.test.js +72 -0
  35. package/dist/__tests__/provider.test.js.map +1 -0
  36. package/dist/__tests__/remediation.test.d.ts +2 -0
  37. package/dist/__tests__/remediation.test.d.ts.map +1 -0
  38. package/dist/__tests__/remediation.test.js +61 -0
  39. package/dist/__tests__/remediation.test.js.map +1 -0
  40. package/dist/__tests__/scoring.test.js +196 -16
  41. package/dist/__tests__/scoring.test.js.map +1 -1
  42. package/dist/__tests__/structural-cohesion.test.js +8 -2
  43. package/dist/__tests__/structural-cohesion.test.js.map +1 -1
  44. package/dist/analyzer.d.ts +31 -94
  45. package/dist/analyzer.d.ts.map +1 -1
  46. package/dist/analyzer.js +260 -678
  47. package/dist/analyzer.js.map +1 -1
  48. package/dist/analyzers/python-context.d.ts.map +1 -1
  49. package/dist/analyzers/python-context.js +10 -8
  50. package/dist/analyzers/python-context.js.map +1 -1
  51. package/dist/ast-utils.d.ts +16 -0
  52. package/dist/ast-utils.d.ts.map +1 -0
  53. package/dist/ast-utils.js +81 -0
  54. package/dist/ast-utils.js.map +1 -0
  55. package/dist/chunk-2HE27YEV.mjs +1739 -0
  56. package/dist/chunk-64U3PNO3.mjs +94 -0
  57. package/dist/chunk-CDIVYADN.mjs +2110 -0
  58. package/dist/chunk-D25B5LZR.mjs +1739 -0
  59. package/dist/chunk-D3SIHB2V.mjs +2118 -0
  60. package/dist/chunk-FNPSK3CG.mjs +1760 -0
  61. package/dist/chunk-GXTGOLZT.mjs +92 -0
  62. package/dist/chunk-KDUUZQBK.mjs +1692 -0
  63. package/dist/chunk-KWIS5FQP.mjs +1739 -0
  64. package/dist/chunk-LERPI33Y.mjs +2060 -0
  65. package/dist/chunk-MZP3G7TF.mjs +2118 -0
  66. package/dist/chunk-NOHK5DLU.mjs +2173 -0
  67. package/dist/chunk-ORLC5Y4J.mjs +1787 -0
  68. package/dist/chunk-OTCQL7DY.mjs +2045 -0
  69. package/dist/chunk-RRB2C34Q.mjs +1738 -0
  70. package/dist/chunk-SFK6XTJE.mjs +2110 -0
  71. package/dist/chunk-U5R2FTCR.mjs +1803 -0
  72. package/dist/chunk-UU4HZ7ZT.mjs +1849 -0
  73. package/dist/chunk-WKOZOHOU.mjs +2060 -0
  74. package/dist/chunk-XIXAWCMS.mjs +1760 -0
  75. package/dist/chunk-XTAXUNQN.mjs +1742 -0
  76. package/dist/classifier.d.ts +114 -0
  77. package/dist/classifier.d.ts.map +1 -0
  78. package/dist/classifier.js +439 -0
  79. package/dist/classifier.js.map +1 -0
  80. package/dist/cli.js +681 -1170
  81. package/dist/cli.js.map +1 -1
  82. package/dist/cli.mjs +63 -533
  83. package/dist/cluster-detector.d.ts +8 -0
  84. package/dist/cluster-detector.d.ts.map +1 -0
  85. package/dist/cluster-detector.js +70 -0
  86. package/dist/cluster-detector.js.map +1 -0
  87. package/dist/defaults.d.ts +7 -0
  88. package/dist/defaults.d.ts.map +1 -0
  89. package/dist/defaults.js +54 -0
  90. package/dist/defaults.js.map +1 -0
  91. package/dist/graph-builder.d.ts +33 -0
  92. package/dist/graph-builder.d.ts.map +1 -0
  93. package/dist/graph-builder.js +225 -0
  94. package/dist/graph-builder.js.map +1 -0
  95. package/dist/index.d.mts +93 -106
  96. package/dist/index.d.ts +93 -106
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +932 -745
  99. package/dist/index.js.map +1 -1
  100. package/dist/index.mjs +262 -28
  101. package/dist/metrics.d.ts +34 -0
  102. package/dist/metrics.d.ts.map +1 -0
  103. package/dist/metrics.js +170 -0
  104. package/dist/metrics.js.map +1 -0
  105. package/dist/provider.d.ts +6 -0
  106. package/dist/provider.d.ts.map +1 -0
  107. package/dist/provider.js +48 -0
  108. package/dist/provider.js.map +1 -0
  109. package/dist/python-context-3GZKN3LR.mjs +162 -0
  110. package/dist/python-context-O2EN3M6Z.mjs +162 -0
  111. package/dist/remediation.d.ts +25 -0
  112. package/dist/remediation.d.ts.map +1 -0
  113. package/dist/remediation.js +98 -0
  114. package/dist/remediation.js.map +1 -0
  115. package/dist/scoring.d.ts +3 -7
  116. package/dist/scoring.d.ts.map +1 -1
  117. package/dist/scoring.js +57 -48
  118. package/dist/scoring.js.map +1 -1
  119. package/dist/semantic-analysis.d.ts +12 -23
  120. package/dist/semantic-analysis.d.ts.map +1 -1
  121. package/dist/semantic-analysis.js +172 -110
  122. package/dist/semantic-analysis.js.map +1 -1
  123. package/dist/summary.d.ts +6 -0
  124. package/dist/summary.d.ts.map +1 -0
  125. package/dist/summary.js +92 -0
  126. package/dist/summary.js.map +1 -0
  127. package/dist/types.d.ts +9 -2
  128. package/dist/types.d.ts.map +1 -1
  129. package/dist/utils/output-formatter.d.ts +14 -0
  130. package/dist/utils/output-formatter.d.ts.map +1 -0
  131. package/dist/utils/output-formatter.js +338 -0
  132. package/dist/utils/output-formatter.js.map +1 -0
  133. package/package.json +2 -2
  134. package/src/__tests__/analyzer.test.ts +1 -1
  135. package/src/__tests__/auto-detection.test.ts +1 -1
  136. package/src/__tests__/contract.test.ts +1 -1
  137. package/src/__tests__/enhanced-cohesion.test.ts +1 -1
  138. package/src/__tests__/file-classification.test.ts +1 -1
  139. package/src/__tests__/fragmentation-advanced.test.ts +1 -1
  140. package/src/__tests__/fragmentation-coupling.test.ts +1 -1
  141. package/src/__tests__/fragmentation-log.test.ts +1 -1
  142. package/src/__tests__/provider.test.ts +1 -1
  143. package/src/__tests__/structural-cohesion.test.ts +1 -1
  144. package/src/analyzer.ts +112 -317
  145. package/src/analyzers/python-context.ts +7 -76
  146. package/src/ast-utils.ts +2 -2
  147. package/src/classifier.ts +13 -328
  148. package/src/cli-action.ts +110 -0
  149. package/src/cli.ts +3 -701
  150. package/src/cluster-detector.ts +28 -1
  151. package/src/defaults.ts +3 -0
  152. package/src/graph-builder.ts +10 -91
  153. package/src/heuristics.ts +216 -0
  154. package/src/index.ts +6 -0
  155. package/src/issue-analyzer.ts +158 -0
  156. package/src/metrics.ts +9 -0
  157. package/src/scoring.ts +3 -5
  158. package/src/semantic-analysis.ts +8 -14
  159. package/src/summary.ts +62 -106
  160. package/src/types.ts +52 -20
  161. package/src/utils/dependency-graph-utils.ts +126 -0
  162. package/src/utils/output-formatter.ts +411 -0
  163. 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
+ }